diff --git a/.github/actions/create-package/action.yml b/.github/actions/create-package/action.yml index aa3420ddbb..989fb4a96b 100644 --- a/.github/actions/create-package/action.yml +++ b/.github/actions/create-package/action.yml @@ -3,6 +3,9 @@ inputs: arch: description: Architecture required: true + qt: + description: Qt Version + required: true outputs: output-basename: description: Output basename @@ -17,4 +20,5 @@ runs: env: RUNNER_OS: ${{runner.os}} INPUT_ARCH: ${{inputs.arch}} + INPUT_QT: ${{inputs.qt}} IS_RELEASE: ${{ github.ref == 'refs/heads/release' }} diff --git a/.github/actions/create-package/create-package.sh b/.github/actions/create-package/create-package.sh index edd252288d..580c835393 100755 --- a/.github/actions/create-package/create-package.sh +++ b/.github/actions/create-package/create-package.sh @@ -39,19 +39,21 @@ platforms/libqwayland-xcomposite-egl.so,platforms/libqwayland-xcomposite-glx.so, wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-integration \ ${update_info} \ -appimage - local output_name="pencil2d-linux-$1-$(date +%F)" + local qtsuffix="-qt${INPUT_QT}" + local output_name="pencil2d${qtsuffix/-qt5/}-linux-$1-$(date +%F)" mv Pencil2D*.AppImage "$output_name.AppImage" mv Pencil2D*.AppImage.zsync "$output_name.AppImage.zsync" \ && sed -i '1,/^$/s/^\(Filename\|URL\): .*$/\1: '"$output_name.AppImage/" "$output_name.AppImage.zsync" \ || true - echo "::set-output name=output-basename::$output_name" + echo "output-basename=$output_name" >> "${GITHUB_OUTPUT}" echo "::endgroup::" } create_package_macos() { echo "::group::Clean" make clean - mv bin Pencil2D + mkdir Pencil2D + mv app/Pencil2D.app Pencil2D/ pushd Pencil2D >/dev/null echo "::endgroup::" @@ -67,12 +69,13 @@ create_package_macos() { rm ffmpeg.7z ffmpeg.7z.sig echo "::endgroup::" - echo "Deploy Qt libraries" + echo "::group::Deploy Qt libraries" macdeployqt Pencil2D.app + echo "::endgroup::" echo "::group::Apply macdeployqt fix" curl -fsSLO https://github.com/aurelien-rainone/macdeployqtfix/archive/master.zip bsdtar xf master.zip - python macdeployqtfix-master/macdeployqtfix.py \ + /Library/Frameworks/Python.framework/Versions/2.7/bin/python macdeployqtfix-master/macdeployqtfix.py \ Pencil2D.app/Contents/MacOS/Pencil2D \ /usr/local/Cellar/qt/5.9.1/ echo "::endgroup::" @@ -80,33 +83,40 @@ create_package_macos() { rm -rf macdeployqtfix-master master.zip popd >/dev/null echo "Create ZIP" - bsdtar caf "pencil2d-mac-$1-$(date +%F).zip" Pencil2D - echo "::set-output name=output-basename::pencil2d-mac-$1-$(date +%F)" + local qtsuffix="-qt${INPUT_QT}" + bsdtar caf "pencil2d${qtsuffix/-qt5/}-mac-$1-$(date +%F).zip" Pencil2D + echo "output-basename=pencil2d${qtsuffix/-qt5/}-mac-$1-$(date +%F)" > "${GITHUB_OUTPUT}" } create_package_windows() { + echo "::group::Set up application files" + nmake install INSTALL_ROOT="$(cygpath -w "${PWD}/Pencil2D")" + echo "::endgroup::" + echo "Copy FFmpeg plugin" local platform="${INPUT_ARCH%%_*}" local ffmpeg="ffmpeg-${platform}.zip" curl -fsSLO "https://github.com/pencil2d/pencil2d-deps/releases/download/ffmpge-v4.1.1/$ffmpeg" "${WINDIR}\\System32\\tar" xf "${ffmpeg}" - mkdir bin/plugins - mv "ffmpeg.exe" bin/plugins/ + mkdir Pencil2D/plugins + mv "ffmpeg.exe" Pencil2D/plugins/ rm -rf "${ffmpeg}" - mv bin Pencil2D echo "Remove files" find \( -name '*.pdb' -o -name '*.ilk' \) -delete echo "::group::Deploy Qt libraries" windeployqt Pencil2D/pencil2d.exe echo "::endgroup::" echo "Copy OpenSSL DLLs" - local xbits="-x${platform#win}" - local _xbits="_x${platform#win}" - cp "${IQTA_TOOLS}\\OpenSSL\\Win${_xbits/32/86}\\bin\\lib"{ssl,crypto}"-1_1${xbits/-x32/}.dll" Pencil2D/ + curl -fsSLO https://download.firedaemon.com/FireDaemon-OpenSSL/openssl-1.1.1w.zip + "${WINDIR}\\System32\\tar" xf openssl-1.1.1w.zip + local xbits="x${platform#win}" + local _xbits="-${xbits}" + cp "openssl-1.1\\${xbits/32/86}\\bin\\lib"{ssl,crypto}"-1_1${_xbits/-x32/}.dll" Pencil2D/ echo "Create ZIP" - "${WINDIR}\\System32\\tar" caf "pencil2d-${platform}-$1-$(date +%F).zip" Pencil2D - echo "::set-output name=output-basename::pencil2d-${platform}-$1-$(date +%F)" + local qtsuffix="-qt${INPUT_QT}" + "${WINDIR}\\System32\\tar" caf "pencil2d${qtsuffix/-qt5/}-${platform}-$1-$(date +%F).zip" Pencil2D + echo "output-basename=pencil2d${qtsuffix/-qt5/}-${platform}-$1-$(date +%F)" > "${GITHUB_OUTPUT}" } "create_package_$(echo $RUNNER_OS | tr '[A-Z]' '[a-z]')" "${GITHUB_RUN_NUMBER}" diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 91df7a19ef..ab8d9f5f89 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -3,6 +3,9 @@ inputs: arch: description: Architecture required: true + qt: + description: Qt Version + required: true runs: using: composite steps: @@ -11,8 +14,10 @@ runs: env: RUNNER_OS: ${{runner.os}} INPUT_ARCH: ${{inputs.arch}} + INPUT_QT: ${{inputs.qt}} - if: runner.os == 'Windows' uses: jurplel/install-qt-action@v3 with: arch: ${{inputs.arch}} - tools: tools_openssl_x${{endsWith(inputs.arch, '_64') && '64' || '86'}} + version: ${{matrix.qt == 6 && '6.4.2' || '5.15.2'}} + modules: ${{matrix.qt == 6 && 'qtmultimedia' || ''}} diff --git a/.github/actions/install-dependencies/install-dependencies.sh b/.github/actions/install-dependencies/install-dependencies.sh index b199caff3a..d078452584 100755 --- a/.github/actions/install-dependencies/install-dependencies.sh +++ b/.github/actions/install-dependencies/install-dependencies.sh @@ -8,9 +8,12 @@ setup_linux() { echo "::group::Add APT sources" for ppa in ppa:ubuntu-toolchain-r/test ppa:ubuntu-sdk-team/ppa \ - ppa:git-core/ppa ppa:beineri/opt-qt-5.15.2-xenial; do + ppa:git-core/ppa; do apt-add-repository -y "${ppa}" done + if [ "${INPUT_QT}" -eq 5 ]; then + apt-add-repository -y ppa:beineri/opt-qt-5.15.2-xenial + fi echo "::endgroup::" echo "::group::Fetch APT updates" @@ -18,12 +21,21 @@ setup_linux() { echo "::endgroup::" echo "::group::Install APT packages" - apt-get install -yq --no-install-suggests --no-install-recommends \ - build-essential qt515tools qt515base qt515multimedia qt515svg \ - qt515xmlpatterns qt515wayland libgl1-mesa-dev bsdtar ffmpeg \ - gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ - gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-alsa \ - gstreamer1.0-pulseaudio git curl libfuse2 + if [ "${INPUT_QT}" -eq 5 ]; then + apt-get install -yq --no-install-suggests --no-install-recommends \ + build-essential qt515tools qt515base qt515multimedia qt515svg \ + qt515wayland libgl1-mesa-dev bsdtar ffmpeg gstreamer1.0-plugins-base \ + gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \ + gstreamer1.0-plugins-ugly gstreamer1.0-alsa gstreamer1.0-pulseaudio git \ + curl libfuse2 + else + apt-get install -yq --no-install-suggests --no-install-recommends \ + build-essential qt6-l10n-tools qt6-base-dev qt6-multimedia-dev \ + libqt6svg6-dev qt6-wayland-dev libgl1-mesa-dev libarchive-tools ffmpeg \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-alsa \ + gstreamer1.0-pulseaudio git curl libfuse2 + fi echo "::endgroup::" } @@ -32,8 +44,8 @@ setup_macos() { brew update echo "::endgroup::" echo "::group::Install Homebrew packages" - brew install libarchive qt@5 - brew link qt@5 --force + brew install libarchive qt@${INPUT_QT} + brew link qt@${INPUT_QT} --force echo "/usr/local/opt/libarchive/bin" >> "${GITHUB_PATH}" echo "::endgroup::" } diff --git a/.github/actions/setup-environment/setup-environment.sh b/.github/actions/setup-environment/setup-environment.sh index fcb4db2d84..37236bb955 100755 --- a/.github/actions/setup-environment/setup-environment.sh +++ b/.github/actions/setup-environment/setup-environment.sh @@ -5,7 +5,9 @@ setup_linux() { # Our container image uses the non-Unicode C locale by default echo "LANG=C.UTF-8" >> "${GITHUB_ENV}" # Set up Qt environment variables and export them to the GitHub Actions workflow - (printenv; (. /opt/qt515/bin/qt515-env.sh; printenv)) | sort -st= -k1,1 | uniq -u >> "${GITHUB_ENV}" + if [ -f /opt/qt515/bin/qt515-env.sh ]; then + (printenv; (. /opt/qt515/bin/qt515-env.sh; printenv)) | sort -st= -k1,1 | uniq -u >> "${GITHUB_ENV}" + fi } setup_macos() { diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fadad09ab5..bd7be4bd7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,23 +24,43 @@ jobs: fail-fast: false matrix: include: - - name: Linux x86_64 + - name: Qt 5 / Linux x86_64 os: ubuntu-latest # XXX: --privileged is sort of a brute-force solution to get FUSE # working inside Docker, however so far I haven’t been able to # figure out precisely *which* privileges are needed. container: { image: "ubuntu:16.04", options: --privileged } - - name: macOS x86_64 + qt: 5 + - name: Qt 5 / macOS x86_64 os: macos-latest container: - - name: Windows x86 + qt: 5 + - name: Qt 5 / Windows x86 os: windows-2019 arch: win32_msvc2019 container: - - name: Windows x86_64 + qt: 5 + - name: Qt 5 / Windows x86_64 os: windows-2019 arch: win64_msvc2019_64 container: + qt: 5 + - name: Qt 6 / Linux x86_64 + os: ubuntu-latest + # XXX: --privileged is sort of a brute-force solution to get FUSE + # working inside Docker, however so far I haven’t been able to + # figure out precisely *which* privileges are needed. + container: { image: "ubuntu:22.04", options: --privileged } + qt: 6 + - name: Qt 6 / macOS x86_64 + os: macos-latest + container: + qt: 6 + - name: Qt 6 / Windows x86_64 + os: windows-2019 + arch: win64_msvc2019_64 + container: + qt: 6 name: ${{matrix.name}} runs-on: ${{matrix.os}} @@ -63,6 +83,7 @@ jobs: uses: ./.github/actions/install-dependencies with: arch: ${{matrix.arch}} + qt: ${{matrix.qt}} - name: Set up environment uses: ./.github/actions/setup-environment @@ -70,8 +91,10 @@ jobs: arch: ${{matrix.arch}} - name: Configure build - run: mkdir build; qmake -o build PREFIX=/usr CONFIG+=release CONFIG+=GIT + run: mkdir build; ${{runner.os == 'Linux' && matrix.qt == 6 && 'qmake6' || 'qmake'}} + -o build PREFIX=/usr CONFIG-=debug_and_release CONFIG+=release CONFIG+=GIT CONFIG+=PENCIL2D_${{github.ref == 'refs/heads/release' && 'RELEASE' || 'NIGHTLY'}} + ${{matrix.qt == 6 && 'CONFIG+=c++17 QMAKE_CXX_FLAGS+=-std=c++17' || ''}} - name: Build Pencil2D working-directory: build @@ -79,15 +102,18 @@ jobs: - name: Run tests env: { QT_QPA_PLATFORM: minimal } - run: build/tests/bin/tests + run: build/tests/tests - name: Create package id: package + if: runner.os != 'Linux' || matrix.qt == 5 uses: ./.github/actions/create-package with: arch: ${{matrix.arch}} + qt: ${{matrix.qt}} - name: Upload package + if: runner.os != 'Linux' || matrix.qt == 5 uses: actions/upload-artifact@v3 with: name: ${{steps.package.outputs.output-basename}} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index de5c077c1c..ae372cbed1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -30,7 +30,7 @@ jobs: - name: Retrieve Qt tagfiles working-directory: util/docs run: | - for i in core gui network svg testlib widgets xml xmlpatterns; do + for i in core gui network svg testlib widgets xml; do curl -fsSLO "https://doc.qt.io/qt-5/qt${i}.tags" done diff --git a/Doxyfile b/Doxyfile index dc73fff5e3..a537ef8061 100644 --- a/Doxyfile +++ b/Doxyfile @@ -2373,7 +2373,6 @@ TAGFILES = util/docs/qtcore.tags=https://doc.qt.io/qt-5/ \ util/docs/qttestlib.tags=https://doc.qt.io/qt-5/ \ util/docs/qtwidgets.tags=https://doc.qt.io/qt-5/ \ util/docs/qtxml.tags=https://doc.qt.io/qt-5/ \ - util/docs/qtxmlpatterns.tags=https://doc.qt.io/qt-5/ \ /usr/share/qt5/doc/qtcore/qtcore.tags=/usr/share/qt5/doc/qtcore/ \ /usr/share/qt5/doc/qtgui/qtgui.tags=/usr/share/qt5/doc/qtgui/ \ /usr/share/qt5/doc/qtnetwork/qtnetwork.tags=/usr/share/qt5/doc/qtnetwork/ \ @@ -2381,7 +2380,6 @@ TAGFILES = util/docs/qtcore.tags=https://doc.qt.io/qt-5/ \ /usr/share/qt5/doc/qttestlib/qttestlib.tags=/usr/share/qt5/doc/qttestlib/ \ /usr/share/qt5/doc/qtwidgets/qtwidgets.tags=/usr/share/qt5/doc/qtwidgets/ \ /usr/share/qt5/doc/qtxml/qtxml.tags=/usr/share/qt5/doc/qtxml/ \ - /usr/share/qt5/doc/qtxmlpatterns/qtxmlpatterns.tags=/usr/share/qt5/doc/qtxmlpatterns/ \ /usr/share/doc/qt/qtcore/qtcore.tags=/usr/share/doc/qt/qtcore/ \ /usr/share/doc/qt/qtgui/qtgui.tags=/usr/share/doc/qt/qtgui/ \ /usr/share/doc/qt/qtnetwork/qtnetwork.tags=/usr/share/doc/qt/qtnetwork/ \ @@ -2389,7 +2387,6 @@ TAGFILES = util/docs/qtcore.tags=https://doc.qt.io/qt-5/ \ /usr/share/doc/qt/qttestlib/qttestlib.tags=/usr/share/doc/qt/qttestlib/ \ /usr/share/doc/qt/qtwidgets/qtwidgets.tags=/usr/share/doc/qt/qtwidgets/ \ /usr/share/doc/qt/qtxml/qtxml.tags=/usr/share/doc/qt/qtxml/ \ - /usr/share/doc/qt/qtxmlpatterns/qtxmlpatterns.tags=/usr/share/doc/qt/qtxmlpatterns/ # Currently Qt Multimedia isn't configured to generate a tagfile, see # https://github.com/qt/qtmultimedia/blob/5.9/src/multimedia/doc/qtmultimedia.qdocconf diff --git a/README.md b/README.md index a8c1e1dc01..0da5a58951 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ # Pencil2D Animation -**Pencil2D** is an animation/drawing software for Windows, macOS, Linux, and FreeBSD. It lets you create traditional hand-drawn animation (cartoon) using both bitmap and vector graphics. Pencil2D is free and open source. +**Pencil2D** is a free and open source animation/drawing software for Windows, macOS, Linux, and FreeBSD. It lets you create traditional hand-drawn animation (cartoon) using both bitmap and vector graphics. -Here is a video demonstrating the project: +Pencil2D is a community-driven project developed entirely by volunteers and we are always looking for more helping hands! There are many different ways to contribute, so anyone can help regardless of their background. For more information, please see the [Contributing](#contributing) section below. -Pencil2D Website: +Don’t forget to check out our official website: ### User Showcase @@ -14,42 +14,48 @@ Pencil2D Website: _User Showcase Reel 2022_ -### Using Pencil2D -Note that the following guides and documentation are outdated. - -* [Quick Reference](https://www.pencil2d.org/doc/quick-reference.html) - Web version of the PDF guide included with Pencil2D -* [User Manual](https://www.pencil2d.org/doc/user-manual.html) - End user documentation how-to -* [FAQ](https://www.pencil2d.org/doc/faq.html) - Frequently Asked Questions - ## Download ### Pencil2D 0.6.6 (17 Feb 2021) -You can download Pencil2D from the [Official Website][p2d-download] or [Github releases][gh-release] +You can download Pencil2D from the [Official Website][p2d-download] or [Github releases][gh-release]. [p2d-download]: https://www.pencil2d.org/download/ [gh-release]: https://github.com/pencil2d/pencil/releases -For first time users refer to the [User Manual][user-man] +### Nightly Builds -[user-man]: https://www.pencil2d.org/doc/user-manual.html +Nightly builds are the bleeding edge versions of Pencil2D, which contains the most recent fixes and features. We currently do not offer legacy mac or 32-bit linux nightly builds. If you have one of these systems, you will have to wait for official releases or build the program from source. -If you run into any issues, make sure to check out the [FAQ][p2d-faq] +[Download Nightly Buildls](https://www.pencil2d.org/download/nightly/) -[p2d-faq]: https://www.pencil2d.org/doc/faq.html +## Using Pencil2D +Note that the following guides and documentation are outdated. -### Nightly Builds +Pencil2D comes with a Quick Reference that you can use to familiarize yourself with the tools and functions at your disposal. After opening Pencil2D, you can access it through the main menu: Help > Quick Reference Guide. -Nightly builds are the bleeding edge versions of Pencil2D, which contains the most recent fixes and features. We currently do not offer legacy mac or 32-bit linux nightly builds. If you have one of these systems, you will have to wait for official releases or build the program from source. +We encourage you to visit our [User Manual][user-man] for a quick guide for novice users, then play around with the program – it's fun! -[Download Nightly Buildls](https://www.pencil2d.org/download/nightly/) +[user-man]: https://www.pencil2d.org/doc/user-manual.html + +After you feel a little more comfortable with the basics, check out these [tutorials][pencil-tutorials] provided by Pencil developers and users. While some of these resources might reference previous versions of Pencil2D, the underlying concepts are still the same. + +[pencil-tutorials]: https://www.pencil2d.org/doc/tutorials.html + +If you encounter difficulty or have additional questions, we have a large community of users who are glad to help out. View the [FAQ][p2d-faq] on our website or visit our [discussion forums][p2d-discussion] to post a new question. + +[p2d-faq]: https://www.pencil2d.org/doc/faq.html +[p2d-discussion]: https://discuss.pencil2d.org/c/support/5 ## Contributing +Interested in contributing to Pencil? There are many ways to help. Take a look at our issues and see what you can help out with, check out the developer guide, or help out with making Pencil2D available to more people by contributing to translation. + * [Issue Tracker](https://github.com/pencil2d/pencil/issues) - Report bugs or request features. * [Developer Guide](https://dev.pencil2d.org/) - Learn how to compile Pencil2D yourself. * [Transifex](https://www.transifex.com/pencil2d/) - You can help translate Pencil2D, too. +* [Documentation contributions](https://www.pencil2d.org/doc/CONTRIBUTING) - Guidelines for contributing to documentation. ## Source Code diff --git a/app/app.pro b/app/app.pro index e798529727..5ebdfa0655 100644 --- a/app/app.pro +++ b/app/app.pro @@ -6,18 +6,11 @@ ! include( ../util/common.pri ) { error( Could not find the common.pri file! ) } +TEMPLATE = app +CONFIG += precompile_header lrelease embed_translations QT += core widgets gui xml multimedia svg network -TEMPLATE = app TARGET = pencil2d -QMAKE_APPLICATION_BUNDLE_NAME = Pencil2D - -CONFIG += qt precompile_header lrelease embed_translations - -DESTDIR = ../bin -MOC_DIR = .moc -OBJECTS_DIR = .obj -UI_DIR = .ui RESOURCES += data/app.qrc @@ -111,7 +104,6 @@ HEADERS += \ src/checkupdatesdialog.h \ src/presetdialog.h \ src/repositionframesdialog.h \ - src/presetdialog.h \ src/commandlineparser.h \ src/commandlineexporter.h \ src/statusbar.h \ @@ -219,10 +211,30 @@ macx { QMAKE_BUNDLE_DATA += FILE_ICONS QMAKE_TARGET_BUNDLE_PREFIX += org.pencil2d + QMAKE_APPLICATION_BUNDLE_NAME = Pencil2D } win32 { + target.path = / + visualelements.path = / + visualelements.files = data/pencil2d.VisualElementsManifest.xml $$OUT_PWD\resources.pri + visualelements.CONFIG += no_check_exist + visualelements.depends += resources.pri + resources.path = /resources + resources.files = data/resources/* + + PRI_CONFIG = data/resources.xml + PRI_INDEX_NAME = Pencil2D RC_FILE = data/pencil2d.rc + INSTALLS += target visualelements resources + + makepri.name = makepri + makepri.input = PRI_CONFIG + makepri.output = ${QMAKE_FILE_IN_BASE}.pri + makepri.commands = makepri new /o /in $$PRI_INDEX_NAME /pr ${QMAKE_FILE_PATH} /cf ${QMAKE_FILE_IN} /of ${QMAKE_FILE_OUT} + silent: makepri.commands = @echo makepri ${QMAKE_FILE_IN} && $$makepri.commands + makepri.CONFIG = no_link + QMAKE_EXTRA_COMPILERS += makepri } unix:!macx { @@ -253,8 +265,9 @@ unix:!macx { INCLUDEPATH += ../../core_lib/src -CONFIG(debug,debug|release) BUILDTYPE = debug -CONFIG(release,debug|release) BUILDTYPE = release +BUILDTYPE = +debug_and_release:CONFIG(debug,debug|release) BUILDTYPE = debug +debug_and_release:CONFIG(release,debug|release) BUILDTYPE = release win32-msvc* { LIBS += -L$$OUT_PWD/../core_lib/$$BUILDTYPE/ -lcore_lib diff --git a/app/data/app.qrc b/app/data/app.qrc index 1c78e052b8..00c7fb59ae 100644 --- a/app/data/app.qrc +++ b/app/data/app.qrc @@ -1,91 +1,98 @@ - icons/remove.png - icons/add.png - icons/magnify.png - icons/layer-vector.png - icons/layer-bitmap.png - icons/layer-sound.png - icons/layer-camera.png - icons/controls/play.png - icons/controls/loop.png - icons/controls/sound.png - icons/prefspencil.png - icons/prefstimeline.png - icons/prefs-files.png + background/checkerboard.png + icons/logo.png + icons/icon.png background/weave.jpg background/dots.png background/grid.jpg - icons/logo.png - icons/icon.png - icons/open.png - icons/copy.png - icons/paste.png - icons/next.png - icons/prev.png - icons/save.png - icons/saveas.png - icons/undo.png - icons/redo.png - icons/new.png - icons/controls/endplay.png - icons/controls/startplay.png - icons/controls/duplicate.png - icons/exit.png - icons/cut.png - icons/prefs-shortcuts.png - icons/grid.png - icons/zoom-in.png - icons/zoom-out.png - icons/zoom-reset.png - background/checkerboard.png - icons/controls/stop.png - icons/controls/sound-disabled.png - icons/new/svg/arrow.svg - icons/new/svg/brush_detailed.svg - icons/new/svg/bucket_detailed.svg - icons/new/svg/eraser_detailed.svg - icons/new/svg/eyedropper_detailed.svg - icons/new/svg/hand_detailed.svg - icons/new/svg/line.svg - icons/new/svg/pen_detailed.svg - icons/new/svg/pencil_detailed.svg - icons/new/svg/selection.svg - icons/new/svg/smudge_detailed.svg - icons/new/svg/trash_detailed.svg - icons/new/checkerboard_smaller.png - icons/overlayCenter.png - icons/overlayGoldenRatio.png - icons/overlaySafe.png - icons/overlayThirds.png - icons/overlayPerspective1.png - icons/overlayPerspective2.png - icons/overlayPerspective3.png - icons/overlayAngle.png - icons/controls/soundscrub.png - icons/controls/soundscrub-disabled.png - icons/new/svg/cursor-rotate.svg - icons/new/svg/cursor-diagonal-left.svg - icons/new/svg/cursor-diagonal-right.svg - icons/new/svg/cursor-horizontal.svg - icons/new/svg/cursor-move.svg - icons/new/svg/cursor-vertical.svg - icons/new/svg/camera-move.svg - icons/new/svg/camera-rotate.svg - icons/new/svg/camera-scale.svg + icons/themes/playful/timeline/frame-add.svg + icons/themes/playful/timeline/frame-duplicate.svg + icons/themes/playful/timeline/frame-remove.svg + icons/themes/playful/timeline/layer-add.svg + icons/themes/playful/timeline/layer-remove.svg + icons/themes/playful/dialog-error.svg + icons/themes/playful/display/perspective-angle.svg + icons/themes/playful/display/perspective-onepoint.svg + icons/themes/playful/display/perspective-threepoints.svg + icons/themes/playful/display/perspective-twopoints.svg + icons/themes/playful/display/lines-invisible.svg + icons/themes/playful/display/lines-outline.svg + icons/themes/playful/display/mirror-horizontal.svg + icons/themes/playful/display/mirror-vertical.svg + icons/themes/playful/display/overlay-center.svg + icons/themes/playful/display/overlay-thirds.svg + icons/themes/playful/menubar/copy.svg + icons/themes/playful/menubar/cut.svg + icons/themes/playful/menubar/new.svg + icons/themes/playful/menubar/open.svg + icons/themes/playful/menubar/paste.svg + icons/themes/playful/menubar/redo.svg + icons/themes/playful/menubar/save.svg + icons/themes/playful/menubar/undo.svg + icons/themes/playful/preferences/preferences-general.svg + icons/themes/playful/preferences/preferences-timeline.svg + icons/themes/playful/preferences/preferences-files.svg + icons/themes/playful/preferences/preferences-shortcuts.svg + icons/themes/playful/preferences/preferences-tools.svg + icons/general/checkerboard_smaller.png + icons/general/cross.png + icons/themes/playful/display/overlay-grid.svg + icons/general/cursor-diagonal-left.svg + icons/general/cursor-diagonal-right.svg + icons/general/cursor-horizontal.svg + icons/general/cursor-move.svg + icons/general/cursor-rotate.svg + icons/general/cursor-vertical.svg + icons/themes/playful/menubar/zoom-in.svg + icons/themes/playful/menubar/zoom-out.svg + icons/themes/playful/menubar/zoom-select.svg + icons/themes/playful/menubar/view-reset.svg + icons/themes/playful/misc/color-dialog.svg + icons/themes/playful/misc/add-color.svg + icons/themes/playful/misc/remove-color.svg + icons/themes/playful/controls/control-play.svg + icons/themes/playful/controls/control-stop.svg + icons/themes/playful/controls/control-loop.svg + icons/themes/playful/controls/control-sound-enable.svg + icons/themes/playful/controls/control-sound-scrub.svg + icons/themes/playful/timeline/cell-bitmap.svg + icons/themes/playful/timeline/cell-camera.svg + icons/themes/playful/timeline/cell-sound.svg + icons/themes/playful/timeline/cell-vector.svg + icons/themes/playful/menubar/clear-canvas.svg + icons/general/cursor-bucket.svg + icons/general/cursor-eyedropper.svg + icons/general/cursor-smudge.svg + icons/general/cursor-pen.svg + icons/general/cursor-brush.svg + icons/general/cursor-pencil.svg + icons/general/cursor-smudge-liquify.svg + icons/themes/playful/display/overlay-golden-ratio.svg + icons/themes/playful/display/overlay-safe.svg + icons/themes/playful/controls/control-play-start.svg + icons/themes/playful/controls/control-play-end.svg + icons/themes/playful/misc/more-options.svg + icons/themes/playful/onion/onionskin-blue.svg + icons/themes/playful/onion/onionskin-red.svg + icons/themes/playful/onion/onionskin-enable.svg + icons/themes/playful/tools/tool-smudge.svg + icons/themes/playful/tools/tool-pencil.svg + icons/themes/playful/tools/tool-bucket.svg + icons/themes/playful/tools/tool-hand.svg + icons/themes/playful/tools/tool-move.svg + icons/themes/playful/tools/tool-brush.svg + icons/themes/playful/tools/tool-eraser.svg + icons/themes/playful/tools/tool-eyedropper.svg + icons/themes/playful/tools/tool-polyline.svg + icons/themes/playful/tools/tool-pen.svg + icons/themes/playful/tools/tool-select.svg + icons/themes/playful/tools/tool-camera-rotate.svg + icons/themes/playful/tools/tool-camera-scale.svg + icons/themes/playful/tools/tool-camera-move.svg + icons/themes/playful/timeline/layer-duplicate.svg - icons/onion-blue.png - icons/onion-red.png - icons/onionNext.png - icons/onionPrev.png - icons/thinlines5.png - icons/outlines5.png - icons/mirror.png - icons/mirrorV.png - icons/dialog-error.svg pencil2d_quick_guide.pdf - icons/new/svg/color-dialog.svg - icons/new/svg/more_options.svg diff --git a/app/data/icons/add.png b/app/data/icons/add.png deleted file mode 100644 index 9ab6832958..0000000000 Binary files a/app/data/icons/add.png and /dev/null differ diff --git a/app/data/icons/controls/duplicate.png b/app/data/icons/controls/duplicate.png deleted file mode 100644 index dc2285a0c7..0000000000 Binary files a/app/data/icons/controls/duplicate.png and /dev/null differ diff --git a/app/data/icons/controls/endplay.png b/app/data/icons/controls/endplay.png deleted file mode 100644 index 0743793d4a..0000000000 Binary files a/app/data/icons/controls/endplay.png and /dev/null differ diff --git a/app/data/icons/controls/loop.png b/app/data/icons/controls/loop.png deleted file mode 100644 index 188cfeb40b..0000000000 Binary files a/app/data/icons/controls/loop.png and /dev/null differ diff --git a/app/data/icons/controls/play.png b/app/data/icons/controls/play.png deleted file mode 100644 index 4377798c48..0000000000 Binary files a/app/data/icons/controls/play.png and /dev/null differ diff --git a/app/data/icons/controls/sound-disabled.png b/app/data/icons/controls/sound-disabled.png deleted file mode 100644 index 90d536ce6a..0000000000 Binary files a/app/data/icons/controls/sound-disabled.png and /dev/null differ diff --git a/app/data/icons/controls/sound.png b/app/data/icons/controls/sound.png deleted file mode 100644 index ba6a452165..0000000000 Binary files a/app/data/icons/controls/sound.png and /dev/null differ diff --git a/app/data/icons/controls/soundscrub-disabled.png b/app/data/icons/controls/soundscrub-disabled.png deleted file mode 100644 index 01aad7c7b6..0000000000 Binary files a/app/data/icons/controls/soundscrub-disabled.png and /dev/null differ diff --git a/app/data/icons/controls/soundscrub.png b/app/data/icons/controls/soundscrub.png deleted file mode 100644 index 51e1c1bf06..0000000000 Binary files a/app/data/icons/controls/soundscrub.png and /dev/null differ diff --git a/app/data/icons/controls/startplay.png b/app/data/icons/controls/startplay.png deleted file mode 100644 index a060f6c0bb..0000000000 Binary files a/app/data/icons/controls/startplay.png and /dev/null differ diff --git a/app/data/icons/controls/stop.png b/app/data/icons/controls/stop.png deleted file mode 100644 index 0fcc166a66..0000000000 Binary files a/app/data/icons/controls/stop.png and /dev/null differ diff --git a/app/data/icons/copy.png b/app/data/icons/copy.png deleted file mode 100644 index 4d60db641f..0000000000 Binary files a/app/data/icons/copy.png and /dev/null differ diff --git a/app/data/icons/cut.png b/app/data/icons/cut.png deleted file mode 100644 index 4222da9688..0000000000 Binary files a/app/data/icons/cut.png and /dev/null differ diff --git a/app/data/icons/dialog-error.svg b/app/data/icons/dialog-error.svg deleted file mode 100644 index c5e13b529e..0000000000 --- a/app/data/icons/dialog-error.svg +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - Dialog Warning - - - - dialog - warning - - - - - Open Clip Art Library, Source: Tango Icon Library, Source: Tango Icon Library, Source: Tango Icon Library, Source: Tango Icon Library - - - - - Andreas Nilsson - - - - - Andreas Nilsson - - - 2005-10-14 - image/svg+xml - - - en - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/exit.png b/app/data/icons/exit.png deleted file mode 100644 index 0e21816019..0000000000 Binary files a/app/data/icons/exit.png and /dev/null differ diff --git a/app/data/icons/new/checkerboard_smaller.png b/app/data/icons/general/checkerboard_smaller.png similarity index 100% rename from app/data/icons/new/checkerboard_smaller.png rename to app/data/icons/general/checkerboard_smaller.png diff --git a/core_lib/data/icons/cross.png b/app/data/icons/general/cross.png similarity index 100% rename from core_lib/data/icons/cross.png rename to app/data/icons/general/cross.png diff --git a/app/data/icons/general/cursor-brush.svg b/app/data/icons/general/cursor-brush.svg new file mode 100644 index 0000000000..908dc2b97f --- /dev/null +++ b/app/data/icons/general/cursor-brush.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/general/cursor-bucket.svg b/app/data/icons/general/cursor-bucket.svg new file mode 100644 index 0000000000..4b9c9cf3a0 --- /dev/null +++ b/app/data/icons/general/cursor-bucket.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/new/svg/cursor-diagonal-left.svg b/app/data/icons/general/cursor-diagonal-left.svg similarity index 100% rename from app/data/icons/new/svg/cursor-diagonal-left.svg rename to app/data/icons/general/cursor-diagonal-left.svg diff --git a/app/data/icons/new/svg/cursor-diagonal-right.svg b/app/data/icons/general/cursor-diagonal-right.svg similarity index 100% rename from app/data/icons/new/svg/cursor-diagonal-right.svg rename to app/data/icons/general/cursor-diagonal-right.svg diff --git a/app/data/icons/general/cursor-eyedropper.svg b/app/data/icons/general/cursor-eyedropper.svg new file mode 100644 index 0000000000..522ac4e42d --- /dev/null +++ b/app/data/icons/general/cursor-eyedropper.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/new/svg/cursor-horizontal.svg b/app/data/icons/general/cursor-horizontal.svg similarity index 100% rename from app/data/icons/new/svg/cursor-horizontal.svg rename to app/data/icons/general/cursor-horizontal.svg diff --git a/app/data/icons/new/svg/cursor-move.svg b/app/data/icons/general/cursor-move.svg similarity index 100% rename from app/data/icons/new/svg/cursor-move.svg rename to app/data/icons/general/cursor-move.svg diff --git a/app/data/icons/general/cursor-pen.svg b/app/data/icons/general/cursor-pen.svg new file mode 100644 index 0000000000..59753b35c9 --- /dev/null +++ b/app/data/icons/general/cursor-pen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/general/cursor-pencil.svg b/app/data/icons/general/cursor-pencil.svg new file mode 100644 index 0000000000..002b17d465 --- /dev/null +++ b/app/data/icons/general/cursor-pencil.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/new/svg/cursor-rotate.svg b/app/data/icons/general/cursor-rotate.svg similarity index 100% rename from app/data/icons/new/svg/cursor-rotate.svg rename to app/data/icons/general/cursor-rotate.svg diff --git a/app/data/icons/general/cursor-smudge-liquify.svg b/app/data/icons/general/cursor-smudge-liquify.svg new file mode 100644 index 0000000000..24e96799a9 --- /dev/null +++ b/app/data/icons/general/cursor-smudge-liquify.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/general/cursor-smudge.svg b/app/data/icons/general/cursor-smudge.svg new file mode 100644 index 0000000000..d8f4c56af1 --- /dev/null +++ b/app/data/icons/general/cursor-smudge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/new/svg/cursor-vertical.svg b/app/data/icons/general/cursor-vertical.svg similarity index 100% rename from app/data/icons/new/svg/cursor-vertical.svg rename to app/data/icons/general/cursor-vertical.svg diff --git a/app/data/icons/grid.png b/app/data/icons/grid.png deleted file mode 100644 index f44227b9be..0000000000 Binary files a/app/data/icons/grid.png and /dev/null differ diff --git a/app/data/icons/layer-bitmap.png b/app/data/icons/layer-bitmap.png deleted file mode 100644 index f955813ea7..0000000000 Binary files a/app/data/icons/layer-bitmap.png and /dev/null differ diff --git a/app/data/icons/layer-camera.png b/app/data/icons/layer-camera.png deleted file mode 100644 index 2003f60ecc..0000000000 Binary files a/app/data/icons/layer-camera.png and /dev/null differ diff --git a/app/data/icons/layer-sound.png b/app/data/icons/layer-sound.png deleted file mode 100644 index c9c9c95a67..0000000000 Binary files a/app/data/icons/layer-sound.png and /dev/null differ diff --git a/app/data/icons/layer-vector.png b/app/data/icons/layer-vector.png deleted file mode 100644 index d264d8c53f..0000000000 Binary files a/app/data/icons/layer-vector.png and /dev/null differ diff --git a/app/data/icons/magnify.png b/app/data/icons/magnify.png deleted file mode 100644 index 3ee87bc0dc..0000000000 Binary files a/app/data/icons/magnify.png and /dev/null differ diff --git a/app/data/icons/mirror.png b/app/data/icons/mirror.png deleted file mode 100644 index 54af26766e..0000000000 Binary files a/app/data/icons/mirror.png and /dev/null differ diff --git a/app/data/icons/mirrorV.png b/app/data/icons/mirrorV.png deleted file mode 100644 index dfe0c01bd9..0000000000 Binary files a/app/data/icons/mirrorV.png and /dev/null differ diff --git a/app/data/icons/new.png b/app/data/icons/new.png deleted file mode 100644 index 6bd01bb238..0000000000 Binary files a/app/data/icons/new.png and /dev/null differ diff --git a/app/data/icons/new/arrow.png b/app/data/icons/new/arrow.png deleted file mode 100644 index c3192963f5..0000000000 Binary files a/app/data/icons/new/arrow.png and /dev/null differ diff --git a/app/data/icons/new/brush_detailed.png b/app/data/icons/new/brush_detailed.png deleted file mode 100644 index 677a2f0bfd..0000000000 Binary files a/app/data/icons/new/brush_detailed.png and /dev/null differ diff --git a/app/data/icons/new/brush_flat_outlined.png b/app/data/icons/new/brush_flat_outlined.png deleted file mode 100644 index f8ad7c56a3..0000000000 Binary files a/app/data/icons/new/brush_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/new/bucket_detailed.png b/app/data/icons/new/bucket_detailed.png deleted file mode 100644 index c8a664e628..0000000000 Binary files a/app/data/icons/new/bucket_detailed.png and /dev/null differ diff --git a/app/data/icons/new/bucket_flat_outlined.png b/app/data/icons/new/bucket_flat_outlined.png deleted file mode 100644 index b18abef14a..0000000000 Binary files a/app/data/icons/new/bucket_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/new/eraser_detailed.png b/app/data/icons/new/eraser_detailed.png deleted file mode 100644 index acb6e5f692..0000000000 Binary files a/app/data/icons/new/eraser_detailed.png and /dev/null differ diff --git a/app/data/icons/new/eraser_flat_outlined.png b/app/data/icons/new/eraser_flat_outlined.png deleted file mode 100644 index c80869e8f5..0000000000 Binary files a/app/data/icons/new/eraser_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/new/eyedropper_detailed.png b/app/data/icons/new/eyedropper_detailed.png deleted file mode 100644 index 7077bad199..0000000000 Binary files a/app/data/icons/new/eyedropper_detailed.png and /dev/null differ diff --git a/app/data/icons/new/eyedropper_flat_outlined.png b/app/data/icons/new/eyedropper_flat_outlined.png deleted file mode 100644 index 11c398feb4..0000000000 Binary files a/app/data/icons/new/eyedropper_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/new/hand_detailed.png b/app/data/icons/new/hand_detailed.png deleted file mode 100644 index f1bb467c1c..0000000000 Binary files a/app/data/icons/new/hand_detailed.png and /dev/null differ diff --git a/app/data/icons/new/hand_flat_outlined.png b/app/data/icons/new/hand_flat_outlined.png deleted file mode 100644 index c9702db012..0000000000 Binary files a/app/data/icons/new/hand_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/new/line.png b/app/data/icons/new/line.png deleted file mode 100644 index bd2049e0ef..0000000000 Binary files a/app/data/icons/new/line.png and /dev/null differ diff --git a/app/data/icons/new/pen_detailed.png b/app/data/icons/new/pen_detailed.png deleted file mode 100644 index aef9720720..0000000000 Binary files a/app/data/icons/new/pen_detailed.png and /dev/null differ diff --git a/app/data/icons/new/pen_flat_outlined.png b/app/data/icons/new/pen_flat_outlined.png deleted file mode 100644 index fac8320e34..0000000000 Binary files a/app/data/icons/new/pen_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/new/pencil_detailed.png b/app/data/icons/new/pencil_detailed.png deleted file mode 100644 index 9517a1398d..0000000000 Binary files a/app/data/icons/new/pencil_detailed.png and /dev/null differ diff --git a/app/data/icons/new/pencil_flat_outlined.png b/app/data/icons/new/pencil_flat_outlined.png deleted file mode 100644 index 3c9f2404b9..0000000000 Binary files a/app/data/icons/new/pencil_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/new/selection.png b/app/data/icons/new/selection.png deleted file mode 100644 index 426ed090f2..0000000000 Binary files a/app/data/icons/new/selection.png and /dev/null differ diff --git a/app/data/icons/new/smudge_detailed.png b/app/data/icons/new/smudge_detailed.png deleted file mode 100644 index c5fe1ba47a..0000000000 Binary files a/app/data/icons/new/smudge_detailed.png and /dev/null differ diff --git a/app/data/icons/new/smudge_flat_outlined.png b/app/data/icons/new/smudge_flat_outlined.png deleted file mode 100644 index 5defd1232a..0000000000 Binary files a/app/data/icons/new/smudge_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/new/svg/arrow.svg b/app/data/icons/new/svg/arrow.svg deleted file mode 100644 index dccb22fbad..0000000000 --- a/app/data/icons/new/svg/arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/brush_detailed.svg b/app/data/icons/new/svg/brush_detailed.svg deleted file mode 100644 index 3439863142..0000000000 --- a/app/data/icons/new/svg/brush_detailed.svg +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/brush_flat_outlined.svg b/app/data/icons/new/svg/brush_flat_outlined.svg deleted file mode 100644 index 2cc465649c..0000000000 --- a/app/data/icons/new/svg/brush_flat_outlined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/bucket_detailed.svg b/app/data/icons/new/svg/bucket_detailed.svg deleted file mode 100644 index 2451c4f20b..0000000000 --- a/app/data/icons/new/svg/bucket_detailed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/bucket_flat_outlined.svg b/app/data/icons/new/svg/bucket_flat_outlined.svg deleted file mode 100644 index 28ac13c64e..0000000000 --- a/app/data/icons/new/svg/bucket_flat_outlined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/camera-move.svg b/app/data/icons/new/svg/camera-move.svg deleted file mode 100644 index 3de5227242..0000000000 --- a/app/data/icons/new/svg/camera-move.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/camera-rotate.svg b/app/data/icons/new/svg/camera-rotate.svg deleted file mode 100644 index a99b2392ab..0000000000 --- a/app/data/icons/new/svg/camera-rotate.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/camera-scale.svg b/app/data/icons/new/svg/camera-scale.svg deleted file mode 100644 index fdf95dff99..0000000000 --- a/app/data/icons/new/svg/camera-scale.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/color-dialog.svg b/app/data/icons/new/svg/color-dialog.svg deleted file mode 100644 index 4a79d4bc75..0000000000 --- a/app/data/icons/new/svg/color-dialog.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/eraser_detailed.svg b/app/data/icons/new/svg/eraser_detailed.svg deleted file mode 100644 index f981f4d059..0000000000 --- a/app/data/icons/new/svg/eraser_detailed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/eraser_flat_outlined.svg b/app/data/icons/new/svg/eraser_flat_outlined.svg deleted file mode 100644 index 24735cd0a9..0000000000 --- a/app/data/icons/new/svg/eraser_flat_outlined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/eyedropper_detailed.svg b/app/data/icons/new/svg/eyedropper_detailed.svg deleted file mode 100644 index ae223140c0..0000000000 --- a/app/data/icons/new/svg/eyedropper_detailed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/eyedropper_flat_outlined.svg b/app/data/icons/new/svg/eyedropper_flat_outlined.svg deleted file mode 100644 index 16a19438a4..0000000000 --- a/app/data/icons/new/svg/eyedropper_flat_outlined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/hand_detailed.svg b/app/data/icons/new/svg/hand_detailed.svg deleted file mode 100644 index b1dc80d364..0000000000 --- a/app/data/icons/new/svg/hand_detailed.svg +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/hand_flat_outlined.svg b/app/data/icons/new/svg/hand_flat_outlined.svg deleted file mode 100644 index 3c320cfc47..0000000000 --- a/app/data/icons/new/svg/hand_flat_outlined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/line.svg b/app/data/icons/new/svg/line.svg deleted file mode 100644 index c727549f2c..0000000000 --- a/app/data/icons/new/svg/line.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/more_options.svg b/app/data/icons/new/svg/more_options.svg deleted file mode 100644 index 07f79b30fd..0000000000 --- a/app/data/icons/new/svg/more_options.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/pen_detailed.svg b/app/data/icons/new/svg/pen_detailed.svg deleted file mode 100644 index be8238920b..0000000000 --- a/app/data/icons/new/svg/pen_detailed.svg +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/pen_flat_outlined.svg b/app/data/icons/new/svg/pen_flat_outlined.svg deleted file mode 100644 index a2588d50df..0000000000 --- a/app/data/icons/new/svg/pen_flat_outlined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/pen_pattern_detailed.svg b/app/data/icons/new/svg/pen_pattern_detailed.svg deleted file mode 100644 index 931fcb1ab9..0000000000 --- a/app/data/icons/new/svg/pen_pattern_detailed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/pencil_detailed.svg b/app/data/icons/new/svg/pencil_detailed.svg deleted file mode 100644 index 3f611b6ac2..0000000000 --- a/app/data/icons/new/svg/pencil_detailed.svg +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/pencil_flat_outlined.svg b/app/data/icons/new/svg/pencil_flat_outlined.svg deleted file mode 100644 index 2e32816869..0000000000 --- a/app/data/icons/new/svg/pencil_flat_outlined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/selection.svg b/app/data/icons/new/svg/selection.svg deleted file mode 100644 index d93ec28657..0000000000 --- a/app/data/icons/new/svg/selection.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/smudge_detailed.svg b/app/data/icons/new/svg/smudge_detailed.svg deleted file mode 100644 index 172a92c75a..0000000000 --- a/app/data/icons/new/svg/smudge_detailed.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/smudge_flat_outlined.svg b/app/data/icons/new/svg/smudge_flat_outlined.svg deleted file mode 100644 index 4ef010bbbc..0000000000 --- a/app/data/icons/new/svg/smudge_flat_outlined.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/data/icons/new/svg/trash_detailed.svg b/app/data/icons/new/svg/trash_detailed.svg deleted file mode 100644 index 6fb77c6297..0000000000 --- a/app/data/icons/new/svg/trash_detailed.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/svg/trash_flat_outlined.svg b/app/data/icons/new/svg/trash_flat_outlined.svg deleted file mode 100644 index 070e36d853..0000000000 --- a/app/data/icons/new/svg/trash_flat_outlined.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/data/icons/new/trash_detailed.png b/app/data/icons/new/trash_detailed.png deleted file mode 100644 index f3ef881348..0000000000 Binary files a/app/data/icons/new/trash_detailed.png and /dev/null differ diff --git a/app/data/icons/new/trash_flat_outlined.png b/app/data/icons/new/trash_flat_outlined.png deleted file mode 100644 index 848f0a8243..0000000000 Binary files a/app/data/icons/new/trash_flat_outlined.png and /dev/null differ diff --git a/app/data/icons/next.png b/app/data/icons/next.png deleted file mode 100644 index 625adbb2c5..0000000000 Binary files a/app/data/icons/next.png and /dev/null differ diff --git a/app/data/icons/onion-blue.png b/app/data/icons/onion-blue.png deleted file mode 100644 index ad3520ef21..0000000000 Binary files a/app/data/icons/onion-blue.png and /dev/null differ diff --git a/app/data/icons/onion-red.png b/app/data/icons/onion-red.png deleted file mode 100644 index c05086a945..0000000000 Binary files a/app/data/icons/onion-red.png and /dev/null differ diff --git a/app/data/icons/onionNext.png b/app/data/icons/onionNext.png deleted file mode 100644 index 45bb9d0136..0000000000 Binary files a/app/data/icons/onionNext.png and /dev/null differ diff --git a/app/data/icons/onionPrev.png b/app/data/icons/onionPrev.png deleted file mode 100644 index 09f529e55c..0000000000 Binary files a/app/data/icons/onionPrev.png and /dev/null differ diff --git a/app/data/icons/open.png b/app/data/icons/open.png deleted file mode 100644 index a79982ea1f..0000000000 Binary files a/app/data/icons/open.png and /dev/null differ diff --git a/app/data/icons/outlines5.png b/app/data/icons/outlines5.png deleted file mode 100644 index c466bb8ec6..0000000000 Binary files a/app/data/icons/outlines5.png and /dev/null differ diff --git a/app/data/icons/overlayAngle.png b/app/data/icons/overlayAngle.png deleted file mode 100644 index 244ac3264e..0000000000 Binary files a/app/data/icons/overlayAngle.png and /dev/null differ diff --git a/app/data/icons/overlayCenter.png b/app/data/icons/overlayCenter.png deleted file mode 100644 index 78c1f7b73f..0000000000 Binary files a/app/data/icons/overlayCenter.png and /dev/null differ diff --git a/app/data/icons/overlayGoldenRatio.png b/app/data/icons/overlayGoldenRatio.png deleted file mode 100644 index a207587b29..0000000000 Binary files a/app/data/icons/overlayGoldenRatio.png and /dev/null differ diff --git a/app/data/icons/overlayPerspective1.png b/app/data/icons/overlayPerspective1.png deleted file mode 100644 index 981cf33dfa..0000000000 Binary files a/app/data/icons/overlayPerspective1.png and /dev/null differ diff --git a/app/data/icons/overlayPerspective2.png b/app/data/icons/overlayPerspective2.png deleted file mode 100644 index 1ee0bb396e..0000000000 Binary files a/app/data/icons/overlayPerspective2.png and /dev/null differ diff --git a/app/data/icons/overlayPerspective3.png b/app/data/icons/overlayPerspective3.png deleted file mode 100644 index e06848d05a..0000000000 Binary files a/app/data/icons/overlayPerspective3.png and /dev/null differ diff --git a/app/data/icons/overlaySafe.png b/app/data/icons/overlaySafe.png deleted file mode 100644 index d0563e97cb..0000000000 Binary files a/app/data/icons/overlaySafe.png and /dev/null differ diff --git a/app/data/icons/overlayThirds.png b/app/data/icons/overlayThirds.png deleted file mode 100644 index ccf9d026de..0000000000 Binary files a/app/data/icons/overlayThirds.png and /dev/null differ diff --git a/app/data/icons/paste.png b/app/data/icons/paste.png deleted file mode 100644 index 5e60ba794c..0000000000 Binary files a/app/data/icons/paste.png and /dev/null differ diff --git a/app/data/icons/prefs-files.png b/app/data/icons/prefs-files.png deleted file mode 100644 index 85bc16e0ef..0000000000 Binary files a/app/data/icons/prefs-files.png and /dev/null differ diff --git a/app/data/icons/prefs-shortcuts.png b/app/data/icons/prefs-shortcuts.png deleted file mode 100644 index 1fd7bd0135..0000000000 Binary files a/app/data/icons/prefs-shortcuts.png and /dev/null differ diff --git a/app/data/icons/prefspencil.png b/app/data/icons/prefspencil.png deleted file mode 100644 index 84d69a7041..0000000000 Binary files a/app/data/icons/prefspencil.png and /dev/null differ diff --git a/app/data/icons/prefstimeline.png b/app/data/icons/prefstimeline.png deleted file mode 100644 index fb00fc9630..0000000000 Binary files a/app/data/icons/prefstimeline.png and /dev/null differ diff --git a/app/data/icons/prev.png b/app/data/icons/prev.png deleted file mode 100644 index e3a64d5276..0000000000 Binary files a/app/data/icons/prev.png and /dev/null differ diff --git a/app/data/icons/redo.png b/app/data/icons/redo.png deleted file mode 100644 index 8a4c817d01..0000000000 Binary files a/app/data/icons/redo.png and /dev/null differ diff --git a/app/data/icons/remove.png b/app/data/icons/remove.png deleted file mode 100644 index 7b7d407eb4..0000000000 Binary files a/app/data/icons/remove.png and /dev/null differ diff --git a/app/data/icons/save.png b/app/data/icons/save.png deleted file mode 100644 index 47d8f201f8..0000000000 Binary files a/app/data/icons/save.png and /dev/null differ diff --git a/app/data/icons/saveas.png b/app/data/icons/saveas.png deleted file mode 100644 index 7e478ebb6d..0000000000 Binary files a/app/data/icons/saveas.png and /dev/null differ diff --git a/app/data/icons/themes/playful/controls/control-loop.svg b/app/data/icons/themes/playful/controls/control-loop.svg new file mode 100644 index 0000000000..accd68b0eb --- /dev/null +++ b/app/data/icons/themes/playful/controls/control-loop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/controls/control-play-end.svg b/app/data/icons/themes/playful/controls/control-play-end.svg new file mode 100644 index 0000000000..4d9464ee48 --- /dev/null +++ b/app/data/icons/themes/playful/controls/control-play-end.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/controls/control-play-start.svg b/app/data/icons/themes/playful/controls/control-play-start.svg new file mode 100644 index 0000000000..06b7b58ca2 --- /dev/null +++ b/app/data/icons/themes/playful/controls/control-play-start.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/controls/control-play.svg b/app/data/icons/themes/playful/controls/control-play.svg new file mode 100644 index 0000000000..1bc0aa42d2 --- /dev/null +++ b/app/data/icons/themes/playful/controls/control-play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/controls/control-sound-enable.svg b/app/data/icons/themes/playful/controls/control-sound-enable.svg new file mode 100644 index 0000000000..6bf0efd276 --- /dev/null +++ b/app/data/icons/themes/playful/controls/control-sound-enable.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/controls/control-sound-scrub.svg b/app/data/icons/themes/playful/controls/control-sound-scrub.svg new file mode 100644 index 0000000000..af430cbaa1 --- /dev/null +++ b/app/data/icons/themes/playful/controls/control-sound-scrub.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/controls/control-stop.svg b/app/data/icons/themes/playful/controls/control-stop.svg new file mode 100644 index 0000000000..8407d60b4f --- /dev/null +++ b/app/data/icons/themes/playful/controls/control-stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/dialog-error.svg b/app/data/icons/themes/playful/dialog-error.svg new file mode 100644 index 0000000000..865a31e319 --- /dev/null +++ b/app/data/icons/themes/playful/dialog-error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/lines-invisible.svg b/app/data/icons/themes/playful/display/lines-invisible.svg new file mode 100644 index 0000000000..1960855348 --- /dev/null +++ b/app/data/icons/themes/playful/display/lines-invisible.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/lines-outline.svg b/app/data/icons/themes/playful/display/lines-outline.svg new file mode 100644 index 0000000000..06203759a0 --- /dev/null +++ b/app/data/icons/themes/playful/display/lines-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/mirror-horizontal.svg b/app/data/icons/themes/playful/display/mirror-horizontal.svg new file mode 100644 index 0000000000..f8af0aa110 --- /dev/null +++ b/app/data/icons/themes/playful/display/mirror-horizontal.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/mirror-vertical.svg b/app/data/icons/themes/playful/display/mirror-vertical.svg new file mode 100644 index 0000000000..cebd5b7a79 --- /dev/null +++ b/app/data/icons/themes/playful/display/mirror-vertical.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/overlay-center.svg b/app/data/icons/themes/playful/display/overlay-center.svg new file mode 100644 index 0000000000..322a746ad8 --- /dev/null +++ b/app/data/icons/themes/playful/display/overlay-center.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/overlay-golden-ratio.svg b/app/data/icons/themes/playful/display/overlay-golden-ratio.svg new file mode 100644 index 0000000000..c1d1c2e1a3 --- /dev/null +++ b/app/data/icons/themes/playful/display/overlay-golden-ratio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/overlay-grid.svg b/app/data/icons/themes/playful/display/overlay-grid.svg new file mode 100644 index 0000000000..cf629212cb --- /dev/null +++ b/app/data/icons/themes/playful/display/overlay-grid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/overlay-safe.svg b/app/data/icons/themes/playful/display/overlay-safe.svg new file mode 100644 index 0000000000..34ece24a7f --- /dev/null +++ b/app/data/icons/themes/playful/display/overlay-safe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/overlay-thirds.svg b/app/data/icons/themes/playful/display/overlay-thirds.svg new file mode 100644 index 0000000000..b03b5a0740 --- /dev/null +++ b/app/data/icons/themes/playful/display/overlay-thirds.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/perspective-angle.svg b/app/data/icons/themes/playful/display/perspective-angle.svg new file mode 100644 index 0000000000..730edb4dbe --- /dev/null +++ b/app/data/icons/themes/playful/display/perspective-angle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/perspective-onepoint.svg b/app/data/icons/themes/playful/display/perspective-onepoint.svg new file mode 100644 index 0000000000..181dfed2e2 --- /dev/null +++ b/app/data/icons/themes/playful/display/perspective-onepoint.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/perspective-threepoints.svg b/app/data/icons/themes/playful/display/perspective-threepoints.svg new file mode 100644 index 0000000000..4e5ee7f054 --- /dev/null +++ b/app/data/icons/themes/playful/display/perspective-threepoints.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/display/perspective-twopoints.svg b/app/data/icons/themes/playful/display/perspective-twopoints.svg new file mode 100644 index 0000000000..478c627a2a --- /dev/null +++ b/app/data/icons/themes/playful/display/perspective-twopoints.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/clear-canvas.svg b/app/data/icons/themes/playful/menubar/clear-canvas.svg new file mode 100644 index 0000000000..622a54fba9 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/clear-canvas.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/copy.svg b/app/data/icons/themes/playful/menubar/copy.svg new file mode 100644 index 0000000000..ee48daa369 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/cut.svg b/app/data/icons/themes/playful/menubar/cut.svg new file mode 100644 index 0000000000..d97b9e4818 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/cut.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/new.svg b/app/data/icons/themes/playful/menubar/new.svg new file mode 100644 index 0000000000..f554767916 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/new.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/open.svg b/app/data/icons/themes/playful/menubar/open.svg new file mode 100644 index 0000000000..a344bc2213 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/paste.svg b/app/data/icons/themes/playful/menubar/paste.svg new file mode 100644 index 0000000000..77635a80f7 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/paste.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/redo.svg b/app/data/icons/themes/playful/menubar/redo.svg new file mode 100644 index 0000000000..e0d0b29bab --- /dev/null +++ b/app/data/icons/themes/playful/menubar/redo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/save.svg b/app/data/icons/themes/playful/menubar/save.svg new file mode 100644 index 0000000000..5c0e590030 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/save.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/undo.svg b/app/data/icons/themes/playful/menubar/undo.svg new file mode 100644 index 0000000000..93a7b3129f --- /dev/null +++ b/app/data/icons/themes/playful/menubar/undo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/view-reset.svg b/app/data/icons/themes/playful/menubar/view-reset.svg new file mode 100644 index 0000000000..4744b3e865 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/view-reset.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/zoom-in.svg b/app/data/icons/themes/playful/menubar/zoom-in.svg new file mode 100644 index 0000000000..b0b9338c98 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/zoom-in.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/zoom-out.svg b/app/data/icons/themes/playful/menubar/zoom-out.svg new file mode 100644 index 0000000000..9251aa6d29 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/zoom-out.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/menubar/zoom-select.svg b/app/data/icons/themes/playful/menubar/zoom-select.svg new file mode 100644 index 0000000000..fc55991536 --- /dev/null +++ b/app/data/icons/themes/playful/menubar/zoom-select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/misc/add-color.svg b/app/data/icons/themes/playful/misc/add-color.svg new file mode 100644 index 0000000000..b4e82b5bc0 --- /dev/null +++ b/app/data/icons/themes/playful/misc/add-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/misc/color-dialog.svg b/app/data/icons/themes/playful/misc/color-dialog.svg new file mode 100644 index 0000000000..e48c0e7abd --- /dev/null +++ b/app/data/icons/themes/playful/misc/color-dialog.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/misc/more-options.svg b/app/data/icons/themes/playful/misc/more-options.svg new file mode 100644 index 0000000000..8edf749a6e --- /dev/null +++ b/app/data/icons/themes/playful/misc/more-options.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/misc/remove-color.svg b/app/data/icons/themes/playful/misc/remove-color.svg new file mode 100644 index 0000000000..59987335b0 --- /dev/null +++ b/app/data/icons/themes/playful/misc/remove-color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/onion/onionskin-blue.svg b/app/data/icons/themes/playful/onion/onionskin-blue.svg new file mode 100644 index 0000000000..2327808070 --- /dev/null +++ b/app/data/icons/themes/playful/onion/onionskin-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/onion/onionskin-enable.svg b/app/data/icons/themes/playful/onion/onionskin-enable.svg new file mode 100644 index 0000000000..2af800b059 --- /dev/null +++ b/app/data/icons/themes/playful/onion/onionskin-enable.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/onion/onionskin-red.svg b/app/data/icons/themes/playful/onion/onionskin-red.svg new file mode 100644 index 0000000000..2dc1faf2ab --- /dev/null +++ b/app/data/icons/themes/playful/onion/onionskin-red.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/preferences/preferences-files.svg b/app/data/icons/themes/playful/preferences/preferences-files.svg new file mode 100644 index 0000000000..f93af37091 --- /dev/null +++ b/app/data/icons/themes/playful/preferences/preferences-files.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/preferences/preferences-general.svg b/app/data/icons/themes/playful/preferences/preferences-general.svg new file mode 100644 index 0000000000..d1a238695a --- /dev/null +++ b/app/data/icons/themes/playful/preferences/preferences-general.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/preferences/preferences-shortcuts.svg b/app/data/icons/themes/playful/preferences/preferences-shortcuts.svg new file mode 100644 index 0000000000..2957338e7d --- /dev/null +++ b/app/data/icons/themes/playful/preferences/preferences-shortcuts.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/preferences/preferences-timeline.svg b/app/data/icons/themes/playful/preferences/preferences-timeline.svg new file mode 100644 index 0000000000..fd7c7d924a --- /dev/null +++ b/app/data/icons/themes/playful/preferences/preferences-timeline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/preferences/preferences-tools.svg b/app/data/icons/themes/playful/preferences/preferences-tools.svg new file mode 100644 index 0000000000..122f4a3a65 --- /dev/null +++ b/app/data/icons/themes/playful/preferences/preferences-tools.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/cell-bitmap.svg b/app/data/icons/themes/playful/timeline/cell-bitmap.svg new file mode 100644 index 0000000000..d6c1ea2335 --- /dev/null +++ b/app/data/icons/themes/playful/timeline/cell-bitmap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/cell-camera.svg b/app/data/icons/themes/playful/timeline/cell-camera.svg new file mode 100644 index 0000000000..c493389459 --- /dev/null +++ b/app/data/icons/themes/playful/timeline/cell-camera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/cell-sound.svg b/app/data/icons/themes/playful/timeline/cell-sound.svg new file mode 100644 index 0000000000..3503240e2b --- /dev/null +++ b/app/data/icons/themes/playful/timeline/cell-sound.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/cell-vector.svg b/app/data/icons/themes/playful/timeline/cell-vector.svg new file mode 100644 index 0000000000..a00f27599d --- /dev/null +++ b/app/data/icons/themes/playful/timeline/cell-vector.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/frame-add.svg b/app/data/icons/themes/playful/timeline/frame-add.svg new file mode 100644 index 0000000000..d898e6ac50 --- /dev/null +++ b/app/data/icons/themes/playful/timeline/frame-add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/frame-duplicate.svg b/app/data/icons/themes/playful/timeline/frame-duplicate.svg new file mode 100644 index 0000000000..fcbe1a8caa --- /dev/null +++ b/app/data/icons/themes/playful/timeline/frame-duplicate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/frame-remove.svg b/app/data/icons/themes/playful/timeline/frame-remove.svg new file mode 100644 index 0000000000..56977b6d1d --- /dev/null +++ b/app/data/icons/themes/playful/timeline/frame-remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/layer-add.svg b/app/data/icons/themes/playful/timeline/layer-add.svg new file mode 100644 index 0000000000..6aa9a06f5b --- /dev/null +++ b/app/data/icons/themes/playful/timeline/layer-add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/layer-duplicate.svg b/app/data/icons/themes/playful/timeline/layer-duplicate.svg new file mode 100644 index 0000000000..f6d0a9ebff --- /dev/null +++ b/app/data/icons/themes/playful/timeline/layer-duplicate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/timeline/layer-remove.svg b/app/data/icons/themes/playful/timeline/layer-remove.svg new file mode 100644 index 0000000000..b0af390983 --- /dev/null +++ b/app/data/icons/themes/playful/timeline/layer-remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-brush.svg b/app/data/icons/themes/playful/tools/tool-brush.svg new file mode 100644 index 0000000000..9627b4b631 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-brush.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-bucket.svg b/app/data/icons/themes/playful/tools/tool-bucket.svg new file mode 100644 index 0000000000..054a684f47 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-bucket.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-camera-move.svg b/app/data/icons/themes/playful/tools/tool-camera-move.svg new file mode 100644 index 0000000000..ac74f3c653 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-camera-move.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-camera-rotate.svg b/app/data/icons/themes/playful/tools/tool-camera-rotate.svg new file mode 100644 index 0000000000..853cc38239 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-camera-rotate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-camera-scale.svg b/app/data/icons/themes/playful/tools/tool-camera-scale.svg new file mode 100644 index 0000000000..bc8f954773 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-camera-scale.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-eraser.svg b/app/data/icons/themes/playful/tools/tool-eraser.svg new file mode 100644 index 0000000000..e9f2ab3f0e --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-eraser.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-eyedropper.svg b/app/data/icons/themes/playful/tools/tool-eyedropper.svg new file mode 100644 index 0000000000..808c1e7829 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-eyedropper.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-hand.svg b/app/data/icons/themes/playful/tools/tool-hand.svg new file mode 100644 index 0000000000..2071aa06bd --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-hand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-move.svg b/app/data/icons/themes/playful/tools/tool-move.svg new file mode 100644 index 0000000000..fbb384c16b --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-move.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-pen.svg b/app/data/icons/themes/playful/tools/tool-pen.svg new file mode 100644 index 0000000000..b593c30d54 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-pen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-pencil.svg b/app/data/icons/themes/playful/tools/tool-pencil.svg new file mode 100644 index 0000000000..c4945557a1 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-pencil.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-polyline.svg b/app/data/icons/themes/playful/tools/tool-polyline.svg new file mode 100644 index 0000000000..59da896252 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-polyline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-select.svg b/app/data/icons/themes/playful/tools/tool-select.svg new file mode 100644 index 0000000000..f25e2d0c16 --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/themes/playful/tools/tool-smudge.svg b/app/data/icons/themes/playful/tools/tool-smudge.svg new file mode 100644 index 0000000000..4527e8fe5a --- /dev/null +++ b/app/data/icons/themes/playful/tools/tool-smudge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/data/icons/thinlines5.png b/app/data/icons/thinlines5.png deleted file mode 100644 index 6d8655d1a2..0000000000 Binary files a/app/data/icons/thinlines5.png and /dev/null differ diff --git a/app/data/icons/undo.png b/app/data/icons/undo.png deleted file mode 100644 index a63c3ef998..0000000000 Binary files a/app/data/icons/undo.png and /dev/null differ diff --git a/app/data/icons/unused/TitleBarCloseButton.png b/app/data/icons/unused/TitleBarCloseButton.png deleted file mode 100644 index a1510b196c..0000000000 Binary files a/app/data/icons/unused/TitleBarCloseButton.png and /dev/null differ diff --git a/app/data/icons/unused/TitleBarNormalButton.png b/app/data/icons/unused/TitleBarNormalButton.png deleted file mode 100644 index d379c70a60..0000000000 Binary files a/app/data/icons/unused/TitleBarNormalButton.png and /dev/null differ diff --git a/app/data/icons/unused/TitleBarNormalButton1.png b/app/data/icons/unused/TitleBarNormalButton1.png deleted file mode 100644 index 7b73f6585e..0000000000 Binary files a/app/data/icons/unused/TitleBarNormalButton1.png and /dev/null differ diff --git a/app/data/icons/unused/TitleBarNormalButton2.png b/app/data/icons/unused/TitleBarNormalButton2.png deleted file mode 100644 index dbba6c93ef..0000000000 Binary files a/app/data/icons/unused/TitleBarNormalButton2.png and /dev/null differ diff --git a/app/data/icons/unused/aqua.png b/app/data/icons/unused/aqua.png deleted file mode 100644 index 303e80f24e..0000000000 Binary files a/app/data/icons/unused/aqua.png and /dev/null differ diff --git a/app/data/icons/unused/arrow.png b/app/data/icons/unused/arrow.png deleted file mode 100644 index ee926e95f4..0000000000 Binary files a/app/data/icons/unused/arrow.png and /dev/null differ diff --git a/app/data/icons/unused/brush0.png b/app/data/icons/unused/brush0.png deleted file mode 100644 index 03d1ecae21..0000000000 Binary files a/app/data/icons/unused/brush0.png and /dev/null differ diff --git a/app/data/icons/unused/bucket.png b/app/data/icons/unused/bucket.png deleted file mode 100644 index b87ff2bf47..0000000000 Binary files a/app/data/icons/unused/bucket.png and /dev/null differ diff --git a/app/data/icons/unused/bucket0.png b/app/data/icons/unused/bucket0.png deleted file mode 100644 index 98a74af436..0000000000 Binary files a/app/data/icons/unused/bucket0.png and /dev/null differ diff --git a/app/data/icons/unused/bucket1.png b/app/data/icons/unused/bucket1.png deleted file mode 100644 index 8823ce8cb1..0000000000 Binary files a/app/data/icons/unused/bucket1.png and /dev/null differ diff --git a/app/data/icons/unused/bucket2.png b/app/data/icons/unused/bucket2.png deleted file mode 100644 index 10a00993dd..0000000000 Binary files a/app/data/icons/unused/bucket2.png and /dev/null differ diff --git a/app/data/icons/unused/cameraBorder.png b/app/data/icons/unused/cameraBorder.png deleted file mode 100644 index 1961de39f2..0000000000 Binary files a/app/data/icons/unused/cameraBorder.png and /dev/null differ diff --git a/app/data/icons/unused/clear.png b/app/data/icons/unused/clear.png deleted file mode 100644 index e04870db8c..0000000000 Binary files a/app/data/icons/unused/clear.png and /dev/null differ diff --git a/app/data/icons/unused/eraser.png b/app/data/icons/unused/eraser.png deleted file mode 100644 index c86abe0c3c..0000000000 Binary files a/app/data/icons/unused/eraser.png and /dev/null differ diff --git a/app/data/icons/unused/eraser1.png b/app/data/icons/unused/eraser1.png deleted file mode 100644 index dc70873bec..0000000000 Binary files a/app/data/icons/unused/eraser1.png and /dev/null differ diff --git a/app/data/icons/unused/eraser2.png b/app/data/icons/unused/eraser2.png deleted file mode 100644 index fc7ec9a48d..0000000000 Binary files a/app/data/icons/unused/eraser2.png and /dev/null differ diff --git a/app/data/icons/unused/grid-b.png b/app/data/icons/unused/grid-b.png deleted file mode 100644 index f956c7a8ec..0000000000 Binary files a/app/data/icons/unused/grid-b.png and /dev/null differ diff --git a/app/data/icons/unused/hand.png b/app/data/icons/unused/hand.png deleted file mode 100644 index 2e16ba02c4..0000000000 Binary files a/app/data/icons/unused/hand.png and /dev/null differ diff --git a/app/data/icons/unused/house.png b/app/data/icons/unused/house.png deleted file mode 100644 index 00750a7621..0000000000 Binary files a/app/data/icons/unused/house.png and /dev/null differ diff --git a/app/data/icons/unused/logo0.png b/app/data/icons/unused/logo0.png deleted file mode 100644 index b632c49ac3..0000000000 Binary files a/app/data/icons/unused/logo0.png and /dev/null differ diff --git a/app/data/icons/unused/move.png b/app/data/icons/unused/move.png deleted file mode 100644 index 474bdf21ee..0000000000 Binary files a/app/data/icons/unused/move.png and /dev/null differ diff --git a/app/data/icons/unused/onion_type.png b/app/data/icons/unused/onion_type.png deleted file mode 100644 index d42ed6cf56..0000000000 Binary files a/app/data/icons/unused/onion_type.png and /dev/null differ diff --git a/app/data/icons/unused/outlines3.png b/app/data/icons/unused/outlines3.png deleted file mode 100644 index 025bcd3094..0000000000 Binary files a/app/data/icons/unused/outlines3.png and /dev/null differ diff --git a/app/data/icons/unused/outlines4.png b/app/data/icons/unused/outlines4.png deleted file mode 100644 index 6b2bffb26c..0000000000 Binary files a/app/data/icons/unused/outlines4.png and /dev/null differ diff --git a/app/data/icons/unused/overlayCenter.png b/app/data/icons/unused/overlayCenter.png deleted file mode 100644 index d01e95f071..0000000000 Binary files a/app/data/icons/unused/overlayCenter.png and /dev/null differ diff --git a/app/data/icons/unused/overlayGoldenRatio.png b/app/data/icons/unused/overlayGoldenRatio.png deleted file mode 100644 index 80a598da69..0000000000 Binary files a/app/data/icons/unused/overlayGoldenRatio.png and /dev/null differ diff --git a/app/data/icons/unused/overlayGoldenRatio_org.png b/app/data/icons/unused/overlayGoldenRatio_org.png deleted file mode 100644 index 21fa61af94..0000000000 Binary files a/app/data/icons/unused/overlayGoldenRatio_org.png and /dev/null differ diff --git a/app/data/icons/unused/overlaySafe.png b/app/data/icons/unused/overlaySafe.png deleted file mode 100644 index 3ed1c7facb..0000000000 Binary files a/app/data/icons/unused/overlaySafe.png and /dev/null differ diff --git a/app/data/icons/unused/overlaySafe_org.png b/app/data/icons/unused/overlaySafe_org.png deleted file mode 100644 index 34e9abe009..0000000000 Binary files a/app/data/icons/unused/overlaySafe_org.png and /dev/null differ diff --git a/app/data/icons/unused/overlayThirds.png b/app/data/icons/unused/overlayThirds.png deleted file mode 100644 index c0697d4a94..0000000000 Binary files a/app/data/icons/unused/overlayThirds.png and /dev/null differ diff --git a/app/data/icons/unused/pen0.png b/app/data/icons/unused/pen0.png deleted file mode 100644 index 326af06e2f..0000000000 Binary files a/app/data/icons/unused/pen0.png and /dev/null differ diff --git a/app/data/icons/unused/pencil.png b/app/data/icons/unused/pencil.png deleted file mode 100644 index 671082a0b4..0000000000 Binary files a/app/data/icons/unused/pencil.png and /dev/null differ diff --git a/app/data/icons/unused/pencil3.png b/app/data/icons/unused/pencil3.png deleted file mode 100644 index 8b2c430f0b..0000000000 Binary files a/app/data/icons/unused/pencil3.png and /dev/null differ diff --git a/app/data/icons/unused/polyline.png b/app/data/icons/unused/polyline.png deleted file mode 100644 index d7d2d59ce3..0000000000 Binary files a/app/data/icons/unused/polyline.png and /dev/null differ diff --git a/app/data/icons/unused/prefstimeline2.png b/app/data/icons/unused/prefstimeline2.png deleted file mode 100644 index c906a14521..0000000000 Binary files a/app/data/icons/unused/prefstimeline2.png and /dev/null differ diff --git a/app/data/icons/unused/printer3.png b/app/data/icons/unused/printer3.png deleted file mode 100644 index 84eac292b1..0000000000 Binary files a/app/data/icons/unused/printer3.png and /dev/null differ diff --git a/app/data/icons/unused/select.png b/app/data/icons/unused/select.png deleted file mode 100644 index 317f6f85e9..0000000000 Binary files a/app/data/icons/unused/select.png and /dev/null differ diff --git a/app/data/icons/unused/separator.png b/app/data/icons/unused/separator.png deleted file mode 100644 index f0b6a43c87..0000000000 Binary files a/app/data/icons/unused/separator.png and /dev/null differ diff --git a/app/data/icons/unused/smudgeblur.png b/app/data/icons/unused/smudgeblur.png deleted file mode 100644 index 5a11cf9560..0000000000 Binary files a/app/data/icons/unused/smudgeblur.png and /dev/null differ diff --git a/app/data/icons/unused/thinlines.png b/app/data/icons/unused/thinlines.png deleted file mode 100644 index b7b8393a9d..0000000000 Binary files a/app/data/icons/unused/thinlines.png and /dev/null differ diff --git a/app/data/icons/unused/thinlines2.png b/app/data/icons/unused/thinlines2.png deleted file mode 100644 index eb1c17204e..0000000000 Binary files a/app/data/icons/unused/thinlines2.png and /dev/null differ diff --git a/app/data/icons/unused/thinlines3.png b/app/data/icons/unused/thinlines3.png deleted file mode 100644 index d6e875b410..0000000000 Binary files a/app/data/icons/unused/thinlines3.png and /dev/null differ diff --git a/app/data/icons/unused/thinlines4.png b/app/data/icons/unused/thinlines4.png deleted file mode 100644 index 2c6ab49d67..0000000000 Binary files a/app/data/icons/unused/thinlines4.png and /dev/null differ diff --git a/app/data/icons/zoom-in.png b/app/data/icons/zoom-in.png deleted file mode 100644 index 0d63f15d26..0000000000 Binary files a/app/data/icons/zoom-in.png and /dev/null differ diff --git a/app/data/icons/zoom-out.png b/app/data/icons/zoom-out.png deleted file mode 100644 index 7ad245c474..0000000000 Binary files a/app/data/icons/zoom-out.png and /dev/null differ diff --git a/app/data/icons/zoom-reset.png b/app/data/icons/zoom-reset.png deleted file mode 100644 index 4e992fde37..0000000000 Binary files a/app/data/icons/zoom-reset.png and /dev/null differ diff --git a/app/data/org.pencil2d.Pencil2D.metainfo.xml b/app/data/org.pencil2d.Pencil2D.metainfo.xml index 4debb60d3b..bf7a50781b 100644 --- a/app/data/org.pencil2d.Pencil2D.metainfo.xml +++ b/app/data/org.pencil2d.Pencil2D.metainfo.xml @@ -3,7 +3,9 @@ org.pencil2d.Pencil2D org.pencil2d.Pencil2D.desktop Pencil2D - The Pencil2D Team + + The Pencil2D Team + CC0-1.0 GPL-2.0 2D animation software supporting bitmap and vector graphics diff --git a/app/data/pencil2d.VisualElementsManifest.xml b/app/data/pencil2d.VisualElementsManifest.xml new file mode 100644 index 0000000000..3a00d0349a --- /dev/null +++ b/app/data/pencil2d.VisualElementsManifest.xml @@ -0,0 +1,8 @@ + + + diff --git a/app/data/resources.xml b/app/data/resources.xml new file mode 100644 index 0000000000..f45b34273e --- /dev/null +++ b/app/data/resources.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/app/data/resources/tile150.scale-100.png b/app/data/resources/tile150.scale-100.png new file mode 100644 index 0000000000..14c3ed0fb8 Binary files /dev/null and b/app/data/resources/tile150.scale-100.png differ diff --git a/app/data/resources/tile150.scale-140.png b/app/data/resources/tile150.scale-140.png new file mode 100644 index 0000000000..43814c90bd Binary files /dev/null and b/app/data/resources/tile150.scale-140.png differ diff --git a/app/data/resources/tile150.scale-180.png b/app/data/resources/tile150.scale-180.png new file mode 100644 index 0000000000..724ae3f707 Binary files /dev/null and b/app/data/resources/tile150.scale-180.png differ diff --git a/app/data/resources/tile150.scale-80.png b/app/data/resources/tile150.scale-80.png new file mode 100644 index 0000000000..e94cf43fbb Binary files /dev/null and b/app/data/resources/tile150.scale-80.png differ diff --git a/app/data/resources/tile70.scale-100.png b/app/data/resources/tile70.scale-100.png new file mode 100644 index 0000000000..c2816b92cc Binary files /dev/null and b/app/data/resources/tile70.scale-100.png differ diff --git a/app/data/resources/tile70.scale-140.png b/app/data/resources/tile70.scale-140.png new file mode 100644 index 0000000000..6166fa8383 Binary files /dev/null and b/app/data/resources/tile70.scale-140.png differ diff --git a/app/data/resources/tile70.scale-180.png b/app/data/resources/tile70.scale-180.png new file mode 100644 index 0000000000..e50c2d3206 Binary files /dev/null and b/app/data/resources/tile70.scale-180.png differ diff --git a/app/data/resources/tile70.scale-80.png b/app/data/resources/tile70.scale-80.png new file mode 100644 index 0000000000..ad5a9c2f0b Binary files /dev/null and b/app/data/resources/tile70.scale-80.png differ diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 3baac54fee..3c1076dc75 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -44,6 +44,8 @@ GNU General Public License for more details. #include "soundclip.h" #include "camera.h" +#include "importimageseqdialog.h" +#include "importpositiondialog.h" #include "movieimporter.h" #include "movieexporter.h" #include "filedialog.h" @@ -62,6 +64,50 @@ ActionCommands::ActionCommands(QWidget* parent) : QObject(parent) ActionCommands::~ActionCommands() {} +Status ActionCommands::importAnimatedImage() +{ + ImportImageSeqDialog fileDialog(mParent, ImportExportDialog::Import, FileType::ANIMATED_IMAGE); + fileDialog.exec(); + if (fileDialog.result() != QDialog::Accepted) + { + return Status::CANCELED; + } + int frameSpacing = fileDialog.getSpace(); + QString strImgFileLower = fileDialog.getFilePath(); + + ImportPositionDialog positionDialog(mEditor, mParent); + positionDialog.exec(); + if (positionDialog.result() != QDialog::Accepted) + { + return Status::CANCELED; + } + + // Show a progress dialog, as this could take a while if the gif is huge + QProgressDialog progressDialog(tr("Importing Animated Image..."), tr("Abort"), 0, 100, mParent); + hideQuestionMark(progressDialog); + progressDialog.setWindowModality(Qt::WindowModal); + progressDialog.show(); + + Status st = mEditor->importAnimatedImage(strImgFileLower, frameSpacing, [&progressDialog](int prog) { + progressDialog.setValue(prog); + QApplication::processEvents(); + }, [&progressDialog]() { + return progressDialog.wasCanceled(); + }); + + progressDialog.setValue(100); + progressDialog.close(); + + if (!st.ok()) + { + ErrorDialog errorDialog(st.title(), st.description(), st.details().html()); + errorDialog.exec(); + return Status::SAFE; + } + + return Status::OK; +} + Status ActionCommands::importMovieVideo() { QString filePath = FileDialog::getOpenFileName(mParent, FileType::MOVIE); @@ -103,6 +149,7 @@ Status ActionCommands::importMovieVideo() { ErrorDialog errorDialog(st.title(), st.description(), st.details().html(), mParent); errorDialog.exec(); + return Status::SAFE; } mEditor->layers()->notifyAnimationLengthChanged(); @@ -290,7 +337,7 @@ Status ActionCommands::exportMovie(bool isGif) desc.loop = dialog->getLoop(); desc.alpha = dialog->getTransparency(); - DoubleProgressDialog progressDlg; + DoubleProgressDialog progressDlg(mParent); progressDlg.setWindowModality(Qt::WindowModal); progressDlg.setWindowTitle(tr("Exporting movie")); Qt::WindowFlags eFlags = Qt::Dialog | Qt::WindowTitleHint; @@ -544,14 +591,16 @@ void ActionCommands::ZoomOut() void ActionCommands::rotateClockwise() { - float currentRotation = mEditor->view()->rotation(); - mEditor->view()->rotate(currentRotation + 15.f); + // Rotation direction is inverted if view is flipped either vertically or horizontally + const float delta = mEditor->view()->isFlipHorizontal() == !mEditor->view()->isFlipVertical() ? -15.f : 15.f; + mEditor->view()->rotateRelative(delta); } void ActionCommands::rotateCounterClockwise() { - float currentRotation = mEditor->view()->rotation(); - mEditor->view()->rotate(currentRotation - 15.f); + // Rotation direction is inverted if view is flipped either vertically or horizontally + const float delta = mEditor->view()->isFlipHorizontal() == !mEditor->view()->isFlipVertical() ? 15.f : -15.f; + mEditor->view()->rotateRelative(delta); } void ActionCommands::PlayStop() @@ -795,6 +844,10 @@ void ActionCommands::duplicateKey() KeyFrame* key = layer->getKeyFrameAt(mEditor->currentFrame()); if (key == nullptr) return; + // Duplicating a selected keyframe is not handled properly. + // The desired behavior is to clear selection anyway so we just do that. + deselectAll(); + KeyFrame* dupKey = key->clone(); int nextEmptyFrame = mEditor->currentFrame() + 1; @@ -819,6 +872,7 @@ void ActionCommands::duplicateKey() } mEditor->layers()->notifyAnimationLengthChanged(); + emit mEditor->layers()->currentLayerChanged(mEditor->layers()->currentLayerIndex()); // trigger timeline repaint. } void ActionCommands::moveFrameForward() @@ -940,7 +994,7 @@ void ActionCommands::changeKeyframeLineColor() QRgb color = mEditor->color()->frontColor().rgb(); LayerBitmap* layer = static_cast(mEditor->layers()->currentLayer()); layer->getBitmapImageAtFrame(mEditor->currentFrame())->fillNonAlphaPixels(color); - mEditor->updateFrame(mEditor->currentFrame()); + mEditor->updateFrame(); } } @@ -955,7 +1009,7 @@ void ActionCommands::changeallKeyframeLineColor() if (layer->keyExists(i)) layer->getBitmapImageAtFrame(i)->fillNonAlphaPixels(color); } - mEditor->updateFrame(mEditor->currentFrame()); + mEditor->updateFrame(); } } diff --git a/app/src/actioncommands.h b/app/src/actioncommands.h index 78a423856a..80632717f5 100644 --- a/app/src/actioncommands.h +++ b/app/src/actioncommands.h @@ -37,6 +37,7 @@ class ActionCommands : public QObject void setCore(Editor* e) { mEditor = e; } // file + Status importAnimatedImage(); Status importMovieVideo(); Status importSound(FileType type); Status exportMovie(bool isGif = false); diff --git a/app/src/basedockwidget.cpp b/app/src/basedockwidget.cpp index a99a30c8d8..d052dd1cc1 100644 --- a/app/src/basedockwidget.cpp +++ b/app/src/basedockwidget.cpp @@ -53,7 +53,9 @@ void BaseDockWidget::resizeEvent(QResizeEvent *event) #ifdef __APPLE__ // For some reason the behavior of minimumSize and the margin changes on mac when floating, so we need to do this #else - minHeight += layout()->margin()*2; + int top, bottom; + layout()->getContentsMargins(nullptr, &top, nullptr, &bottom); + minHeight += top + bottom; #endif setMinimumSize(QSize(layout()->minimumSize().width(), minHeight)); } diff --git a/app/src/bucketoptionswidget.cpp b/app/src/bucketoptionswidget.cpp index 9f3feedeff..2e9e89e3e0 100644 --- a/app/src/bucketoptionswidget.cpp +++ b/app/src/bucketoptionswidget.cpp @@ -49,10 +49,6 @@ BucketOptionsWidget::BucketOptionsWidget(Editor* editor, QWidget* parent) : ui->colorToleranceSpinbox->setMaximum(MAX_COLOR_TOLERANCE); ui->strokeThicknessSpinBox->setMinimum(1); - ui->fillToLayerComboBox->addItem(tr("Current layer"), 0); - ui->fillToLayerComboBox->addItem(tr("Layer below"), 1); - ui->fillToLayerComboBox->setToolTip(tr("Fill to the current layer or the layer below")); - ui->referenceLayerComboBox->addItem(tr("Current layer", "Reference Layer Options"), 0); ui->referenceLayerComboBox->addItem(tr("All layers", "Reference Layer Options"), 1); ui->referenceLayerComboBox->setToolTip(tr("Refers to the layer that used to flood fill from")); @@ -76,7 +72,6 @@ BucketOptionsWidget::BucketOptionsWidget(Editor* editor, QWidget* parent) : connect(mEditor->tools(), &ToolManager::toolPropertyChanged, this, &BucketOptionsWidget::onPropertyChanged); connect(mEditor->layers(), &LayerManager::currentLayerChanged, this, &BucketOptionsWidget::onLayerChanged); - connect(ui->fillToLayerComboBox, static_cast(&QComboBox::currentIndexChanged), mEditor->tools(), &ToolManager::setBucketFillToLayerMode); connect(ui->referenceLayerComboBox, static_cast(&QComboBox::currentIndexChanged), mEditor->tools(), &ToolManager::setBucketFillReferenceMode); connect(ui->blendModeComboBox, static_cast(&QComboBox::currentIndexChanged), mEditor->tools(), &ToolManager::setFillMode); @@ -84,7 +79,6 @@ BucketOptionsWidget::BucketOptionsWidget(Editor* editor, QWidget* parent) : ui->expandSpinBox->setValue(settings.value(SETTING_BUCKET_FILL_EXPAND, 2).toInt()); ui->colorToleranceSlider->setValue(settings.value(SETTING_BUCKET_TOLERANCE, 50).toInt()); ui->colorToleranceSpinbox->setValue(settings.value(SETTING_BUCKET_TOLERANCE, 50).toInt()); - ui->fillToLayerComboBox->setCurrentIndex(settings.value(SETTING_BUCKET_FILL_TO_LAYER_MODE, 0).toInt()); ui->referenceLayerComboBox->setCurrentIndex(settings.value(SETTING_BUCKET_FILL_REFERENCE_MODE, 0).toInt()); ui->blendModeComboBox->setCurrentIndex(settings.value(SETTING_FILL_MODE, 0).toInt()); @@ -110,8 +104,6 @@ void BucketOptionsWidget::updatePropertyVisibility() ui->strokeThicknessSlider->show(); ui->strokeThicknessSpinBox->show(); - ui->fillToLayerComboBox->hide(); - ui->fillToDescLabel->hide(); ui->colorToleranceCheckbox->hide(); ui->colorToleranceSlider->hide(); ui->colorToleranceSpinbox->hide(); @@ -127,8 +119,6 @@ void BucketOptionsWidget::updatePropertyVisibility() ui->strokeThicknessSlider->hide(); ui->strokeThicknessSpinBox->hide(); - ui->fillToLayerComboBox->show(); - ui->fillToDescLabel->show(); ui->referenceLayerComboBox->show(); ui->referenceLayerDescLabel->show(); ui->colorToleranceCheckbox->show(); @@ -137,7 +127,6 @@ void BucketOptionsWidget::updatePropertyVisibility() ui->expandCheckbox->show(); ui->expandSlider->show(); ui->expandSpinBox->show(); - disableFillToLayerComboBox(mEditor->tools()->bucketReferenceModeIsCurrentLayer(ui->referenceLayerComboBox->currentIndex())); ui->blendModeComboBox->show(); ui->blendModeLabel->show(); break; @@ -145,8 +134,6 @@ void BucketOptionsWidget::updatePropertyVisibility() default: ui->strokeThicknessSlider->hide(); ui->strokeThicknessSpinBox->hide(); - ui->fillToLayerComboBox->hide(); - ui->fillToDescLabel->hide(); ui->colorToleranceCheckbox->hide(); ui->colorToleranceSlider->hide(); ui->colorToleranceSpinbox->hide(); @@ -175,8 +162,6 @@ void BucketOptionsWidget::onPropertyChanged(ToolType, ToolPropertyType propertyT setFillExpand(static_cast(p.bucketFillExpand)); break; case ToolPropertyType::USEBUCKETFILLEXPAND: setFillExpandEnabled(p.bucketFillExpandEnabled); break; - case ToolPropertyType::BUCKETFILLLAYERMODE: - setFillToLayerMode(p.bucketFillToLayerMode); break; case ToolPropertyType::BUCKETFILLLAYERREFERENCEMODE: setFillReferenceMode(p.bucketFillReferenceMode); break; case ToolPropertyType::FILL_MODE: @@ -227,23 +212,10 @@ void BucketOptionsWidget::setFillExpand(int value) ui->expandSpinBox->setValue(value); } -void BucketOptionsWidget::setFillToLayerMode(int layerMode) -{ - QSignalBlocker b(ui->fillToLayerComboBox); - ui->fillToLayerComboBox->setCurrentIndex(layerMode); -} - void BucketOptionsWidget::setFillReferenceMode(int referenceMode) { QSignalBlocker b(ui->referenceLayerComboBox); ui->referenceLayerComboBox->setCurrentIndex(referenceMode); - disableFillToLayerComboBox(mEditor->tools()->bucketReferenceModeIsCurrentLayer(referenceMode)); -} - -void BucketOptionsWidget::disableFillToLayerComboBox(bool state) -{ - ui->fillToLayerComboBox->setDisabled(state); - ui->fillToDescLabel->setDisabled(state); } void BucketOptionsWidget::setStrokeWidth(qreal value) diff --git a/app/src/bucketoptionswidget.h b/app/src/bucketoptionswidget.h index a53644f33f..c8985e2535 100644 --- a/app/src/bucketoptionswidget.h +++ b/app/src/bucketoptionswidget.h @@ -41,14 +41,12 @@ class BucketOptionsWidget : public QWidget void setFillExpand(int value); void setColorTolerance(int tolerance); void setFillReferenceMode(int referenceMode); - void setFillToLayerMode(int layerMode); void setFillMode(int mode); void onPropertyChanged(ToolType, const ToolPropertyType propertyType); void onLayerChanged(int); private: - void disableFillToLayerComboBox(bool state); void updatePropertyVisibility(); Ui::BucketOptionsWidget *ui; diff --git a/app/src/checkupdatesdialog.cpp b/app/src/checkupdatesdialog.cpp index 0a2dc13567..8723062da2 100644 --- a/app/src/checkupdatesdialog.cpp +++ b/app/src/checkupdatesdialog.cpp @@ -25,6 +25,7 @@ GNU General Public License for more details. #include #include #include +#include #include #include @@ -204,15 +205,15 @@ QString CheckUpdatesDialog::getVersionNumberFromXml(QString xml) while (!xmlReader.atEnd() && !xmlReader.hasError()) { QXmlStreamReader::TokenType tokenType = xmlReader.readNext(); - if (tokenType == QXmlStreamReader::StartElement && xmlReader.name() == "entry") + if (tokenType == QXmlStreamReader::StartElement && xmlReader.name() == QLatin1String("entry")) { while (!xmlReader.atEnd() && !xmlReader.hasError()) { xmlReader.readNext(); - if (xmlReader.name() == "title") + if (xmlReader.name() == QLatin1String("title")) { QString titleTag = xmlReader.readElementText(); - return titleTag.remove(QRegExp("^v")); // remove the leading 'v' + return titleTag.remove(QRegularExpression("^v")); // remove the leading 'v' } } } diff --git a/app/src/colorinspector.cpp b/app/src/colorinspector.cpp index 3196090fae..6b8c72cdfe 100644 --- a/app/src/colorinspector.cpp +++ b/app/src/colorinspector.cpp @@ -67,11 +67,11 @@ void ColorInspector::initUI() ui->hsvAlphaSlider->init(ColorSlider::ColorSpecType::HSV, ColorSlider::ColorType::ALPHA, mCurrentColor, 0.0, 255.0); QPalette p1 = ui->colorWrapper->palette(); - p1.setBrush(QPalette::Background, QBrush(QImage(":/background/checkerboard.png"))); + p1.setBrush(QPalette::Window, QBrush(QImage(":/background/checkerboard.png"))); ui->colorWrapper->setPalette(p1); QPalette p2 = ui->color->palette(); - p2.setColor(QPalette::Background, mCurrentColor); + p2.setColor(QPalette::Window, mCurrentColor); ui->color->setPalette(p2); connect(ui->colorSpecTabWidget, &QTabWidget::currentChanged, this, &ColorInspector::onColorSpecChanged); @@ -180,7 +180,7 @@ void ColorInspector::updateControls() ui->hsvAlphaSpinBox->setValue(qRound(mCurrentColor.alpha() / 2.55)); QPalette p = ui->color->palette(); - p.setColor(QPalette::Background, mCurrentColor); + p.setColor(QPalette::Window, mCurrentColor); ui->color->setPalette(p); update(); diff --git a/app/src/colorpalettewidget.cpp b/app/src/colorpalettewidget.cpp index 7f86fcd602..9038cc9bf2 100644 --- a/app/src/colorpalettewidget.cpp +++ b/app/src/colorpalettewidget.cpp @@ -360,8 +360,7 @@ void ColorPaletteWidget::palettePreferences() // Add to UI - ui->palettePref->setIcon(QIcon(":/app/icons/new/svg/more_options.svg")); - ui->palettePref->setIconSize(QSize(15,15)); + ui->palettePref->setIconSize(QSize(22,22)); ui->palettePref->setArrowType(Qt::ArrowType::NoArrow); ui->palettePref->setStyleSheet(buttonStylesheet); ui->palettePref->addAction(ui->listModeAction); @@ -636,7 +635,7 @@ void ColorPaletteWidget::clickRemoveColorButton() { fitSwatchSize(); } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } bool ColorPaletteWidget::showPaletteWarning() diff --git a/app/src/colorslider.cpp b/app/src/colorslider.cpp index f3b3c24c4d..1f67e05853 100644 --- a/app/src/colorslider.cpp +++ b/app/src/colorslider.cpp @@ -182,7 +182,7 @@ void ColorSlider::drawColorBox(const QColor &color, QSize size) // draw checkerboard background painter.begin(&mBoxPixmapSource); - QBrush brush2(QBrush(QPixmap("://icons/new/checkerboard_smaller.png"))); + QBrush brush2(QBrush(QPixmap(":icons/general/checkerboard_smaller.png"))); painter.setBrush(brush2); QPen pen2; diff --git a/app/src/commandlineexporter.cpp b/app/src/commandlineexporter.cpp index 2b258f4490..e4851a395a 100644 --- a/app/src/commandlineexporter.cpp +++ b/app/src/commandlineexporter.cpp @@ -29,6 +29,12 @@ GNU General Public License for more details. #include "commandlineexporter.h" +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) +const auto qEndl = Qt::endl; +#else +const auto qEndl = endl; +#endif + CommandLineExporter::CommandLineExporter(Editor *editor) : mEditor(editor), mOut(stdout, QIODevice::WriteOnly), @@ -50,16 +56,16 @@ bool CommandLineExporter::process(const QString &inputPath, if(inputPath.isEmpty()) { - mErr << tr("Error: No input file specified. An input project file argument is required when output path(s) are specified.") << endl; + mErr << tr("Error: No input file specified. An input project file argument is required when output path(s) are specified.") << qEndl; return false; } Status s = mEditor->openObject(inputPath, [](int){}, [](int){}); if (!s.ok()) { - mErr << endl << endl << s.title() << endl << endl; - mErr << s.description() << endl << endl; - mErr << s.details().str() << endl << endl; + mErr << qEndl << qEndl << s.title() << qEndl << qEndl; + mErr << s.description() << qEndl << qEndl; + mErr << s.details().str() << qEndl << qEndl; return false; } @@ -69,7 +75,7 @@ bool CommandLineExporter::process(const QString &inputPath, cameraLayer = dynamic_cast(layerManager->findLayerByName(camera, Layer::CAMERA)); if (cameraLayer == nullptr) { - mErr << tr("Warning: the specified camera layer %1 was not found, ignoring.").arg(camera) << endl; + mErr << tr("Warning: the specified camera layer %1 was not found, ignoring.").arg(camera) << qEndl; } } if (cameraLayer == nullptr) @@ -101,7 +107,7 @@ bool CommandLineExporter::process(const QString &inputPath, QString format = detectFormatByFileNameExtension(outputPath); if (format.isNull()) { - mErr << tr("Warning: Output format is not specified or unsupported. Using PNG.", "Command line warning") << endl; + mErr << tr("Warning: Output format is not specified or unsupported. Using PNG.", "Command line warning") << qEndl; format = "PNG"; } @@ -126,10 +132,10 @@ void CommandLineExporter::exportMovie(const QString &outputPath, { if (transparency) { - mErr << tr("Warning: Transparency is not currently supported in movie files", "Command line warning") << endl; + mErr << tr("Warning: Transparency is not currently supported in movie files", "Command line warning") << qEndl; } - mOut << tr("Exporting movie...", "Command line task progress") << endl; + mOut << tr("Exporting movie...", "Command line task progress") << qEndl; ExportMovieDesc desc; desc.strFileName = outputPath; @@ -141,7 +147,7 @@ void CommandLineExporter::exportMovie(const QString &outputPath, MovieExporter ex; ex.run(mEditor->object(), desc, [](float, float){}, [](float){}, [](const QString &){}); - mOut << tr("Done.", "Command line task done") << endl; + mOut << tr("Done.", "Command line task done") << qEndl; } void CommandLineExporter::exportImageSequence(const QString &outputPath, @@ -152,7 +158,7 @@ void CommandLineExporter::exportImageSequence(const QString &outputPath, int endFrame, bool transparency) { - mOut << tr("Exporting image sequence...", "Command line task progress") << endl; + mOut << tr("Exporting image sequence...", "Command line task progress") << qEndl; mEditor->object()->exportFrames(startFrame, endFrame, cameraLayer, @@ -165,5 +171,5 @@ void CommandLineExporter::exportImageSequence(const QString &outputPath, true, nullptr, 0); - mOut << tr("Done.", "Command line task done") << endl; + mOut << tr("Done.", "Command line task done") << qEndl; } diff --git a/app/src/commandlineparser.cpp b/app/src/commandlineparser.cpp index dc8e8e0dd0..3685da12c9 100644 --- a/app/src/commandlineparser.cpp +++ b/app/src/commandlineparser.cpp @@ -19,6 +19,12 @@ GNU General Public License for more details. #include +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) +const auto qEndl = Qt::endl; +#else +const auto qEndl = endl; +#endif + CommandLineParser::CommandLineParser() : mParser(), mInputPath(), mOutputPaths(), mCamera() { mParser.setApplicationDescription(tr("Pencil2D is an animation/drawing software for Mac OS X, Windows, and Linux. " @@ -96,7 +102,7 @@ void CommandLineParser::process(QStringList arguments) mWidth = mParser.value("width").toInt(&ok); if (!ok) { - err << tr("Warning: width value %1 is not an integer, ignoring.").arg(mParser.value("width")) << endl; + err << tr("Warning: width value %1 is not an integer, ignoring.").arg(mParser.value("width")) << qEndl; mWidth = -1; } } @@ -107,7 +113,7 @@ void CommandLineParser::process(QStringList arguments) mHeight = mParser.value("height").toInt(&ok); if (!ok) { - err << tr("Warning: height value %1 is not an integer, ignoring.").arg(mParser.value("height")) << endl; + err << tr("Warning: height value %1 is not an integer, ignoring.").arg(mParser.value("height")) << qEndl; mHeight = -1; } } @@ -118,12 +124,12 @@ void CommandLineParser::process(QStringList arguments) mStartFrame = mParser.value("start").toInt(&ok); if (!ok) { - err << tr("Warning: start value %1 is not an integer, ignoring.").arg(mParser.value("start")) << endl; + err << tr("Warning: start value %1 is not an integer, ignoring.").arg(mParser.value("start")) << qEndl; mStartFrame = 1; } if (mStartFrame < 1) { - err << tr("Warning: start value must be at least 1, ignoring.") << endl; + err << tr("Warning: start value must be at least 1, ignoring.") << qEndl; mStartFrame = 1; } } @@ -144,13 +150,13 @@ void CommandLineParser::process(QStringList arguments) mEndFrame = mParser.value("end").toInt(&ok); if (!ok) { - err << tr("Warning: end value %1 is not an integer, last or last-sound, ignoring.").arg(mParser.value("end")) << endl; + err << tr("Warning: end value %1 is not an integer, last or last-sound, ignoring.").arg(mParser.value("end")) << qEndl; mEndFrame = -1; } } if (mEndFrame > -1 && mEndFrame < mStartFrame) { - err << tr("Warning: end value %1 is smaller than start value %2, ignoring.").arg(mEndFrame).arg(mStartFrame) << endl; + err << tr("Warning: end value %1 is smaller than start value %2, ignoring.").arg(mEndFrame).arg(mStartFrame) << qEndl; mEndFrame = mStartFrame; } } diff --git a/app/src/doubleprogressdialog.cpp b/app/src/doubleprogressdialog.cpp index 7d171a01b5..22c2dc3907 100644 --- a/app/src/doubleprogressdialog.cpp +++ b/app/src/doubleprogressdialog.cpp @@ -21,7 +21,7 @@ GNU General Public License for more details. #include DoubleProgressDialog::DoubleProgressDialog(QWidget *parent) : - QDialog(parent), + QProgressDialog(parent), ui(new Ui::DoubleProgressDialog) { ui->setupUi(this); @@ -29,7 +29,7 @@ DoubleProgressDialog::DoubleProgressDialog(QWidget *parent) : major = new ProgressBarControl(ui->majorProgressBar); minor = new ProgressBarControl(ui->minorProgressBar); - connect(ui->cancelButton, &QPushButton::pressed, this, &DoubleProgressDialog::canceled); + setCancelButton(ui->cancelButton); } DoubleProgressDialog::~DoubleProgressDialog() diff --git a/app/src/doubleprogressdialog.h b/app/src/doubleprogressdialog.h index 8dda8301ff..93bfb67abf 100644 --- a/app/src/doubleprogressdialog.h +++ b/app/src/doubleprogressdialog.h @@ -18,14 +18,14 @@ GNU General Public License for more details. #ifndef DOUBLEPROGRESSDIALOG_H #define DOUBLEPROGRESSDIALOG_H -#include +#include #include namespace Ui { class DoubleProgressDialog; } -class DoubleProgressDialog : public QDialog +class DoubleProgressDialog : public QProgressDialog { Q_OBJECT @@ -63,9 +63,6 @@ class DoubleProgressDialog : public QDialog ProgressBarControl *major, *minor; -signals: - void canceled(); - private: Ui::DoubleProgressDialog *ui; }; diff --git a/app/src/filedialog.cpp b/app/src/filedialog.cpp index 8b35d69ca2..42ee65db25 100644 --- a/app/src/filedialog.cpp +++ b/app/src/filedialog.cpp @@ -119,6 +119,7 @@ QString FileDialog::getDefaultExtensionByFileType(const FileType fileType) case FileType::IMAGE: return PFF_DEFAULT_IMAGE_EXT; case FileType::IMAGE_SEQUENCE: return PFF_DEFAULT_IMAGE_SEQ_EXT; case FileType::GIF: return PFF_DEFAULT_ANIMATED_EXT; + case FileType::ANIMATED_IMAGE: return PFF_DEFAULT_ANIMATED_EXT; case FileType::PALETTE: return PFF_DEFAULT_PALETTE_EXT; case FileType::MOVIE: return PFF_DEFAULT_MOVIE_EXT; case FileType::SOUND: return PFF_DEFAULT_SOUND_EXT; @@ -167,6 +168,7 @@ QString FileDialog::openDialogCaption(FileType fileType) case FileType::IMAGE: return tr("Import image"); case FileType::IMAGE_SEQUENCE: return tr("Import image sequence"); case FileType::GIF: return tr("Import Animated GIF"); + case FileType::ANIMATED_IMAGE: return tr("Import animated image"); case FileType::MOVIE: return tr("Import movie"); case FileType::SOUND: return tr("Import sound"); case FileType::PALETTE: return tr("Open palette"); @@ -182,6 +184,7 @@ QString FileDialog::saveDialogCaption(FileType fileType) case FileType::IMAGE: return tr("Export image"); case FileType::IMAGE_SEQUENCE: return tr("Export image sequence"); case FileType::GIF: return tr("Export Animated GIF"); + case FileType::ANIMATED_IMAGE: return tr("Export animated image"); case FileType::MOVIE: return tr("Export movie"); case FileType::SOUND: return tr("Export sound"); case FileType::PALETTE: return tr("Export palette"); @@ -197,6 +200,7 @@ QString FileDialog::openFileFilters(FileType fileType) case FileType::IMAGE: return PFF_IMAGE_FILTER; case FileType::IMAGE_SEQUENCE: return PFF_IMAGE_SEQ_FILTER; case FileType::GIF: return PFF_GIF_EXT_FILTER; + case FileType::ANIMATED_IMAGE: return PFF_ANIMATED_IMAGE_EXT_FILTER; case FileType::MOVIE: return PFF_MOVIE_EXT; case FileType::SOUND: return PFF_SOUND_EXT_FILTER; case FileType::PALETTE: return PFF_PALETTE_EXT_FILTER; @@ -212,6 +216,7 @@ QString FileDialog::saveFileFilters(FileType fileType) case FileType::IMAGE: return ""; case FileType::IMAGE_SEQUENCE: return ""; case FileType::GIF: return QString("%1 (*.gif)").arg(tr("Animated GIF")); + case FileType::ANIMATED_IMAGE: return ""; case FileType::MOVIE: return "MP4 (*.mp4);; AVI (*.avi);; WebM (*.webm);; APNG (*.apng)"; case FileType::SOUND: return ""; case FileType::PALETTE: return PFF_PALETTE_EXT_FILTER; @@ -286,6 +291,7 @@ QString FileDialog::toSettingKey(FileType fileType) case FileType::IMAGE: return "Image"; case FileType::IMAGE_SEQUENCE: return "ImageSequence"; case FileType::GIF: return "Animated GIF"; + case FileType::ANIMATED_IMAGE: return "Animated Image"; case FileType::MOVIE: return "Movie"; case FileType::SOUND: return "Sound"; case FileType::PALETTE: return "Palette"; diff --git a/app/src/generalpage.cpp b/app/src/generalpage.cpp index 66288a7b27..ff0406835a 100644 --- a/app/src/generalpage.cpp +++ b/app/src/generalpage.cpp @@ -92,7 +92,7 @@ GeneralPage::GeneralPage() : ui(new Ui::GeneralPage) ui->backgroundButtons->setId(ui->dotsBackgroundButton, 4); ui->backgroundButtons->setId(ui->weaveBackgroundButton, 5); - auto buttonClicked = static_cast(&QButtonGroup::buttonClicked); + auto buttonClicked = static_cast(&QButtonGroup::buttonClicked); auto curIndexChanged = static_cast(&QComboBox::currentIndexChanged); auto spinValueChanged = static_cast(&QSpinBox::valueChanged); connect(ui->languageCombo, curIndexChanged, this, &GeneralPage::languageChanged); @@ -113,6 +113,7 @@ GeneralPage::GeneralPage() : ui(new Ui::GeneralPage) connect(ui->safeHelperTextCheckbox, &QCheckBox::stateChanged, this, &GeneralPage::SafeAreaHelperTextCheckBoxStateChanged); connect(ui->gridCheckBox, &QCheckBox::stateChanged, this, &GeneralPage::gridCheckBoxStateChanged); connect(ui->framePoolSizeSpin, spinValueChanged, this, &GeneralPage::frameCacheNumberChanged); + connect(ui->invertScrollDirectionBox, &QCheckBox::stateChanged, this, &GeneralPage::invertScrollDirectionBoxStateChanged); } GeneralPage::~GeneralPage() @@ -181,6 +182,8 @@ void GeneralPage::updateValues() else Q_ASSERT(false); ui->backgroundButtons->button(buttonIdx)->setChecked(true); + + ui->invertScrollDirectionBox->setChecked(mManager->isOn(SETTING::INVERT_SCROLL_ZOOM_DIRECTION)); } void GeneralPage::languageChanged(int i) @@ -193,19 +196,15 @@ void GeneralPage::languageChanged(int i) tr("The language change will take effect after a restart of Pencil2D")); } -void GeneralPage::backgroundChanged(int value) +void GeneralPage::backgroundChanged(QAbstractButton* button) { QString brushName = "white"; - switch (value) - { - case 1: brushName = "checkerboard"; break; - case 2: brushName = "white"; break; - case 3: brushName = "grey"; break; - case 4: brushName = "dots"; break; - case 5: brushName = "weave"; break; - default: - break; - } + if (button == ui->checkerBackgroundButton) brushName = "checkerboard"; + else if (button == ui->whiteBackgroundButton) brushName = "white"; + else if (button == ui->greyBackgroundButton) brushName = "grey"; + else if (button == ui->dotsBackgroundButton) brushName = "dots"; + else if (button == ui->weaveBackgroundButton) brushName = "weave"; + else Q_UNREACHABLE(); mManager->set(SETTING::BACKGROUND_STYLE, brushName); } @@ -296,3 +295,8 @@ void GeneralPage::frameCacheNumberChanged(int value) { mManager->set(SETTING::FRAME_POOL_SIZE, value); } + +void GeneralPage::invertScrollDirectionBoxStateChanged(int b) +{ + mManager->set(SETTING::INVERT_SCROLL_ZOOM_DIRECTION, b != Qt::Unchecked); +} diff --git a/app/src/generalpage.h b/app/src/generalpage.h index d36b86e493..239e6b8b9f 100644 --- a/app/src/generalpage.h +++ b/app/src/generalpage.h @@ -18,6 +18,7 @@ GNU General Public License for more details. #ifndef GENERALPAGE_H #define GENERALPAGE_H +class QAbstractButton; class PreferenceManager; namespace Ui { @@ -54,8 +55,9 @@ private slots: void highResCheckboxStateChanged(int b); void gridCheckBoxStateChanged(int b); void curveSmoothingChanged(int value); - void backgroundChanged(int value); + void backgroundChanged(QAbstractButton* button); void frameCacheNumberChanged(int value); + void invertScrollDirectionBoxStateChanged(int b); private: diff --git a/app/src/importimageseqdialog.cpp b/app/src/importimageseqdialog.cpp index a4ac70378b..86c4515913 100644 --- a/app/src/importimageseqdialog.cpp +++ b/app/src/importimageseqdialog.cpp @@ -58,10 +58,16 @@ void ImportImageSeqDialog::setupLayout() hideInstructionsLabel(true); - if (mFileType == FileType::GIF) { + switch (mFileType) + { + case FileType::GIF: setWindowTitle(tr("Import Animated GIF")); - } else { + break; + case FileType::IMAGE_SEQUENCE: setWindowTitle(tr("Import image sequence")); + break; + default: + setWindowTitle(tr("Import animated image")); } connect(uiOptionsBox->spaceSpinBox, static_cast(&QSpinBox::valueChanged), this, &ImportImageSeqDialog::setSpace); @@ -364,7 +370,7 @@ Status ImportImageSeqDialog::validateFiles(const QStringList &filepaths) for (int i = 0; i < filepaths.count(); i++) { - QFileInfo file = filepaths.at(i); + QFileInfo file(filepaths.at(i)); if (!file.exists()) failedPathsString += filepaths.at(i) + "\n"; } diff --git a/app/src/importlayersdialog.cpp b/app/src/importlayersdialog.cpp index f303072f08..35ae3f71bc 100644 --- a/app/src/importlayersdialog.cpp +++ b/app/src/importlayersdialog.cpp @@ -27,6 +27,7 @@ GNU General Public License for more details. #include "soundmanager.h" #include "layer.h" #include "layersound.h" +#include "layervector.h" #include "soundclip.h" @@ -83,29 +84,43 @@ void ImportLayersDialog::importLayers() int currentFrame = mEditor->currentFrame(); Q_ASSERT(ui->lwLayers->count() == mImportObject->getLayerCount()); - for (int i = 0; i < ui->lwLayers->count(); i++ ) + QMap importedColors; + + for (const QListWidgetItem* item : ui->lwLayers->selectedItems()) { - QListWidgetItem* item = ui->lwLayers->item(i); - if (item->isSelected()) - { - int layerId = item->data(Qt::UserRole).toInt(); + mImportLayer = mImportObject->takeLayer(item->data(Qt::UserRole).toInt()); + mImportLayer->setName(mEditor->layers()->nameSuggestLayer(item->text())); + loadKeyFrames(mImportLayer); // all keyframes of this layer must be in memory - mImportLayer = mImportObject->takeLayer(layerId); - mImportLayer->setName(mEditor->layers()->nameSuggestLayer(item->text())); - loadKeyFrames(mImportLayer); // all keyframes of this layer must be in memory + object->addLayer(mImportLayer); - object->addLayer(mImportLayer); + if (mImportLayer->type() == Layer::VECTOR) + { + LayerVector* layerVector = static_cast(mImportLayer); + for (int i = 0; i < mImportObject->getColorCount(); i++) { + if (!layerVector->usesColor(i)) { + continue; + } + + if (!importedColors.contains(i)) { + const ColorRef color = mImportObject->getColor(i); + object->addColor(color); + importedColors[i] = object->getColorCount() - 1; + } + + layerVector->moveColor(i, importedColors[i]); + } + } - if (mImportLayer->type() == Layer::SOUND) + if (mImportLayer->type() == Layer::SOUND) + { + LayerSound* layerSound = static_cast(mImportLayer); + layerSound->foreachKeyFrame([this](KeyFrame* key) { - LayerSound* layerSound = static_cast(mImportLayer); - layerSound->foreachKeyFrame([this](KeyFrame* key) - { - SoundClip* clip = dynamic_cast(key); - Status st = mEditor->sound()->loadSound(clip, clip->fileName()); - Q_ASSERT(st.ok()); - }); - } + SoundClip* clip = dynamic_cast(key); + Status st = mEditor->sound()->loadSound(clip, clip->fileName()); + Q_ASSERT(st.ok()); + }); } } mEditor->object()->modification(); diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index 99fe070208..7a80541ebf 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -20,6 +20,7 @@ GNU General Public License for more details. #include "ui_mainwindow2.h" // Qt headers +#include #include #include #include @@ -185,7 +186,9 @@ void MainWindow2::createDockWidgets() for (BaseDockWidget* pWidget : mDockWidgets) { pWidget->setAllowedAreas(Qt::AllDockWidgetAreas); - pWidget->setFeatures(QDockWidget::AllDockWidgetFeatures); + pWidget->setFeatures(QDockWidget::DockWidgetFeature::DockWidgetClosable | + QDockWidget::DockWidgetFeature::DockWidgetMovable | + QDockWidget::DockWidgetFeature::DockWidgetFloatable); pWidget->setFocusPolicy(Qt::NoFocus); pWidget->setEditor(mEditor); @@ -251,7 +254,7 @@ void MainWindow2::createMenus() connect(ui->actionImport_ImageSeqNum, &QAction::triggered, this, &MainWindow2::importPredefinedImageSet); connect(ui->actionImportLayers_from_pclx, &QAction::triggered, this, &MainWindow2::importLayers); connect(ui->actionImport_MovieVideo, &QAction::triggered, this, &MainWindow2::importMovieVideo); - connect(ui->actionImport_Gif, &QAction::triggered, this, &MainWindow2::importGIF); + connect(ui->actionImport_AnimatedImage, &QAction::triggered, this, &MainWindow2::importAnimatedImage); connect(ui->actionImport_Sound, &QAction::triggered, [=] { mCommands->importSound(FileType::SOUND); }); connect(ui->actionImport_MovieAudio, &QAction::triggered, [=] { mCommands->importSound(FileType::MOVIE); }); @@ -877,7 +880,7 @@ void MainWindow2::importImage() return; } - ui->scribbleArea->updateCurrentFrame(); + ui->scribbleArea->updateFrame(); mTimeLine->updateContent(); } @@ -947,64 +950,23 @@ void MainWindow2::importLayers() importLayers->open(); } -void MainWindow2::importGIF() +void MainWindow2::importAnimatedImage() { - auto gifDialog = new ImportImageSeqDialog(this, ImportExportDialog::Import, FileType::GIF); - gifDialog->exec(); - if (gifDialog->result() == QDialog::Rejected) - { - return; - } - // Flag this so we don't prompt the user about auto-save in the middle of the import. mSuppressAutoSaveDialog = true; - ImportPositionDialog* positionDialog = new ImportPositionDialog(mEditor, this); - OnScopeExit(delete positionDialog) - - positionDialog->exec(); - if (positionDialog->result() != QDialog::Accepted) - { - return; - } - - int space = gifDialog->getSpace(); - - // Show a progress dialog, as this could take a while if the gif is huge - QProgressDialog progress(tr("Importing Animated GIF..."), tr("Abort"), 0, 100, this); - hideQuestionMark(progress); - progress.setWindowModality(Qt::WindowModal); - progress.show(); - - QString strImgFileLower = gifDialog->getFilePath(); - if (!strImgFileLower.toLower().endsWith(".gif")) - { - ErrorDialog errorDialog(tr("Import failed"), tr("You can only import files ending with .gif.")); - errorDialog.exec(); - } - else - { - Status st = mEditor->importGIF(strImgFileLower, space); - - progress.setValue(50); - QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); // Required to make progress bar update - - progress.setValue(100); - progress.close(); - - if (!st.ok()) - { - ErrorDialog errorDialog(st.title(), st.description(), st.details().html()); - errorDialog.exec(); - } - } + mCommands->importAnimatedImage(); mSuppressAutoSaveDialog = false; } void MainWindow2::lockWidgets(bool shouldLock) { - QDockWidget::DockWidgetFeatures feat = shouldLock ? QDockWidget::NoDockWidgetFeatures : QDockWidget::AllDockWidgetFeatures; + QDockWidget::DockWidgetFeatures feat = shouldLock + ? QDockWidget::NoDockWidgetFeatures + : (QDockWidget::DockWidgetFeature::DockWidgetClosable | + QDockWidget::DockWidgetFeature::DockWidgetMovable | + QDockWidget::DockWidgetFeature::DockWidgetFloatable); for (QDockWidget* d : mDockWidgets) { @@ -1443,7 +1405,7 @@ void MainWindow2::makeConnections(Editor* editor, ColorInspector* colorInspector void MainWindow2::makeConnections(Editor* editor, ScribbleArea* scribbleArea) { - connect(editor->tools(), &ToolManager::toolChanged, scribbleArea, &ScribbleArea::setCurrentTool); + connect(editor->tools(), &ToolManager::toolChanged, scribbleArea, &ScribbleArea::updateToolCursor); connect(editor->tools(), &ToolManager::toolChanged, mToolBox, &ToolBoxWidget::onToolSetActive); connect(editor->tools(), &ToolManager::toolPropertyChanged, scribbleArea, &ScribbleArea::updateToolCursor); @@ -1691,6 +1653,9 @@ void MainWindow2::createToolbars() mOverlayToolbar->addAction(ui->actionTwoPointPerspective); mOverlayToolbar->addAction(ui->actionThreePointPerspective); mOverlayToolbar->setIconSize(QSize(22,22)); + mViewToolbar->setIconSize(QSize(22,22)); + mMainToolbar->setIconSize(QSize(22,22)); + QToolButton* perspectiveLinesAngleButton = new QToolButton(this); perspectiveLinesAngleButton->setDefaultAction(ui->menuPerspectiveLinesAngle->menuAction()); perspectiveLinesAngleButton->setPopupMode(QToolButton::InstantPopup); diff --git a/app/src/mainwindow2.h b/app/src/mainwindow2.h index a9f9f7a0cb..9f1b683f6c 100644 --- a/app/src/mainwindow2.h +++ b/app/src/mainwindow2.h @@ -90,7 +90,7 @@ public slots: void importPredefinedImageSet(); void importLayers(); void importMovieVideo(); - void importGIF(); + void importAnimatedImage(); void lockWidgets(bool shouldLock); diff --git a/app/src/onionskinwidget.cpp b/app/src/onionskinwidget.cpp index f4872aa3d3..8b7b8cac1c 100644 --- a/app/src/onionskinwidget.cpp +++ b/app/src/onionskinwidget.cpp @@ -57,9 +57,23 @@ void OnionSkinWidget::initUI() ui->opacityGroup->setLayout(opacityLayout); #ifdef __APPLE__ + + ui->scrollAreaWidgetContents->layout()->setSpacing(8); + // Mac only style. ToolButtons are naturally borderless on Win/Linux. QString stylesheet = "QToolButton { border: 0px; } " + "QGroupBox::title {" + "subcontrol-origin: padding;" + "left: 6px;" + "padding: 2px 2px 0px 0px;" + "background: transparent;" + "}" + "QGroupBox {" + "subcontrol-origin: margin;" + "margin-top: 8px;" + "padding-top: 16px;" + "}" "QToolButton:pressed{ border: 1px solid #FFADAD; border-radius: 2px; background-color: #D5D5D5; }" "QToolButton:checked{ border: 1px solid #ADADAD; border-radius: 2px; background-color: #D5D5D5; }"; setStyleSheet(this->styleSheet().append(stylesheet)); @@ -74,8 +88,8 @@ void OnionSkinWidget::makeConnections() connect(ui->onionPrevFramesNumBox, spinBoxChanged, this, &OnionSkinWidget::onionPrevFramesNumChange); connect(ui->onionNextFramesNumBox, spinBoxChanged, this, &OnionSkinWidget::onionNextFramesNumChange); - connect(ui->onionPrevButton, &QToolButton::clicked, this, &OnionSkinWidget::onionPrevButtonClicked); - connect(ui->onionNextButton, &QToolButton::clicked, this, &OnionSkinWidget::onionNextButtonClicked); + connect(ui->prevFramesGroup, &QGroupBox::clicked, this, &OnionSkinWidget::prevFramesGroupClicked); + connect(ui->nextFramesGroup, &QGroupBox::clicked, this, &OnionSkinWidget::nextFramesGroupClicked); connect(ui->onionBlueButton, &QToolButton::clicked, this, &OnionSkinWidget::onionBlueButtonClicked); connect(ui->onionRedButton, &QToolButton::clicked, this, &OnionSkinWidget::onionRedButtonClicked); @@ -91,18 +105,12 @@ void OnionSkinWidget::updateUI() { PreferenceManager* prefs = editor()->preference(); - QSignalBlocker b1(ui->onionPrevButton); - ui->onionPrevButton->setChecked(prefs->isOn(SETTING::PREV_ONION)); - - QSignalBlocker b2(ui->onionNextButton); - ui->onionNextButton->setChecked(prefs->isOn(SETTING::NEXT_ONION)); + ui->prevFramesGroup->setChecked(prefs->isOn(SETTING::PREV_ONION)); + ui->nextFramesGroup->setChecked(prefs->isOn(SETTING::NEXT_ONION)); QSignalBlocker b3(ui->onionBlueButton); ui->onionBlueButton->setChecked(prefs->isOn(SETTING::ONION_BLUE)); - ui->onionRedButton->setEnabled(ui->onionPrevButton->isChecked()); - ui->onionBlueButton->setEnabled(ui->onionNextButton->isChecked()); - QSignalBlocker b4(ui->onionRedButton); ui->onionRedButton->setChecked(prefs->isOn(SETTING::ONION_RED)); @@ -119,13 +127,13 @@ void OnionSkinWidget::updateUI() } -void OnionSkinWidget::onionPrevButtonClicked(bool isOn) +void OnionSkinWidget::prevFramesGroupClicked(bool isOn) { PreferenceManager* prefs = editor()->preference(); prefs->set(SETTING::PREV_ONION, isOn); } -void OnionSkinWidget::onionNextButtonClicked(bool isOn) +void OnionSkinWidget::nextFramesGroupClicked(bool isOn) { PreferenceManager* prefs = editor()->preference(); prefs->set(SETTING::NEXT_ONION, isOn); diff --git a/app/src/onionskinwidget.h b/app/src/onionskinwidget.h index 0bfcf64ef7..711f249702 100644 --- a/app/src/onionskinwidget.h +++ b/app/src/onionskinwidget.h @@ -41,8 +41,8 @@ class OnionSkinWidget : public BaseDockWidget private slots: void playbackStateChanged(int); - void onionPrevButtonClicked(bool); - void onionNextButtonClicked(bool); + void prevFramesGroupClicked(bool); + void nextFramesGroupClicked(bool); void onionBlueButtonClicked(bool); void onionRedButtonClicked(bool); void onionMaxOpacityChange(int); diff --git a/app/src/pencil2d.cpp b/app/src/pencil2d.cpp index 3c52edf809..d7c40e9dde 100644 --- a/app/src/pencil2d.cpp +++ b/app/src/pencil2d.cpp @@ -150,7 +150,11 @@ void Pencil2D::installTranslators() #endif std::unique_ptr qtTranslator(new QTranslator(this)); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (qtTranslator->load(locale, "qt", "_", QLibraryInfo::path(QLibraryInfo::TranslationsPath))) +#else if (qtTranslator->load(locale, "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath))) +#endif { installTranslator(qtTranslator.release()); } diff --git a/app/src/shortcutspage.cpp b/app/src/shortcutspage.cpp index 0eab0dc7b7..3cc3e47980 100644 --- a/app/src/shortcutspage.cpp +++ b/app/src/shortcutspage.cpp @@ -18,7 +18,6 @@ GNU General Public License for more details. #include "ui_shortcutspage.h" #include -#include #include #include #include diff --git a/app/src/spinslider.cpp b/app/src/spinslider.cpp index b7efb602c6..a9bf226277 100644 --- a/app/src/spinslider.cpp +++ b/app/src/spinslider.cpp @@ -51,7 +51,7 @@ void SpinSlider::init(QString text, GROWTH_TYPE type, VALUE_TYPE dataType, qreal mSlider->setMaximumWidth(500); QGridLayout* layout = new QGridLayout(); - layout->setMargin(2); + layout->setContentsMargins(2, 2, 2, 2); layout->setSpacing(2); layout->addWidget(mLabel, 0, 0, 1, 1); diff --git a/app/src/statusbar.cpp b/app/src/statusbar.cpp index 73ace9bd0d..904c455062 100644 --- a/app/src/statusbar.cpp +++ b/app/src/statusbar.cpp @@ -39,7 +39,7 @@ StatusBar::StatusBar(QWidget *parent) : QStatusBar(parent) addWidget(mToolLabel, 1); mModifiedLabel = new QLabel(this); - mModifiedLabel->setPixmap(QPixmap(":/icons/save.png")); + mModifiedLabel->setPixmap(QPixmap(":/icons/themes/playful/menubar/save.svg")); updateModifiedStatus(false); addPermanentWidget(mModifiedLabel); @@ -62,7 +62,11 @@ StatusBar::StatusBar(QWidget *parent) : QStatusBar(parent) mZoomBox->setMaxCount(mZoomBox->count() + 1); mZoomBox->setEditable(true); mZoomBox->lineEdit()->setAlignment(Qt::AlignRight); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + connect(mZoomBox, &QComboBox::textActivated, [=](const QString ¤tText) +#else connect(mZoomBox, static_cast(&QComboBox::activated), [=](const QString ¤tText) +#endif { if (mZoomBox->count() == mZoomBox->maxCount()) { @@ -138,18 +142,18 @@ void StatusBar::updateToolStatus(ToolType tool) } static QPixmap toolIcons[TOOL_TYPE_COUNT]{ - {":icons/new/svg/pencil_detailed.svg"}, - {":icons/new/svg/eraser_detailed.svg"}, - {":icons/new/svg/selection.svg"}, - {":icons/new/svg/arrow.svg"}, - {":icons/new/svg/hand_detailed.svg"}, - {":icons/new/svg/smudge_detailed.svg"}, - {":icons/new/svg/arrow.svg"}, - {":icons/new/svg/pen_detailed.svg"}, - {":icons/new/svg/line.svg"}, - {":icons/new/svg/bucket_detailed.svg"}, - {":icons/new/svg/eyedropper_detailed.svg"}, - {":icons/new/svg/brush_detailed.svg"} + {":icons/themes/playful/tools/tool-pencil.svg"}, + {":icons/themes/playful/tools/tool-eraser.svg"}, + {":icons/themes/playful/tools/tool-select.svg"}, + {":icons/themes/playful/tools/tool-move.svg"}, + {":icons/themes/playful/tools/tool-hand.svg"}, + {":icons/themes/playful/tools/tool-smudge.svg"}, + {""}, // Camera tool does not have an icon + {":icons/themes/playful/tools/tool-pen.svg"}, + {":icons/themes/playful/tools/tool-polyline.svg"}, + {":icons/themes/playful/tools/tool-bucket.svg"}, + {":icons/themes/playful/tools/tool-eyedropper.svg"}, + {":icons/themes/playful/tools/tool-brush.svg"} }; mToolIcon->setPixmap(toolIcons[tool]); mToolIcon->setToolTip(BaseTool::TypeName(tool)); diff --git a/app/src/timecontrols.cpp b/app/src/timecontrols.cpp index 8a224c3ace..2dc7748f15 100644 --- a/app/src/timecontrols.cpp +++ b/app/src/timecontrols.cpp @@ -50,12 +50,17 @@ void TimeControls::initUI() mFps = mFpsBox->value(); mTimecodeSelect = new QToolButton(this); - mTimecodeSelect->setIcon(QIcon(":app/icons/new/svg/more_options.svg")); + + QMenu* timeSelectMenu = new QMenu(tr("Display timecode", "Timeline menu for choose a timecode"), this); + mTimecodeSelect->setIcon(QIcon(":/icons/themes/playful/misc/more-options.svg")); + + timeSelectMenu->addAction(mNoTimecodeAction = new QAction(tr("No text"), this)); + timeSelectMenu->addAction(mOnlyFramesAction = new QAction(tr("Frames"), this)); + timeSelectMenu->addAction(mSmpteAction = new QAction(tr("SMPTE Timecode"), this)); + timeSelectMenu->addAction(mSffAction = new QAction(tr("SFF Timecode"), this)); + mTimecodeSelect->setMenu(timeSelectMenu); mTimecodeSelect->setPopupMode(QToolButton::InstantPopup); - mTimecodeSelect->addAction(mNoTimecodeAction = new QAction(tr("No text"), this)); - mTimecodeSelect->addAction(mOnlyFramesAction = new QAction(tr("Frames"), this)); - mTimecodeSelect->addAction(mSmpteAction = new QAction(tr("SMPTE Timecode"), this)); - mTimecodeSelect->addAction(mSffAction = new QAction(tr("SFF Timecode"), this)); + mTimecodeSelect->setStyleSheet("::menu-indicator{ image: none; }"); mTimecodeLabelEnum = mEditor->preference()->getInt(SETTING::TIMECODE_TEXT); mTimecodeLabel = new QLabel(this); mTimecodeLabel->setContentsMargins(2, 0, 0, 0); @@ -100,23 +105,25 @@ void TimeControls::initUI() mPlaybackRangeCheckBox->setToolTip(tr("Playback range")); mPlayButton = new QPushButton(this); + mPlayButton->setIconSize(QSize(22,22)); mLoopButton = new QPushButton(this); + mLoopButton->setIconSize(QSize(22,22)); mSoundButton = new QPushButton(this); + mSoundButton->setIconSize(QSize(22,22)); mSoundScrubButton = new QPushButton(this); + mSoundScrubButton->setIconSize(QSize(22,22)); mJumpToEndButton = new QPushButton(this); + mJumpToEndButton->setIconSize(QSize(22,22)); mJumpToStartButton = new QPushButton(this); - - mLoopIcon = QIcon(":icons/controls/loop.png"); - mSoundIcon = QIcon(); - mSoundIcon.addFile(":icons/controls/sound.png", QSize(), QIcon::Normal, QIcon::On); - mSoundIcon.addFile(":icons/controls/sound-disabled.png", QSize(), QIcon::Normal, QIcon::Off); - mSoundScrubIcon = QIcon(); - mSoundScrubIcon.addFile(":icons/controls/soundscrub.png", QSize(), QIcon::Normal, QIcon::On); - mSoundScrubIcon.addFile(":icons/controls/soundscrub-disabled.png", QSize(), QIcon::Normal, QIcon::Off); - mJumpToEndIcon = QIcon(":icons/controls/endplay.png"); - mJumpToStartIcon = QIcon(":icons/controls/startplay.png"); - mStartIcon = QIcon(":icons/controls/play.png"); - mStopIcon = QIcon(":icons/controls/stop.png"); + mJumpToStartButton->setIconSize(QSize(22,22)); + + mLoopIcon = QIcon(":icons/themes/playful/controls/control-loop.svg"); + mSoundIcon = QIcon(":icons/themes/playful/controls/control-sound-enable.svg"); + mSoundScrubIcon = QIcon(":icons/themes/playful/controls/control-sound-scrub.svg"); + mJumpToEndIcon = QIcon(":icons/themes/playful/controls/control-play-end.svg"); + mJumpToStartIcon = QIcon(":icons/themes/playful/controls/control-play-start.svg"); + mStartIcon = QIcon(":icons/themes/playful/controls/control-play.svg"); + mStopIcon = QIcon(":icons/themes/playful/controls/control-stop.svg"); mPlayButton->setIcon(mStartIcon); mLoopButton->setIcon(mLoopIcon); mSoundButton->setIcon(mSoundIcon); diff --git a/app/src/timeline.cpp b/app/src/timeline.cpp index 77baecc4d5..dcb26b28f1 100644 --- a/app/src/timeline.cpp +++ b/app/src/timeline.cpp @@ -67,23 +67,21 @@ void TimeLine::initUI() // --- left widget --- // --------- layer buttons --------- QToolBar* layerButtons = new QToolBar(this); + layerButtons->setIconSize(QSize(22,22)); QLabel* layerLabel = new QLabel(tr("Layers:")); layerLabel->setIndent(5); QToolButton* addLayerButton = new QToolButton(this); - addLayerButton->setIcon(QIcon(":icons/add.png")); + addLayerButton->setIcon(QIcon(":icons/themes/playful/timeline/layer-add.svg")); addLayerButton->setToolTip(tr("Add Layer")); - addLayerButton->setFixedSize(24, 24); mLayerDeleteButton = new QToolButton(this); - mLayerDeleteButton->setIcon(QIcon(":icons/remove.png")); + mLayerDeleteButton->setIcon(QIcon(":icons/themes/playful/timeline/layer-remove.svg")); mLayerDeleteButton->setToolTip(tr("Delete Layer")); - mLayerDeleteButton->setFixedSize(24, 24); QToolButton* duplicateLayerButton = new QToolButton(this); - duplicateLayerButton->setIcon(QIcon(":icons/controls/duplicate.png")); + duplicateLayerButton->setIcon(QIcon(":icons/themes/playful/timeline/layer-duplicate.svg")); duplicateLayerButton->setToolTip(tr("Duplicate Layer")); - duplicateLayerButton->setFixedSize(24, 24); layerButtons->addWidget(layerLabel); layerButtons->addWidget(addLayerButton); @@ -92,16 +90,16 @@ void TimeLine::initUI() layerButtons->setFixedHeight(30); QHBoxLayout* leftToolBarLayout = new QHBoxLayout(); - leftToolBarLayout->setMargin(0); + leftToolBarLayout->setContentsMargins(0, 0, 0, 0); leftToolBarLayout->addWidget(layerButtons); leftToolBar->setLayout(leftToolBarLayout); - QAction* newBitmapLayerAct = new QAction(QIcon(":icons/layer-bitmap.png"), tr("New Bitmap Layer"), this); - QAction* newVectorLayerAct = new QAction(QIcon(":icons/layer-vector.png"), tr("New Vector Layer"), this); - QAction* newSoundLayerAct = new QAction(QIcon(":icons/layer-sound.png"), tr("New Sound Layer"), this); - QAction* newCameraLayerAct = new QAction(QIcon(":icons/layer-camera.png"), tr("New Camera Layer"), this); + QAction* newBitmapLayerAct = new QAction(QIcon(":icons/themes/playful/timeline/cell-bitmap.svg"), tr("New Bitmap Layer"), this); + QAction* newVectorLayerAct = new QAction(QIcon(":icons/themes/playful/timeline/cell-vector.svg"), tr("New Vector Layer"), this); + QAction* newSoundLayerAct = new QAction(QIcon(":icons/themes/playful/timeline/cell-sound.svg"), tr("New Sound Layer"), this); + QAction* newCameraLayerAct = new QAction(QIcon(":icons/themes/playful/timeline/cell-camera.svg"), tr("New Camera Layer"), this); - QMenu* layerMenu = new QMenu(tr("&Layer", "Timeline add-layer menu"), this); + QMenu* layerMenu = new QMenu(tr("Layer", "Timeline add-layer menu"), this); layerMenu->addAction(newBitmapLayerAct); layerMenu->addAction(newVectorLayerAct); layerMenu->addAction(newSoundLayerAct); @@ -112,30 +110,28 @@ void TimeLine::initUI() QGridLayout* leftLayout = new QGridLayout(); leftLayout->addWidget(leftToolBar, 0, 0); leftLayout->addWidget(mLayerList, 1, 0); - leftLayout->setMargin(0); + leftLayout->setContentsMargins(0, 0, 0, 0); leftLayout->setSpacing(0); leftWidget->setLayout(leftLayout); // --- right widget --- // --------- key buttons --------- QToolBar* timelineButtons = new QToolBar(this); + timelineButtons->setIconSize(QSize(22,22)); QLabel* keyLabel = new QLabel(tr("Keys:")); keyLabel->setIndent(5); QToolButton* addKeyButton = new QToolButton(this); - addKeyButton->setIcon(QIcon(":icons/add.png")); + addKeyButton->setIcon(QIcon(":icons/themes/playful/timeline/frame-add.svg")); addKeyButton->setToolTip(tr("Add Frame")); - addKeyButton->setFixedSize(24, 24); QToolButton* removeKeyButton = new QToolButton(this); - removeKeyButton->setIcon(QIcon(":icons/remove.png")); + removeKeyButton->setIcon(QIcon(":icons/themes/playful/timeline/frame-remove.svg")); removeKeyButton->setToolTip(tr("Remove Frame")); - removeKeyButton->setFixedSize(24, 24); QToolButton* duplicateKeyButton = new QToolButton(this); - duplicateKeyButton->setIcon(QIcon(":icons/controls/duplicate.png")); + duplicateKeyButton->setIcon(QIcon(":icons/themes/playful/timeline/frame-duplicate.svg")); duplicateKeyButton->setToolTip(tr("Duplicate Frame")); - duplicateKeyButton->setFixedSize(24, 24); QLabel* zoomLabel = new QLabel(tr("Zoom:")); zoomLabel->setIndent(5); @@ -159,6 +155,7 @@ void TimeLine::initUI() // --------- Time controls --------- mTimeControls = new TimeControls(this); + mTimeControls->setIconSize(QSize(22,22)); mTimeControls->setEditor(editor()); mTimeControls->initUI(); updateLength(); @@ -167,14 +164,14 @@ void TimeLine::initUI() rightToolBarLayout->addWidget(timelineButtons); rightToolBarLayout->setAlignment(Qt::AlignLeft); rightToolBarLayout->addWidget(mTimeControls); - rightToolBarLayout->setMargin(0); + rightToolBarLayout->setContentsMargins(0, 0, 0, 0); rightToolBarLayout->setSpacing(0); rightToolBar->setLayout(rightToolBarLayout); QGridLayout* rightLayout = new QGridLayout(); rightLayout->addWidget(rightToolBar, 0, 0); rightLayout->addWidget(mTracks, 1, 0); - rightLayout->setMargin(0); + rightLayout->setContentsMargins(0, 0, 0, 0); rightLayout->setSpacing(0); rightWidget->setLayout(rightLayout); @@ -189,7 +186,7 @@ void TimeLine::initUI() lay->addWidget(splitter, 0, 0); lay->addWidget(mVScrollbar, 0, 1); lay->addWidget(mHScrollbar, 1, 0); - lay->setMargin(0); + lay->setContentsMargins(0, 0, 0, 0); lay->setSpacing(0); timeLineContent->setLayout(lay); setWidget(timeLineContent); diff --git a/app/src/timelinecells.cpp b/app/src/timelinecells.cpp index a37a6400a4..4524cf38c1 100644 --- a/app/src/timelinecells.cpp +++ b/app/src/timelinecells.cpp @@ -21,6 +21,7 @@ GNU General Public License for more details. #include #include #include +#include #include #include "camerapropertiesdialog.h" @@ -604,10 +605,10 @@ void TimeLineCells::paintLabel(QPainter& painter, const Layer* layer, painter.drawEllipse(x + 6, y + 4, 9, 9); painter.setRenderHint(QPainter::Antialiasing, false); - if (layer->type() == Layer::BITMAP) painter.drawPixmap(QPoint(20, y + 2), QPixmap(":/icons/layer-bitmap.png")); - if (layer->type() == Layer::VECTOR) painter.drawPixmap(QPoint(20, y + 2), QPixmap(":/icons/layer-vector.png")); - if (layer->type() == Layer::SOUND) painter.drawPixmap(QPoint(21, y + 2), QPixmap(":/icons/layer-sound.png")); - if (layer->type() == Layer::CAMERA) painter.drawPixmap(QPoint(21, y + 2), QPixmap(":/icons/layer-camera.png")); + if (layer->type() == Layer::BITMAP) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-bitmap.svg")); + if (layer->type() == Layer::VECTOR) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-vector.svg")); + if (layer->type() == Layer::SOUND) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-sound.svg")); + if (layer->type() == Layer::CAMERA) painter.drawPixmap(QPoint(22, y - 1), QPixmap(":icons/themes/playful/timeline/cell-camera.svg")); if (selected) { @@ -859,7 +860,7 @@ void TimeLineCells::mousePressEvent(QMouseEvent* event) } break; case TIMELINE_CELL_TYPE::Tracks: - if (event->button() == Qt::MidButton) + if (event->button() == Qt::MiddleButton) { mLastFrameNumber = getFrameNumber(event->pos().x()); } @@ -993,7 +994,7 @@ void TimeLineCells::mouseMoveEvent(QMouseEvent* event) } else if (mType == TIMELINE_CELL_TYPE::Tracks) { - if (primaryButton == Qt::MidButton) + if (primaryButton == Qt::MiddleButton) { mFrameOffset = qMin(qMax(0, mFrameLength - width() / getFrameSize()), qMax(0, mFrameOffset + mLastFrameNumber - mFramePosMoveX)); update(); @@ -1050,7 +1051,7 @@ void TimeLineCells::mouseReleaseEvent(QMouseEvent* event) if (frameNumber < 1) frameNumber = 1; int layerNumber = getLayerNumber(event->pos().y()); - if (mType == TIMELINE_CELL_TYPE::Tracks && mCurrentLayerNumber != -1 && primaryButton != Qt::MidButton) + if (mType == TIMELINE_CELL_TYPE::Tracks && mCurrentLayerNumber != -1 && primaryButton != Qt::MiddleButton) { // We should affect the current layer based on what's selected, not where the mouse currently is. Layer* currentLayer = mEditor->layers()->getLayer(mCurrentLayerNumber); @@ -1155,7 +1156,7 @@ void TimeLineCells::editLayerProperties(Layer *layer) const void TimeLineCells::editLayerProperties(LayerCamera* cameraLayer) const { - QRegExp regex("([\\xFFEF-\\xFFFF])+"); + QRegularExpression regex("([\\x{FFEF}-\\x{FFFF}])+"); CameraPropertiesDialog dialog(cameraLayer->name(), cameraLayer->getViewRect().width(), cameraLayer->getViewRect().height()); @@ -1178,7 +1179,7 @@ void TimeLineCells::editLayerProperties(LayerCamera* cameraLayer) const void TimeLineCells::editLayerName(Layer* layer) const { - QRegExp regex("([\\xFFEF-\\xFFFF])+"); + QRegularExpression regex("([\\x{FFEF}-\\x{FFFF}])+"); bool ok; QString name = QInputDialog::getText(nullptr, tr("Layer Properties"), diff --git a/app/src/tooloptionwidget.cpp b/app/src/tooloptionwidget.cpp index f6261336b5..5c51c42065 100644 --- a/app/src/tooloptionwidget.cpp +++ b/app/src/tooloptionwidget.cpp @@ -143,7 +143,6 @@ void ToolOptionWidget::onToolPropertyChanged(ToolType, ToolPropertyType ePropert case USETOLERANCE: break; case BUCKETFILLEXPAND: break; case USEBUCKETFILLEXPAND: break; - case BUCKETFILLLAYERMODE: break; case BUCKETFILLLAYERREFERENCEMODE: break; case FILL_MODE: break; default: diff --git a/app/src/toolspage.cpp b/app/src/toolspage.cpp index 6c7176dfbb..e090fb92ca 100644 --- a/app/src/toolspage.cpp +++ b/app/src/toolspage.cpp @@ -29,6 +29,7 @@ ToolsPage::ToolsPage() : ui(new Ui::ToolsPage) connect(ui->useQuickSizingBox, &QCheckBox::stateChanged, this, &ToolsPage::quickSizingChange); connect(ui->rotationIncrementSlider, &QSlider::valueChanged, this, &ToolsPage::rotationIncrementChange); + connect(ui->invertZoomDirectionBox, &QCheckBox::stateChanged, this, &ToolsPage::invertZoomDirectionChange); } ToolsPage::~ToolsPage() @@ -39,6 +40,7 @@ ToolsPage::~ToolsPage() void ToolsPage::updateValues() { ui->useQuickSizingBox->setChecked(mManager->isOn(SETTING::QUICK_SIZING)); + ui->invertZoomDirectionBox->setChecked(mManager->isOn(SETTING::INVERT_DRAG_ZOOM_DIRECTION)); setRotationIncrement(mManager->getInt(SETTING::ROTATION_INCREMENT)); } @@ -47,6 +49,11 @@ void ToolsPage::quickSizingChange(int b) mManager->set(SETTING::QUICK_SIZING, b != Qt::Unchecked); } +void ToolsPage::invertZoomDirectionChange(int b) +{ + mManager->set(SETTING::INVERT_DRAG_ZOOM_DIRECTION, b != Qt::Unchecked); +} + void ToolsPage::setRotationIncrement(int angle) { int value = qSqrt((angle - 1) / 359.0) * 359; diff --git a/app/src/toolspage.h b/app/src/toolspage.h index 3755f61ffa..dcf642e8c9 100644 --- a/app/src/toolspage.h +++ b/app/src/toolspage.h @@ -37,6 +37,7 @@ public slots: void quickSizingChange(int); void setRotationIncrement(int); void rotationIncrementChange(int); + void invertZoomDirectionChange(int); private: Ui::ToolsPage* ui = nullptr; PreferenceManager* mManager = nullptr; diff --git a/app/ui/bucketoptionswidget.ui b/app/ui/bucketoptionswidget.ui index b5b20c4d73..49ffe7ee29 100644 --- a/app/ui/bucketoptionswidget.ui +++ b/app/ui/bucketoptionswidget.ui @@ -7,7 +7,7 @@ 0 0 400 - 197 + 221 @@ -35,25 +35,7 @@ 6 - - - - - Fill to - - - - - - - - 0 - 0 - - - - - + diff --git a/app/ui/cameraoptionswidget.ui b/app/ui/cameraoptionswidget.ui index ad3a20ac15..f2100f77f5 100644 --- a/app/ui/cameraoptionswidget.ui +++ b/app/ui/cameraoptionswidget.ui @@ -51,12 +51,12 @@ - :/icons/new/svg/camera-scale.svg:/icons/new/svg/camera-scale.svg + :/icons/themes/playful/tools/tool-camera-scale.svg:/icons/themes/playful/tools/tool-camera-scale.svg - 26 - 24 + 22 + 22 @@ -68,12 +68,12 @@ - :/icons/new/svg/camera-rotate.svg:/icons/new/svg/camera-rotate.svg + :/icons/themes/playful/tools/tool-camera-rotate.svg:/icons/themes/playful/tools/tool-camera-rotate.svg - 26 - 24 + 22 + 22 @@ -92,12 +92,12 @@ - :/icons/new/svg/camera-move.svg:/icons/new/svg/camera-move.svg + :/icons/themes/playful/tools/tool-camera-move.svg:/icons/themes/playful/tools/tool-camera-move.svg - 26 - 24 + 22 + 22 diff --git a/app/ui/colorpalette.ui b/app/ui/colorpalette.ui index a66c820f13..559d9a1e7e 100644 --- a/app/ui/colorpalette.ui +++ b/app/ui/colorpalette.ui @@ -51,7 +51,13 @@ - :/icons/add.png:/icons/add.png + :/icons/themes/playful/misc/add-color.svg:/icons/themes/playful/misc/add-color.svg + + + + 22 + 22 + true @@ -80,7 +86,13 @@ - :/icons/remove.png:/icons/remove.png + :/icons/themes/playful/misc/remove-color.svg:/icons/themes/playful/misc/remove-color.svg + + + + 22 + 22 + true @@ -95,7 +107,7 @@ 40 - 20 + 22 @@ -134,12 +146,12 @@ - :/app/icons/new/svg/color-dialog.svg:/app/icons/new/svg/color-dialog.svg + :/icons/themes/playful/misc/color-dialog.svg:/icons/themes/playful/misc/color-dialog.svg - 16 - 16 + 22 + 22 @@ -155,7 +167,7 @@ 40 - 20 + 22 @@ -179,12 +191,12 @@ - :/app/icons/new/svg/more_options.svg:/app/icons/new/svg/more_options.svg + :/icons/themes/playful/misc/more-options.svg:/icons/themes/playful/misc/more-options.svg - 15 - 15 + 22 + 22 diff --git a/app/ui/errordialog.ui b/app/ui/errordialog.ui index 7e955dca55..315bf36b77 100644 --- a/app/ui/errordialog.ui +++ b/app/ui/errordialog.ui @@ -40,7 +40,7 @@ - :/app/icons/dialog-error.svg + :/icons/themes/playful/dialog-error.svg Qt::AlignCenter diff --git a/app/ui/exportimageoptions.ui b/app/ui/exportimageoptions.ui index 23341ac933..98d1ae91ce 100644 --- a/app/ui/exportimageoptions.ui +++ b/app/ui/exportimageoptions.ui @@ -98,6 +98,11 @@ TIFF + + + WEBP + + diff --git a/app/ui/generalpage.ui b/app/ui/generalpage.ui index 0418f4d27b..5586b0b6bb 100644 --- a/app/ui/generalpage.ui +++ b/app/ui/generalpage.ui @@ -38,9 +38,9 @@ 0 - -302 - 292 - 845 + -470 + 306 + 997 @@ -410,6 +410,34 @@ + + + + Scroll Wheel Zoom + + + + 11 + + + 11 + + + 11 + + + 11 + + + + + Invert Scroll Direction + + + + + + diff --git a/app/ui/mainwindow2.ui b/app/ui/mainwindow2.ui index ae8c041974..2cddb9ae31 100644 --- a/app/ui/mainwindow2.ui +++ b/app/ui/mainwindow2.ui @@ -49,7 +49,7 @@ 0 0 831 - 26 + 24 @@ -65,7 +65,7 @@ - + @@ -140,6 +140,10 @@ Zoom + + + :/icons/themes/playful/menubar/zoom-select.svg:/icons/themes/playful/menubar/zoom-select.svg + @@ -166,7 +170,7 @@ - :/icons/overlayAngle.png:/icons/overlayAngle.png + :/icons/themes/playful/display/perspective-angle.svg:/icons/themes/playful/display/perspective-angle.svg @@ -331,7 +335,7 @@ - :/icons/new.png:/icons/new.png + :/icons/themes/playful/menubar/new.svg:/icons/themes/playful/menubar/new.svg New @@ -340,7 +344,7 @@ - :/icons/open.png:/icons/open.png + :/icons/themes/playful/menubar/open.svg:/icons/themes/playful/menubar/open.svg Open @@ -349,7 +353,7 @@ - :/icons/save.png:/icons/save.png + :/icons/themes/playful/menubar/save.svg:/icons/themes/playful/menubar/save.svg Save @@ -358,17 +362,13 @@ - :/icons/saveas.png:/icons/saveas.png + :/icons/themes/playful/menubar/save.svg:/icons/themes/playful/menubar/save.svg Save As... - - - :/icons/exit.png:/icons/exit.png - Exit @@ -424,7 +424,7 @@ - :/icons/undo.png:/icons/undo.png + :/icons/themes/playful/menubar/undo.svg:/icons/themes/playful/menubar/undo.svg Undo @@ -433,7 +433,7 @@ - :/icons/redo.png:/icons/redo.png + :/icons/themes/playful/menubar/redo.svg:/icons/themes/playful/menubar/redo.svg Redo @@ -445,7 +445,7 @@ - :/icons/cut.png:/icons/cut.png + :/icons/themes/playful/menubar/cut.svg:/icons/themes/playful/menubar/cut.svg Cut @@ -457,7 +457,7 @@ - :/icons/copy.png:/icons/copy.png + :/icons/themes/playful/menubar/copy.svg:/icons/themes/playful/menubar/copy.svg Copy @@ -469,7 +469,7 @@ - :/icons/paste.png:/icons/paste.png + :/icons/themes/playful/menubar/paste.svg:/icons/themes/playful/menubar/paste.svg Paste @@ -488,7 +488,7 @@ - :/icons/new/svg/trash_detailed.svg:/icons/new/svg/trash_detailed.svg + :/icons/themes/playful/menubar/clear-canvas.svg:/icons/themes/playful/menubar/clear-canvas.svg Clear Frame @@ -510,7 +510,7 @@ - :/icons/zoom-in.png:/icons/zoom-in.png + :/icons/themes/playful/menubar/zoom-in.svg:/icons/themes/playful/menubar/zoom-in.svg Zoom In @@ -519,7 +519,7 @@ - :/icons/zoom-out.png:/icons/zoom-out.png + :/icons/themes/playful/menubar/zoom-out.svg:/icons/themes/playful/menubar/zoom-out.svg Zoom Out @@ -538,7 +538,7 @@ - :/icons/zoom-reset.png:/icons/zoom-reset.png + :/icons/themes/playful/menubar/view-reset.svg:/icons/themes/playful/menubar/view-reset.svg Reset @@ -550,7 +550,7 @@ - :/app/icons/mirror.png:/app/icons/mirror.png + :/icons/themes/playful/display/mirror-horizontal.svg:/icons/themes/playful/display/mirror-horizontal.svg Horizontal Flip @@ -562,7 +562,7 @@ - :/app/icons/mirrorV.png:/app/icons/mirrorV.png + :/icons/themes/playful/display/mirror-vertical.svg:/icons/themes/playful/display/mirror-vertical.svg Vertical Flip @@ -582,7 +582,7 @@ - :/icons/grid.png:/icons/grid.png + :/icons/themes/playful/display/overlay-grid.svg:/icons/themes/playful/display/overlay-grid.svg Grid @@ -594,7 +594,7 @@ - :/app/icons/onionPrev.png:/app/icons/onionPrev.png + :/icons/themes/playful/onion/onionskin-red.svg:/icons/themes/playful/onion/onionskin-red.svg Previous @@ -609,7 +609,7 @@ - :/app/icons/onionNext.png:/app/icons/onionNext.png + :/icons/themes/playful/onion/onionskin-blue.svg:/icons/themes/playful/onion/onionskin-blue.svg Next @@ -621,7 +621,7 @@ - :/icons/controls/play.png:/icons/controls/play.png + :/icons/themes/playful/controls/control-play.svg:/icons/themes/playful/controls/control-play.svg Play @@ -633,26 +633,18 @@ - :/icons/controls/loop.png:/icons/controls/loop.png + :/icons/themes/playful/controls/control-loop.svg:/icons/themes/playful/controls/control-loop.svg Loop - - - :/icons/next.png:/icons/next.png - Next Frame - - - :/icons/prev.png:/icons/prev.png - Previous Frame @@ -660,7 +652,7 @@ - :/icons/add.png:/icons/add.png + :/icons/themes/playful/timeline/frame-add.svg:/icons/themes/playful/timeline/frame-add.svg Add Frame @@ -669,7 +661,7 @@ - :/icons/controls/duplicate.png:/icons/controls/duplicate.png + :/icons/themes/playful/timeline/frame-duplicate.svg:/icons/themes/playful/timeline/frame-duplicate.svg Duplicate Frame @@ -678,7 +670,7 @@ - :/icons/remove.png:/icons/remove.png + :/icons/themes/playful/timeline/frame-remove.svg:/icons/themes/playful/timeline/frame-remove.svg Remove Frame @@ -687,7 +679,7 @@ - :/icons/new/svg/arrow.svg:/icons/new/svg/arrow.svg + :/icons/themes/playful/tools/tool-move.svg:/icons/themes/playful/tools/tool-move.svg Move @@ -696,7 +688,7 @@ - :/icons/new/svg/selection.svg:/icons/new/svg/selection.svg + :/icons/themes/playful/tools/tool-select.svg:/icons/themes/playful/tools/tool-select.svg Select @@ -705,7 +697,7 @@ - :/icons/new/svg/brush_detailed.svg:/icons/new/svg/brush_detailed.svg + :/icons/themes/playful/tools/tool-brush.svg:/icons/themes/playful/tools/tool-brush.svg Brush @@ -714,7 +706,7 @@ - :/icons/new/svg/line.svg:/icons/new/svg/line.svg + :/icons/themes/playful/tools/tool-polyline.svg:/icons/themes/playful/tools/tool-polyline.svg Polyline @@ -723,7 +715,7 @@ - :/icons/new/svg/smudge_detailed.svg:/icons/new/svg/smudge_detailed.svg + :/icons/themes/playful/tools/tool-smudge.svg:/icons/themes/playful/tools/tool-smudge.svg Smudge @@ -732,7 +724,7 @@ - :/icons/new/svg/pen_detailed.svg:/icons/new/svg/pen_detailed.svg + :/icons/themes/playful/tools/tool-pen.svg:/icons/themes/playful/tools/tool-pen.svg Pen @@ -741,7 +733,7 @@ - :/icons/new/svg/hand_detailed.svg:/icons/new/svg/hand_detailed.svg + :/icons/themes/playful/tools/tool-hand.svg:/icons/themes/playful/tools/tool-hand.svg Hand @@ -750,7 +742,7 @@ - :/icons/new/svg/pencil_detailed.svg:/icons/new/svg/pencil_detailed.svg + :/icons/themes/playful/tools/tool-pencil.svg:/icons/themes/playful/tools/tool-pencil.svg Pencil @@ -759,7 +751,7 @@ - :/icons/new/svg/bucket_detailed.svg:/icons/new/svg/bucket_detailed.svg + :/icons/themes/playful/tools/tool-bucket.svg:/icons/themes/playful/tools/tool-bucket.svg Bucket @@ -768,7 +760,7 @@ - :/icons/new/svg/eyedropper_detailed.svg:/icons/new/svg/eyedropper_detailed.svg + :/icons/themes/playful/tools/tool-eyedropper.svg:/icons/themes/playful/tools/tool-eyedropper.svg Eyedropper @@ -777,7 +769,7 @@ - :/icons/new/svg/eraser_detailed.svg:/icons/new/svg/eraser_detailed.svg + :/icons/themes/playful/tools/tool-eraser.svg:/icons/themes/playful/tools/tool-eraser.svg Eraser @@ -786,7 +778,7 @@ - :/icons/layer-bitmap.png:/icons/layer-bitmap.png + :/icons/themes/playful/timeline/cell-bitmap.svg:/icons/themes/playful/timeline/cell-bitmap.svg New Bitmap Layer @@ -795,7 +787,7 @@ - :/icons/layer-vector.png:/icons/layer-vector.png + :/icons/themes/playful/timeline/cell-vector.svg:/icons/themes/playful/timeline/cell-vector.svg New Vector Layer @@ -804,7 +796,7 @@ - :/icons/layer-sound.png:/icons/layer-sound.png + :/icons/themes/playful/timeline/cell-sound.svg:/icons/themes/playful/timeline/cell-sound.svg New Sound Layer @@ -813,13 +805,17 @@ - :/icons/layer-camera.png:/icons/layer-camera.png + :/icons/themes/playful/timeline/cell-camera.svg:/icons/themes/playful/timeline/cell-camera.svg New Camera Layer + + + :/icons/themes/playful/timeline/layer-remove.svg:/icons/themes/playful/timeline/layer-remove.svg + Delete Current Layer @@ -917,9 +913,9 @@ F1 - + - Animated GIF... + Animated Image... @@ -1124,7 +1120,7 @@ - :/app/icons/thinlines5.png:/app/icons/thinlines5.png + :/icons/themes/playful/display/lines-invisible.svg:/icons/themes/playful/display/lines-invisible.svg Show Invisible Lines @@ -1136,7 +1132,7 @@ - :/app/icons/outlines5.png:/app/icons/outlines5.png + :/icons/themes/playful/display/lines-outline.svg:/icons/themes/playful/display/lines-outline.svg Show Outlines Only @@ -1148,7 +1144,7 @@ - :/icons/overlayCenter.png:/icons/overlayCenter.png + :/icons/themes/playful/display/overlay-center.svg:/icons/themes/playful/display/overlay-center.svg Center @@ -1160,7 +1156,7 @@ - :/icons/overlayThirds.png:/icons/overlayThirds.png + :/icons/themes/playful/display/overlay-thirds.svg:/icons/themes/playful/display/overlay-thirds.svg Thirds @@ -1172,7 +1168,7 @@ - :/icons/overlayGoldenRatio.png:/icons/overlayGoldenRatio.png + :/icons/themes/playful/display/overlay-golden-ratio.svg:/icons/themes/playful/display/overlay-golden-ratio.svg Golden Ratio @@ -1184,7 +1180,7 @@ - :/icons/overlaySafe.png:/icons/overlaySafe.png + :/icons/themes/playful/display/overlay-safe.svg:/icons/themes/playful/display/overlay-safe.svg Safe Areas @@ -1196,7 +1192,7 @@ - :/icons/overlayPerspective1.png:/icons/overlayPerspective1.png + :/icons/themes/playful/display/perspective-onepoint.svg:/icons/themes/playful/display/perspective-onepoint.svg One Point Perspective @@ -1208,7 +1204,7 @@ - :/icons/overlayPerspective2.png:/icons/overlayPerspective2.png + :/icons/themes/playful/display/perspective-twopoints.svg:/icons/themes/playful/display/perspective-twopoints.svg Two Point Perspective @@ -1220,7 +1216,7 @@ - :/icons/overlayPerspective3.png:/icons/overlayPerspective3.png + :/icons/themes/playful/display/perspective-threepoints.svg:/icons/themes/playful/display/perspective-threepoints.svg Three Point Perspective diff --git a/app/ui/onionskin.ui b/app/ui/onionskin.ui index bc9b8967c7..20c6eec598 100644 --- a/app/ui/onionskin.ui +++ b/app/ui/onionskin.ui @@ -13,7 +13,7 @@ 187 - 121 + 122 @@ -77,7 +77,7 @@ 0 0 213 - 361 + 360 @@ -104,6 +104,9 @@ Previous Frames + + true + QLayout::SetDefaultConstraint @@ -120,38 +123,6 @@ 6 - - - - Onion skin previous frame - - - Onion skin previous frame - - - false - - - ... - - - - :/app/icons/onionPrev.png:/app/icons/onionPrev.png - - - - 24 - 24 - - - - true - - - true - - - @@ -190,12 +161,12 @@ - :/app/icons/onion-red.png:/app/icons/onion-red.png + :/icons/themes/playful/onion/onionskin-red.svg:/icons/themes/playful/onion/onionskin-red.svg - 24 - 24 + 22 + 22 @@ -214,6 +185,9 @@ Next Frames + + true + QLayout::SetDefaultConstraint @@ -230,35 +204,6 @@ 6 - - - - Onion skin next frame - - - Onion skin next frame - - - ... - - - - :/app/icons/onionNext.png:/app/icons/onionNext.png - - - - 24 - 24 - - - - true - - - true - - - @@ -309,12 +254,12 @@ - :/app/icons/onion-blue.png:/app/icons/onion-blue.png + :/icons/themes/playful/onion/onionskin-blue.svg:/icons/themes/playful/onion/onionskin-blue.svg - 24 - 24 + 22 + 22 diff --git a/app/ui/preferencesdialog.ui b/app/ui/preferencesdialog.ui index aabb79cba6..2e9ddf3f0a 100644 --- a/app/ui/preferencesdialog.ui +++ b/app/ui/preferencesdialog.ui @@ -6,7 +6,7 @@ 0 0 - 563 + 534 445 @@ -44,21 +44,24 @@ - 96 - 84 + 64 + 64 QListView::Static - 5 + 20 QListView::IconMode + + false + - 0 + -1 @@ -69,7 +72,7 @@ - :/icons/prefspencil.png:/icons/prefspencil.png + :/icons/themes/playful/preferences/preferences-general.svg:/icons/themes/playful/preferences/preferences-general.svg ItemIsSelectable|ItemIsEnabled @@ -84,7 +87,7 @@ - :/icons/prefs-files.png:/icons/prefs-files.png + :/icons/themes/playful/preferences/preferences-files.svg:/icons/themes/playful/preferences/preferences-files.svg ItemIsSelectable|ItemIsEnabled @@ -99,7 +102,7 @@ - :/icons/prefstimeline.png:/icons/prefstimeline.png + :/icons/themes/playful/preferences/preferences-timeline.svg:/icons/themes/playful/preferences/preferences-timeline.svg ItemIsSelectable|ItemIsEnabled @@ -114,7 +117,7 @@ - :/icons/prefs-files.png:/icons/prefs-files.png + :/icons/themes/playful/preferences/preferences-tools.svg:/icons/themes/playful/preferences/preferences-tools.svg ItemIsSelectable|ItemIsEnabled @@ -129,7 +132,7 @@ - :/icons/prefs-shortcuts.png:/icons/prefs-shortcuts.png + :/icons/themes/playful/preferences/preferences-shortcuts.svg:/icons/themes/playful/preferences/preferences-shortcuts.svg ItemIsSelectable|ItemIsEnabled diff --git a/app/ui/toolboxwidget.ui b/app/ui/toolboxwidget.ui index 80ead29212..b2c062096b 100644 --- a/app/ui/toolboxwidget.ui +++ b/app/ui/toolboxwidget.ui @@ -56,7 +56,7 @@ 0 0 88 - 483 + 480 @@ -91,12 +91,12 @@ - :/icons/new/svg/pencil_detailed.svg:/icons/new/svg/pencil_detailed.svg + :/icons/themes/playful/tools/tool-pencil.svg:/icons/themes/playful/tools/tool-pencil.svg - 30 - 30 + 22 + 22 @@ -126,12 +126,12 @@ - :/icons/new/svg/eraser_detailed.svg:/icons/new/svg/eraser_detailed.svg + :/icons/themes/playful/tools/tool-eraser.svg:/icons/themes/playful/tools/tool-eraser.svg - 30 - 30 + 22 + 22 @@ -158,12 +158,12 @@ - :/icons/new/svg/selection.svg:/icons/new/svg/selection.svg + :/icons/themes/playful/tools/tool-select.svg:/icons/themes/playful/tools/tool-select.svg - 30 - 30 + 22 + 22 @@ -190,12 +190,12 @@ - :/icons/new/svg/arrow.svg:/icons/new/svg/arrow.svg + :/icons/themes/playful/tools/tool-move.svg:/icons/themes/playful/tools/tool-move.svg - 30 - 30 + 22 + 22 @@ -222,12 +222,12 @@ - :/icons/new/svg/pen_detailed.svg:/icons/new/svg/pen_detailed.svg + :/icons/themes/playful/tools/tool-pen.svg:/icons/themes/playful/tools/tool-pen.svg - 30 - 30 + 22 + 22 @@ -254,12 +254,12 @@ - :/icons/new/svg/hand_detailed.svg:/icons/new/svg/hand_detailed.svg + :/icons/themes/playful/tools/tool-hand.svg:/icons/themes/playful/tools/tool-hand.svg - 30 - 30 + 22 + 22 @@ -286,12 +286,12 @@ - :/icons/new/svg/line.svg:/icons/new/svg/line.svg + :/icons/themes/playful/tools/tool-polyline.svg:/icons/themes/playful/tools/tool-polyline.svg - 30 - 30 + 22 + 22 @@ -318,12 +318,12 @@ - :/icons/new/svg/bucket_detailed.svg:/icons/new/svg/bucket_detailed.svg + :/icons/themes/playful/tools/tool-bucket.svg:/icons/themes/playful/tools/tool-bucket.svg - 30 - 30 + 22 + 22 @@ -350,12 +350,12 @@ - :/icons/new/svg/eyedropper_detailed.svg:/icons/new/svg/eyedropper_detailed.svg + :/icons/themes/playful/tools/tool-eyedropper.svg:/icons/themes/playful/tools/tool-eyedropper.svg - 30 - 30 + 22 + 22 @@ -382,12 +382,12 @@ - :/icons/new/svg/brush_detailed.svg:/icons/new/svg/brush_detailed.svg + :/icons/themes/playful/tools/tool-brush.svg:/icons/themes/playful/tools/tool-brush.svg - 30 - 30 + 22 + 22 @@ -412,14 +412,17 @@ 32 + + Smudge + - :/icons/new/svg/smudge_detailed.svg:/icons/new/svg/smudge_detailed.svg + :/icons/themes/playful/tools/tool-smudge.svg:/icons/themes/playful/tools/tool-smudge.svg - 30 - 30 + 22 + 22 diff --git a/app/ui/toolspage.ui b/app/ui/toolspage.ui index 3d968d4bc8..30754fce75 100644 --- a/app/ui/toolspage.ui +++ b/app/ui/toolspage.ui @@ -33,8 +33,8 @@ 0 0 - 272 - 566 + 265 + 562 @@ -93,6 +93,37 @@ + + + + Hand Tool + + + + 11 + + + 11 + + + 11 + + + 11 + + + + + Zoom in by dragging the cursor up instead of down + + + Invert Zoom Direction + + + + + + diff --git a/core_lib/core_lib.pro b/core_lib/core_lib.pro index e928b977f8..d1725c8973 100644 --- a/core_lib/core_lib.pro +++ b/core_lib/core_lib.pro @@ -6,17 +6,12 @@ ! include( ../util/common.pri ) { error( Could not find the common.pri file! ) } -QT += core widgets gui xml xmlpatterns multimedia svg - TEMPLATE = lib -CONFIG += qt staticlib precompile_header +CONFIG += staticlib precompile_header +QT += core widgets gui xml multimedia svg RESOURCES += data/core_lib.qrc -MOC_DIR = .moc -OBJECTS_DIR = .obj -UI_DIR = .ui - INCLUDEPATH += src \ src/graphics \ src/graphics/bitmap \ @@ -34,6 +29,8 @@ HEADERS += \ src/corelib-pch.h \ src/graphics/bitmap/bitmapbucket.h \ src/graphics/bitmap/bitmapimage.h \ + src/graphics/bitmap/tile.h \ + src/graphics/bitmap/tiledbuffer.h \ src/graphics/vector/bezierarea.h \ src/graphics/vector/beziercurve.h \ src/graphics/vector/colorref.h \ @@ -119,6 +116,8 @@ HEADERS += \ SOURCES += src/graphics/bitmap/bitmapimage.cpp \ src/graphics/bitmap/bitmapbucket.cpp \ + src/graphics/bitmap/tile.cpp \ + src/graphics/bitmap/tiledbuffer.cpp \ src/graphics/vector/bezierarea.cpp \ src/graphics/vector/beziercurve.cpp \ src/graphics/vector/colorref.cpp \ diff --git a/core_lib/data/core_lib.qrc b/core_lib/data/core_lib.qrc index a420eac515..5bc67e882b 100644 --- a/core_lib/data/core_lib.qrc +++ b/core_lib/data/core_lib.qrc @@ -1,13 +1,5 @@ - icons/brush.png - icons/bucketTool.png - icons/cross.png - icons/eyedropper.png - icons/liquify.png - icons/pen.png - icons/pencil2.png - icons/smudge.png resources/kb.ini diff --git a/core_lib/src/camerapainter.cpp b/core_lib/src/camerapainter.cpp index e33c0ebbcd..ca4a40993f 100644 --- a/core_lib/src/camerapainter.cpp +++ b/core_lib/src/camerapainter.cpp @@ -28,9 +28,21 @@ GNU General Public License for more details. #include "painterutils.h" -CameraPainter::CameraPainter() +CameraPainter::CameraPainter(QPixmap& canvas) : mCanvas(canvas) { + reset(); +} +void CameraPainter::reset() +{ + mCameraPixmap = QPixmap(mCanvas.size()); + mCameraPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF()); + mCameraPixmap.fill(Qt::transparent); +} + +void CameraPainter::resetCache() +{ + mCameraCacheValid = false; } void CameraPainter::preparePainter(const Object* object, @@ -52,50 +64,51 @@ void CameraPainter::preparePainter(const Object* object, mViewScale = viewScale; } -void CameraPainter::paint() const +void CameraPainter::paint(const QRect& blitRect) { QPainter painter; - initializePainter(painter, *mCanvas); - paintVisuals(painter); + initializePainter(painter, mCanvas, blitRect, false); + paintVisuals(painter, blitRect); + + mCameraCacheValid = true; } -void CameraPainter::paintCached() +void CameraPainter::paintCached(const QRect& blitRect) { - if (!mCachedPaint) { - QPainter tempPainter; - QPixmap cachedPixmap(mCanvas->size()); - cachedPixmap.fill(Qt::transparent); - initializePainter(tempPainter, cachedPixmap); - - paintVisuals(tempPainter); - mCachedPaint.reset(new QPixmap(cachedPixmap)); - tempPainter.end(); - } - QPainter painter; - initializePainter(painter, *mCanvas); - painter.drawPixmap(0, 0, *mCachedPaint.get()); - painter.end(); + // As always, initialize the painter with the canvas image, as this is what we'll paint on + // In this case though because the canvas has already been painted, we're not interested in + // having the blitter clear the image again, as that would remove our previous painted data, ie. strokes... + initializePainter(painter, mCanvas, blitRect, false); + if (!mCameraCacheValid) { + paintVisuals(painter, blitRect); + painter.end(); + mCameraCacheValid = true; + } else { + painter.setWorldMatrixEnabled(false); + painter.drawPixmap(mZeroPoint, mCameraPixmap); + painter.setWorldMatrixEnabled(true); + painter.end(); + } } -void CameraPainter::setCanvas(QPixmap* canvas) +void CameraPainter::initializePainter(QPainter& painter, QPixmap& pixmap, const QRect& blitRect, bool blitEnabled) { - mCanvas = canvas; -} + painter.begin(&pixmap); -void CameraPainter::resetCache() -{ - mCachedPaint.reset(); -} + if (blitEnabled) { + painter.setCompositionMode(QPainter::CompositionMode_Clear); + painter.fillRect(blitRect, Qt::transparent); + // Surface has been cleared and is ready to be painted on + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + } -void CameraPainter::initializePainter(QPainter& painter, QPixmap& pixmap) const -{ - painter.begin(&pixmap); + painter.setClipRect(blitRect); + painter.setWorldMatrixEnabled(true); painter.setWorldTransform(mViewTransform); - painter.setWorldMatrixEnabled(false); } -void CameraPainter::paintVisuals(QPainter& painter) const +void CameraPainter::paintVisuals(QPainter& painter, const QRect& blitRect) { LayerCamera* cameraLayerBelow = static_cast(mObject->getLayerBelow(mCurrentLayerIndex, Layer::CAMERA)); @@ -105,6 +118,9 @@ void CameraPainter::paintVisuals(QPainter& painter) const if (mLayerVisibility == LayerVisibility::CURRENTONLY && currentLayer->type() != Layer::CAMERA) { return; } + QPainter visualsPainter; + initializePainter(visualsPainter, mCameraPixmap, blitRect, true); + if (!mIsPlaying || mOnionSkinOptions.enabledWhilePlaying) { int startLayerI = 0; @@ -117,15 +133,15 @@ void CameraPainter::paintVisuals(QPainter& painter) const bool isCurrentLayer = cameraLayer == cameraLayerBelow; - painter.save(); - painter.setOpacity(1); + visualsPainter.save(); + visualsPainter.setOpacity(1); if (mLayerVisibility == LayerVisibility::RELATED && !isCurrentLayer) { - painter.setOpacity(calculateRelativeOpacityForLayer(mCurrentLayerIndex, i, mRelativeLayerOpacityThreshold)); + visualsPainter.setOpacity(calculateRelativeOpacityForLayer(mCurrentLayerIndex, i, mRelativeLayerOpacityThreshold)); } - paintOnionSkinning(painter, cameraLayer); + paintOnionSkinning(visualsPainter, cameraLayer); - painter.restore(); + visualsPainter.restore(); } } @@ -133,10 +149,13 @@ void CameraPainter::paintVisuals(QPainter& painter) const QTransform camTransform = cameraLayerBelow->getViewAtFrame(mFrameIndex); QRect cameraRect = cameraLayerBelow->getViewRect(); - paintBorder(painter, camTransform, cameraRect); + paintBorder(visualsPainter, camTransform, cameraRect); + + painter.setWorldMatrixEnabled(false); + painter.drawPixmap(mZeroPoint, mCameraPixmap); } -void CameraPainter::paintBorder(QPainter& painter, const QTransform& camTransform, const QRect& camRect) const +void CameraPainter::paintBorder(QPainter& painter, const QTransform& camTransform, const QRect& camRect) { painter.save(); QRect viewRect = painter.viewport(); @@ -159,12 +178,13 @@ void CameraPainter::paintBorder(QPainter& painter, const QTransform& camTransfor painter.restore(); } -void CameraPainter::paintOnionSkinning(QPainter& painter, const LayerCamera* cameraLayer) const +void CameraPainter::paintOnionSkinning(QPainter& painter, const LayerCamera* cameraLayer) { QPen onionSkinPen; painter.save(); painter.setBrush(Qt::NoBrush); + painter.setWorldMatrixEnabled(false); onionSkinPen.setStyle(Qt::PenStyle::DashLine); mOnionSkinPainter.paint(painter, cameraLayer, mOnionSkinOptions, mFrameIndex, [&] (OnionSkinPaintState state, int onionSkinNumber) { diff --git a/core_lib/src/camerapainter.h b/core_lib/src/camerapainter.h index 1bfa1a9c24..45dcac50f4 100644 --- a/core_lib/src/camerapainter.h +++ b/core_lib/src/camerapainter.h @@ -37,28 +37,31 @@ class KeyFrame; class CameraPainter { public: - explicit CameraPainter(); + explicit CameraPainter(QPixmap& canvas); - void paint() const; - void paintCached(); + void paint(const QRect& blitRect); + void paintCached(const QRect& blitRect); void setOnionSkinPainterOptions(const OnionSkinPainterOptions& options) { mOnionSkinOptions = options; } - void setCanvas(QPixmap* canvas); void preparePainter(const Object* object, int layerIndex, int frameIndex, const QTransform& transform, bool isPlaying, LayerVisibility layerVisibility, float relativeLayerOpacityThreshold, qreal viewScale); + void reset(); + void resetCache(); private: - void initializePainter(QPainter& painter, QPixmap& pixmap) const; - void paintVisuals(QPainter& painter) const; - void paintBorder(QPainter& painter, const QTransform& camTransform, const QRect& camRect) const; - void paintOnionSkinning(QPainter& painter, const LayerCamera* cameraLayer) const; + void initializePainter(QPainter& painter, QPixmap& pixmap, const QRect& blitRect, bool blitEnabled); + void paintVisuals(QPainter& painter, const QRect& blitRect); + void paintBorder(QPainter& painter, const QTransform& camTransform, const QRect& camRect); + void paintOnionSkinning(QPainter& painter, const LayerCamera* cameraLayer); const Object* mObject = nullptr; - QPixmap* mCanvas = nullptr; + QPixmap& mCanvas; - std::unique_ptr mCachedPaint = nullptr; + QPixmap mCameraPixmap; QTransform mViewTransform; + const QPointF mZeroPoint; + OnionSkinSubPainter mOnionSkinPainter; OnionSkinPainterOptions mOnionSkinOptions; @@ -69,6 +72,7 @@ class CameraPainter qreal mViewScale = 0; bool mIsPlaying = false; + bool mCameraCacheValid = false; }; #endif // CAMERAPAINTER_H diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 529149764e..ef6c212214 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -17,12 +17,13 @@ GNU General Public License for more details. #include "canvaspainter.h" #include -#include #include "object.h" #include "layerbitmap.h" #include "layervector.h" #include "bitmapimage.h" +#include "tile.h" +#include "tiledbuffer.h" #include "vectorimage.h" #include "painterutils.h" @@ -40,13 +41,17 @@ void CanvasPainter::reset() { mPostLayersPixmap = QPixmap(mCanvas.size()); mPreLayersPixmap = QPixmap(mCanvas.size()); + mCurrentLayerPixmap = QPixmap(mCanvas.size()); + mOnionSkinPixmap = QPixmap(mCanvas.size()); mPreLayersPixmap.fill(Qt::transparent); mCanvas.fill(Qt::transparent); - mCurrentLayerPixmap = QPixmap(mCanvas.size()); mCurrentLayerPixmap.fill(Qt::transparent); mPostLayersPixmap.fill(Qt::transparent); - mOnionSkinPixmap = QPixmap(mCanvas.size()); mOnionSkinPixmap.fill(Qt::transparent); + mCurrentLayerPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF()); + mPreLayersPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF()); + mPostLayersPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF()); + mOnionSkinPixmap.setDevicePixelRatio(mCanvas.devicePixelRatioF()); } void CanvasPainter::setViewTransform(const QTransform view, const QTransform viewInverse) @@ -92,7 +97,7 @@ void CanvasPainter::paintCached(const QRect& blitRect) QPainter mainPainter; initializePainter(mainPainter, mCanvas, blitRect); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); + mainPainter.drawPixmap(mPointZero, mPreLayersPixmap); mainPainter.setWorldMatrixEnabled(true); paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); @@ -107,7 +112,7 @@ void CanvasPainter::paintCached(const QRect& blitRect) } mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); + mainPainter.drawPixmap(mPointZero, mPostLayersPixmap); mainPainter.setWorldMatrixEnabled(true); } @@ -121,6 +126,9 @@ void CanvasPainter::initializePainter(QPainter& painter, QPaintDevice& device, c { painter.begin(&device); + // Only draw inside the clipped rectangle + painter.setClipRect(blitRect); + // Clear the area that's about to be painted again, to avoid painting on top of existing pixels // causing artifacts. painter.setCompositionMode(QPainter::CompositionMode_Clear); @@ -151,16 +159,15 @@ void CanvasPainter::renderPostLayers(QPainter& painter, const QRect& blitRect) } } -void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, BitmapImage* buffer) +void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, TiledBuffer* tiledBuffer) { - Q_UNUSED(rect) Q_ASSERT(object); mObject = object; CANVASPAINTER_LOG("Set CurrentLayerIndex = %d", currentLayer); mCurrentLayerIndex = currentLayer; mFrameNumber = frame; - mBuffer = buffer; + mTiledBuffer = tiledBuffer; } void CanvasPainter::paint(const QRect& blitRect) @@ -176,7 +183,7 @@ void CanvasPainter::paint(const QRect& blitRect) preLayerPainter.end(); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(blitRect, mPreLayersPixmap, blitRect); + mainPainter.drawPixmap(mPointZero, mPreLayersPixmap); mainPainter.setWorldMatrixEnabled(true); paintCurrentFrame(mainPainter, blitRect, mCurrentLayerIndex, mCurrentLayerIndex); @@ -186,7 +193,7 @@ void CanvasPainter::paint(const QRect& blitRect) postLayerPainter.end(); mainPainter.setWorldMatrixEnabled(false); - mainPainter.drawPixmap(blitRect, mPostLayersPixmap, blitRect); + mainPainter.drawPixmap(mPointZero, mPostLayersPixmap); mainPainter.setWorldMatrixEnabled(true); mPreLayersPixmapCacheValid = true; @@ -230,7 +237,7 @@ void CanvasPainter::paintBitmapOnionSkinFrame(QPainter& painter, const QRect& bl initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); onionSkinPainter.drawImage(bitmapImage->topLeft(), *bitmapImage->image()); - paintOnionSkinFrame(painter, onionSkinPainter, blitRect, nFrame, colorize, bitmapImage->getOpacity()); + paintOnionSkinFrame(painter, onionSkinPainter, nFrame, colorize, bitmapImage->getOpacity()); } void CanvasPainter::paintVectorOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize) @@ -244,11 +251,11 @@ void CanvasPainter::paintVectorOnionSkinFrame(QPainter& painter, const QRect& bl QPainter onionSkinPainter; initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); - vectorImage->paintImage(onionSkinPainter, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); - paintOnionSkinFrame(painter, onionSkinPainter, blitRect, nFrame, colorize, vectorImage->getOpacity()); + vectorImage->paintImage(onionSkinPainter, *mObject, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); + paintOnionSkinFrame(painter, onionSkinPainter, nFrame, colorize, vectorImage->getOpacity()); } -void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, const QRect& blitRect, int nFrame, bool colorize, qreal frameOpacity) +void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, int nFrame, bool colorize, qreal frameOpacity) { // Don't transform the image here as we used the viewTransform in the image output painter.setWorldMatrixEnabled(false); @@ -272,7 +279,7 @@ void CanvasPainter::paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPa onionSkinPainter.setBrush(colorBrush); onionSkinPainter.drawRect(painter.viewport()); } - painter.drawPixmap(blitRect, mOnionSkinPixmap, blitRect); + painter.drawPixmap(mPointZero, mOnionSkinPixmap); } void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -283,7 +290,7 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit if (paintedImage == nullptr) { return; } paintedImage->loadFile(); // Critical! force the BitmapImage to load the image - const bool isDrawing = mBuffer && !mBuffer->bounds().isEmpty(); + const bool isDrawing = mTiledBuffer && !mTiledBuffer->bounds().isEmpty(); QPainter currentBitmapPainter; initializePainter(currentBitmapPainter, mCurrentLayerPixmap, blitRect); @@ -291,19 +298,29 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit painter.setOpacity(paintedImage->getOpacity() - (1.0-painter.opacity())); painter.setWorldMatrixEnabled(false); - currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); + if (isCurrentLayer && isDrawing) + { + currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); - if (isCurrentLayer) { - if (isDrawing) { - // paint the current stroke data which hasn't been applied to a bitmapImage yet - currentBitmapPainter.setCompositionMode(mOptions.cmBufferBlendMode); - currentBitmapPainter.drawImage(mBuffer->topLeft(), *mBuffer->image()); - } else if (mRenderTransform) { - paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); + currentBitmapPainter.setCompositionMode(mOptions.cmBufferBlendMode); + const auto tiles = mTiledBuffer->tiles(); + for (const Tile* tile : tiles) { + currentBitmapPainter.drawPixmap(tile->posF(), tile->pixmap()); } + } else { + // When we're drawing using a tool, the surface will be painted by the tiled buffer, + // and thus we don't want to paint the current image again + // When we're on another layer though, the tiled buffer is not used + currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); + } + + // We do not wish to draw selection transformations on anything but the current layer + Q_ASSERT(!isDrawing || mSelectionTransform.isIdentity()); + if (isCurrentLayer && mRenderTransform && !isDrawing) { + paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); } - painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); + painter.drawPixmap(mPointZero, mCurrentLayerPixmap); } void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -318,15 +335,19 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit QPainter currentVectorPainter; initializePainter(currentVectorPainter, mCurrentLayerPixmap, blitRect); - const bool isDrawing = mBuffer && !mBuffer->bounds().isEmpty(); + const bool isDrawing = mTiledBuffer->isValid(); // Paint existing vector image to the painter - vectorImage->paintImage(currentVectorPainter, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); + vectorImage->paintImage(currentVectorPainter, *mObject, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); if (isCurrentLayer) { if (isDrawing) { currentVectorPainter.setCompositionMode(mOptions.cmBufferBlendMode); - currentVectorPainter.drawImage(mBuffer->topLeft(), *mBuffer->image()); + + const auto tiles = mTiledBuffer->tiles(); + for (const Tile* tile : tiles) { + currentVectorPainter.drawPixmap(tile->posF(), tile->pixmap()); + } } else if (mRenderTransform) { vectorImage->setSelectionTransformation(mSelectionTransform); } @@ -336,9 +357,9 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit painter.setWorldMatrixEnabled(false); painter.setTransform(QTransform()); - // Remember to adjust opacity based on addition opacity value from image + // Remember to adjust opacity based on additional opacity value from the keyframe painter.setOpacity(vectorImage->getOpacity() - (1.0-painter.opacity())); - painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); + painter.drawPixmap(mPointZero, mCurrentLayerPixmap); } void CanvasPainter::paintTransformedSelection(QPainter& painter, BitmapImage* bitmapImage, const QRect& selection) const diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index 58ad6e993d..7f1d3c285a 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -30,6 +30,8 @@ GNU General Public License for more details. #include "onionskinpainteroptions.h" #include "onionskinsubpainter.h" + +class TiledBuffer; class Object; class BitmapImage; class ViewManager; @@ -39,6 +41,7 @@ struct CanvasPainterOptions bool bAntiAlias = false; bool bThinLines = false; bool bOutlines = false; + LayerVisibility eLayerVisibility = LayerVisibility::RELATED; float fLayerVisibilityThreshold = 0.f; float scaling = 1.0f; @@ -61,7 +64,7 @@ class CanvasPainter void setTransformedSelection(QRect selection, QTransform transform); void ignoreTransformedSelection(); - void setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, BitmapImage* buffer); + void setPaintSettings(const Object* object, int currentLayer, int frame, TiledBuffer* tilledBuffer); void paint(const QRect& blitRect); void paintCached(const QRect& blitRect); void resetLayerCache(); @@ -88,7 +91,7 @@ class CanvasPainter void paintBitmapOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize); void paintVectorOnionSkinFrame(QPainter& painter, const QRect& blitRect, Layer* layer, int nFrame, bool colorize); - void paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, const QRect& blitRect, int nFrame, bool colorize, qreal frameOpacity); + void paintOnionSkinFrame(QPainter& painter, QPainter& onionSkinPainter, int nFrame, bool colorize, qreal frameOpacity); void paintCurrentBitmapFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer); void paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer); @@ -102,7 +105,7 @@ class CanvasPainter int mCurrentLayerIndex = 0; int mFrameNumber = 0; - BitmapImage* mBuffer = nullptr; + TiledBuffer* mTiledBuffer = nullptr; QImage mScaledBitmap; @@ -119,6 +122,10 @@ class CanvasPainter bool mPreLayersPixmapCacheValid = false; bool mPostLayersPixmapCacheValid = false; + // There's a considerable amount of overhead in simply allocating a QPointF on the fly. + // Since we just need to draw it at 0,0, we might as well make a const value for that purpose + const QPointF mPointZero; + OnionSkinSubPainter mOnionSkinSubPainter; OnionSkinPainterOptions mOnionSkinPainterOptions; diff --git a/core_lib/src/external/linux/linux.cpp b/core_lib/src/external/linux/linux.cpp index 2b0f3e665c..16e86c17d9 100644 --- a/core_lib/src/external/linux/linux.cpp +++ b/core_lib/src/external/linux/linux.cpp @@ -49,6 +49,7 @@ namespace PlatformHandler } } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Temporary solution for high DPI displays // EnableHighDpiScaling is a just in case mechanism in the event that we // want to disable this without recompiling, see #922 @@ -60,5 +61,6 @@ namespace PlatformHandler // Only works on Windows & X11 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) } } diff --git a/core_lib/src/external/macosx/macosx.cpp b/core_lib/src/external/macosx/macosx.cpp index deb1316bc9..a4f1f54355 100644 --- a/core_lib/src/external/macosx/macosx.cpp +++ b/core_lib/src/external/macosx/macosx.cpp @@ -39,7 +39,9 @@ namespace PlatformHandler void initialise() { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif } } diff --git a/core_lib/src/external/win32/win32.cpp b/core_lib/src/external/win32/win32.cpp index 82222e592b..eef2c17377 100644 --- a/core_lib/src/external/win32/win32.cpp +++ b/core_lib/src/external/win32/win32.cpp @@ -29,6 +29,7 @@ namespace PlatformHandler bool isDarkMode() { return false; }; void initialise() { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Temporary solution for high DPI displays // EnableHighDpiScaling is a just in case mechanism in the event that we // want to disable this without recompiling, see #922 @@ -40,5 +41,6 @@ namespace PlatformHandler // Only works on Windows & X11 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } +#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) }; } diff --git a/core_lib/src/graphics/bitmap/bitmapbucket.cpp b/core_lib/src/graphics/bitmap/bitmapbucket.cpp index 88ba46b223..60e506dda2 100644 --- a/core_lib/src/graphics/bitmap/bitmapbucket.cpp +++ b/core_lib/src/graphics/bitmap/bitmapbucket.cpp @@ -48,66 +48,82 @@ BitmapBucket::BitmapBucket(Editor* editor, mTargetFillToLayerIndex = initialLayerIndex; mTolerance = mProperties.toleranceEnabled ? static_cast(mProperties.tolerance) : 0; + const QPoint& point = QPoint(qFloor(fillPoint.x()), qFloor(fillPoint.y())); - if (properties.bucketFillToLayerMode == 1) - { - auto result = findBitmapLayerBelow(initialLayer, initialLayerIndex); - mTargetFillToLayer = result.first; - mTargetFillToLayerIndex = result.second; - } Q_ASSERT(mTargetFillToLayer); - mReferenceImage = *static_cast(initialLayer->getLastKeyFrameAtPosition(frameIndex)); + BitmapImage singleLayerImage = *static_cast(initialLayer->getLastKeyFrameAtPosition(frameIndex)); if (properties.bucketFillReferenceMode == 1) // All layers { mReferenceImage = flattenBitmapLayersToImage(); + } else { + mReferenceImage = singleLayerImage; } - const QPoint point = QPoint(qFloor(fillPoint.x()), qFloor(fillPoint.y())); mStartReferenceColor = mReferenceImage.constScanLine(point.x(), point.y()); + mUseDragToFill = canUseDragToFill(point, color, singleLayerImage); mPixelCache = new QHash(); } -bool BitmapBucket::allowFill(const QPoint& checkPoint) const +bool BitmapBucket::canUseDragToFill(const QPoint& fillPoint, const QColor& bucketColor, const BitmapImage& referenceImage) { - if (mProperties.fillMode == 0 && qAlpha(mBucketColor) == 0) - { - // Filling in overlay mode with a fully transparent color has no - // effect, so we can skip it in this case + QRgb pressReferenceColorSingleLayer = referenceImage.constScanLine(fillPoint.x(), fillPoint.y()); + QRgb startRef = qUnpremultiply(pressReferenceColorSingleLayer); + + if (mProperties.fillMode == 0 && ((QColor(qRed(startRef), qGreen(startRef), qBlue(startRef)) == bucketColor.rgb() && qAlpha(startRef) == 255) || bucketColor.alpha() == 0)) { + // In overlay mode: When the reference pixel matches the bucket color and the reference is fully opaque + // Otherwise when the bucket alpha is zero. + return false; + } else if (mProperties.fillMode == 2 && qAlpha(startRef) == 255) { + // In behind mode: When the reference pixel is already fully opaque, the output will be invisible. return false; } - Q_ASSERT(mTargetFillToLayer); - BitmapImage targetImage = *static_cast(mTargetFillToLayer)->getLastBitmapImageAtFrame(mEditor->currentFrame(), 0); + return true; +} - if (!targetImage.isLoaded()) { return false; } +bool BitmapBucket::allowFill(const QPoint& checkPoint, const QRgb& checkColor) const +{ + // A normal click to fill should happen unconditionally, because the alternative is utterly confusing. + if (!mFilledOnce) { + return true; + } + + return allowContinuousFill(checkPoint, checkColor); +} + +bool BitmapBucket::allowContinuousFill(const QPoint& checkPoint, const QRgb& checkColor) const +{ + if (!mUseDragToFill) { + return false; + } - QRgb colorOfReferenceImage = mReferenceImage.constScanLine(checkPoint.x(), checkPoint.y()); - QRgb targetPixelColor = targetImage.constScanLine(checkPoint.x(), checkPoint.y()); + const QRgb& colorOfReferenceImage = mReferenceImage.constScanLine(checkPoint.x(), checkPoint.y()); - if (targetPixelColor == mBucketColor &&(mProperties.fillMode == 1 || qAlpha(targetPixelColor) == 255)) + if (checkColor == mBucketColor && (mProperties.fillMode == 1 || qAlpha(checkColor) == 255)) { // Avoid filling if target pixel color matches fill color // to avoid creating numerous seemingly useless undo operations return false; } - // Allow filling if the reference pixel matches the start reference color, and - // the target pixel is either transparent or matches the start reference color return BitmapImage::compareColor(colorOfReferenceImage, mStartReferenceColor, mTolerance, mPixelCache) && - (targetPixelColor == 0 || BitmapImage::compareColor(targetPixelColor, mStartReferenceColor, mTolerance, mPixelCache)); + (checkColor == 0 || BitmapImage::compareColor(checkColor, mStartReferenceColor, mTolerance, mPixelCache)); } -void BitmapBucket::paint(const QPointF updatedPoint, std::function state) +void BitmapBucket::paint(const QPointF& updatedPoint, std::function state) { - const QPoint point = QPoint(qFloor(updatedPoint.x()), qFloor(updatedPoint.y())); + const QPoint& point = QPoint(qFloor(updatedPoint.x()), qFloor(updatedPoint.y())); const int currentFrameIndex = mEditor->currentFrame(); - if (!allowFill(point)) { return; } + BitmapImage* targetImage = static_cast(mTargetFillToLayer)->getLastBitmapImageAtFrame(mEditor->currentFrame(), 0); + if (targetImage == nullptr || !targetImage->isLoaded()) { return; } // Can happen if the first frame is deleted while drawing - BitmapImage* targetImage = static_cast(mTargetFillToLayer->getLastKeyFrameAtPosition(currentFrameIndex)); + const QRgb& targetPixelColor = targetImage->constScanLine(point.x(), point.y()); - if (targetImage == nullptr || !targetImage->isLoaded()) { return; } // Can happen if the first frame is deleted while drawing + if (!allowFill(point, targetPixelColor)) { + return; + } QRgb fillColor = mBucketColor; if (mProperties.fillMode == 1) @@ -163,6 +179,7 @@ void BitmapBucket::paint(const QPointF updatedPoint, std::function BitmapBucket::findBitmapLayerBelow(Layer* targetLayer, int layerIndex) const -{ - bool foundLayerBelow = false; - int layerBelowIndex = layerIndex; - for (int i = layerIndex - 1; i >= 0; i--) - { - Layer* searchlayer = mEditor->layers()->getLayer(i); - Q_ASSERT(searchlayer); - - if (searchlayer->type() == Layer::BITMAP && searchlayer->visible()) - { - targetLayer = searchlayer; - foundLayerBelow = true; - layerBelowIndex = i; - break; - } - } - - if (foundLayerBelow && !targetLayer->keyExists(mEditor->currentFrame())) - { - targetLayer->addNewKeyFrameAt(mEditor->currentFrame()); - emit mEditor->updateTimeLine(); - } - return std::make_pair(targetLayer, layerBelowIndex); -} diff --git a/core_lib/src/graphics/bitmap/bitmapbucket.h b/core_lib/src/graphics/bitmap/bitmapbucket.h index 88464994e8..edd71d6158 100644 --- a/core_lib/src/graphics/bitmap/bitmapbucket.h +++ b/core_lib/src/graphics/bitmap/bitmapbucket.h @@ -43,7 +43,7 @@ class BitmapBucket * @param progress - a function that returns the progress of the paint operation, * the layer and frame that was affected at the given point. */ - void paint(const QPointF updatedPoint, std::function progress); + void paint(const QPointF& updatedPoint, std::function progress); private: @@ -57,9 +57,12 @@ class BitmapBucket * @param checkPoint * @return True if you are allowed to fill, otherwise false */ - bool allowFill(const QPoint& checkPoint) const; + bool allowFill(const QPoint& checkPoint, const QRgb& checkColor) const; + bool allowContinuousFill(const QPoint& checkPoint, const QRgb& checkColor) const; + + /** Determines whether fill to drag feature can be used */ + bool canUseDragToFill(const QPoint& fillPoint, const QColor& bucketColor, const BitmapImage& referenceImage); - std::pair findBitmapLayerBelow(Layer* targetLayer, int layerIndex) const; BitmapImage flattenBitmapLayersToImage(); Editor* mEditor = nullptr; @@ -76,6 +79,8 @@ class BitmapBucket int mTolerance = 0; int mTargetFillToLayerIndex = -1; + bool mFilledOnce = false; + bool mUseDragToFill = false; Properties mProperties; }; diff --git a/core_lib/src/graphics/bitmap/bitmapimage.cpp b/core_lib/src/graphics/bitmap/bitmapimage.cpp index c4be6a77c2..e2be3641e0 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.cpp +++ b/core_lib/src/graphics/bitmap/bitmapimage.cpp @@ -24,6 +24,8 @@ GNU General Public License for more details. #include "util.h" #include "blitrect.h" +#include "tile.h" +#include "tiledbuffer.h" BitmapImage::BitmapImage() { @@ -98,7 +100,7 @@ BitmapImage* BitmapImage::clone() const b->setFileName(""); // don't link to the file of the source bitmap image const bool validKeyFrame = !fileName().isEmpty(); - if (validKeyFrame && !isLoaded()) + if (validKeyFrame && !isLoaded()) { // This bitmapImage is temporarily unloaded. // since it's not in the memory, we need to copy the linked png file to prevent data loss. @@ -204,6 +206,28 @@ void BitmapImage::paste(BitmapImage* bitmapImage, QPainter::CompositionMode cm) modification(); } +void BitmapImage::paste(const TiledBuffer* tiledBuffer, QPainter::CompositionMode cm) +{ + if(tiledBuffer->bounds().width() <= 0 || tiledBuffer->bounds().height() <= 0) + { + return; + } + extend(tiledBuffer->bounds()); + + QPainter painter(image()); + + painter.setCompositionMode(cm); + auto const tiles = tiledBuffer->tiles(); + for (const Tile* item : tiles) { + const QPixmap& tilePixmap = item->pixmap(); + const QPoint& tilePos = item->pos(); + painter.drawPixmap(tilePos-mBounds.topLeft(), tilePixmap); + } + painter.end(); + + modification(); +} + void BitmapImage::moveTopLeft(QPoint point) { mBounds.moveTopLeft(point); @@ -624,7 +648,10 @@ void BitmapImage::drawRect(QRectF rectangle, QPen pen, QBrush brush, QPainter::C painter.setRenderHint(QPainter::Antialiasing, antialiasing); painter.setPen(pen); painter.setBrush(brush); - painter.drawRect(rectangle.translated(-mBounds.topLeft())); + + // Adjust the brush rectangle to be bigger than the bounds itself, + // otherwise there will be artifacts shown in some cases when smudging + painter.drawRect(rectangle.translated(-mBounds.topLeft()).adjusted(-1, -1, 1, 1)); painter.end(); } modification(); diff --git a/core_lib/src/graphics/bitmap/bitmapimage.h b/core_lib/src/graphics/bitmap/bitmapimage.h index 6122b92559..ead411bfbb 100644 --- a/core_lib/src/graphics/bitmap/bitmapimage.h +++ b/core_lib/src/graphics/bitmap/bitmapimage.h @@ -22,6 +22,8 @@ GNU General Public License for more details. #include #include +class TiledBuffer; + class BitmapImage : public KeyFrame { @@ -50,6 +52,7 @@ class BitmapImage : public KeyFrame BitmapImage copy(); BitmapImage copy(QRect rectangle); void paste(BitmapImage*, QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver); + void paste(const TiledBuffer* tiledBuffer, QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver); void moveTopLeft(QPoint point); void moveTopLeft(QPointF point) { moveTopLeft(point.toPoint()); } diff --git a/core_lib/src/graphics/bitmap/tile.cpp b/core_lib/src/graphics/bitmap/tile.cpp new file mode 100644 index 0000000000..7727e0517e --- /dev/null +++ b/core_lib/src/graphics/bitmap/tile.cpp @@ -0,0 +1,47 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ + +#include "tile.h" + +#include + +Tile::Tile(const QPoint& pos, QSize size): + mTilePixmap(size), + mPosF(pos), + mPos(pos), + mBounds(pos, size), + mSize(size) +{ + clear(); //Default tiles are transparent +} + +Tile::~Tile() +{ +} + +void Tile::load(const QImage& image, const QPoint& topLeft) +{ + QPainter painter(&mTilePixmap); + + painter.translate(-mPos); + painter.drawImage(topLeft, image); + painter.end(); +} + +void Tile::clear() +{ + mTilePixmap.fill(Qt::transparent); +} diff --git a/core_lib/src/graphics/bitmap/tile.h b/core_lib/src/graphics/bitmap/tile.h new file mode 100644 index 0000000000..b94bf3dc49 --- /dev/null +++ b/core_lib/src/graphics/bitmap/tile.h @@ -0,0 +1,50 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ + +#ifndef TILE_H +#define TILE_H + +#include +#include + +class Tile +{ +public: + + explicit Tile (const QPoint& pos, QSize size); + ~Tile(); + + const QPixmap& pixmap() const { return mTilePixmap; } + QPixmap& pixmap() { return mTilePixmap; } + + const QPoint& pos() const { return mPos; } + const QPointF& posF() const { return mPosF; } + const QRect& bounds() const { return mBounds; } + const QSize& size() const { return mSize; } + + /** Loads the input image into the tile */ + void load(const QImage& image, const QPoint& topLeft); + void clear(); + +private: + QPixmap mTilePixmap; + QPointF mPosF; + QPoint mPos; + QRect mBounds; + QSize mSize; +}; + +#endif // TILE_H diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.cpp b/core_lib/src/graphics/bitmap/tiledbuffer.cpp new file mode 100644 index 0000000000..5d4929e64c --- /dev/null +++ b/core_lib/src/graphics/bitmap/tiledbuffer.cpp @@ -0,0 +1,166 @@ +/* + +Pencil - Traditional Animation Software +Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon +Copyright (C) 2012-2018 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ +#include "tiledbuffer.h" + +#include +#include + +#include "tile.h" + +TiledBuffer::TiledBuffer(QObject* parent) : QObject(parent) +{ +} + +TiledBuffer::~TiledBuffer() +{ + clear(); +} + +Tile* TiledBuffer::getTileFromIndex(const TileIndex& tileIndex) +{ + Tile* selectedTile = mTiles.value(tileIndex, nullptr); + + if (!selectedTile) { + // Time to allocate it, update table: + const QPoint& tilePos (getTilePos(tileIndex)); + selectedTile = new Tile(tilePos, QSize(UNIFORM_TILE_SIZE, UNIFORM_TILE_SIZE)); + mTiles.insert(tileIndex, selectedTile); + + emit this->tileCreated(this, selectedTile); + } else { + emit this->tileUpdated(this, selectedTile); + } + + return selectedTile; +} + +void TiledBuffer::drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing) { + const QRectF brushRect(point.x() - 0.5 * brushWidth, point.y() - 0.5 * brushWidth, brushWidth, brushWidth); + const float tileSize = UNIFORM_TILE_SIZE; + const int width = qMax(brushCursorWidth,brushWidth); + + // Gather the number of tiles that fits the size of the brush width + const int xLeft = qFloor((qFloor(point.x() - width)) / tileSize); + const int xRight = qFloor((qFloor(point.x() + width)) / tileSize); + const int yTop = qFloor(qFloor(point.y() - width) / tileSize); + const int yBottom = qFloor(qFloor(point.y() + width) / tileSize); + + for (int tileY = yTop; tileY <= yBottom; tileY++) { + for (int tileX = xLeft; tileX <= xRight; tileX++) { + + Tile* tile = getTileFromIndex({tileX, tileY}); + + QPainter painter(&tile->pixmap()); + + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setPen(pen); + painter.setBrush(brush); + painter.setCompositionMode(cm); + painter.drawEllipse(brushRect); + painter.end(); + + mTileBounds.extend(tile->bounds()); + } + } +} + +void TiledBuffer::drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing) { + const float tileSize = UNIFORM_TILE_SIZE; + const float imageXRad = image.width(); + const float imageYRad = image.height(); + // Gather the number of tiles that fits the size of the brush width + const int xLeft = qFloor((qFloor(imageBounds.left() - imageXRad)) / tileSize); + const int xRight = qFloor((qFloor(imageBounds.right() + imageXRad)) / tileSize); + const int yTop = qFloor(qFloor(imageBounds.top() - imageYRad) / tileSize); + const int yBottom = qFloor(qFloor(imageBounds.bottom() + imageYRad) / tileSize); + + for (int tileY = yTop; tileY <= yBottom; tileY++) { + for (int tileX = xLeft; tileX <= xRight; tileX++) { + + Tile* tile = getTileFromIndex({tileX, tileY}); + + QPainter painter(&tile->pixmap()); + + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setCompositionMode(cm); + painter.drawImage(imageBounds.topLeft(), image); + painter.end(); + + mTileBounds.extend(tile->bounds()); + } + } +} + + +void TiledBuffer::drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush, + QPainter::CompositionMode cm, bool antialiasing) +{ + const int pathWidth = pen.width(); + const int width = (qMax(pathWidth,cursorWidth) + 1); + const float tileSize = UNIFORM_TILE_SIZE; + const QRectF pathRect = path.boundingRect(); + + // Gather the number of tiles that fits the size of the brush width + const int xLeft = qFloor((qFloor(pathRect.left() - width)) / tileSize); + const int xRight = qFloor((qFloor(pathRect.right() + width)) / tileSize); + const int yTop = qFloor(qFloor(pathRect.top() - width) / tileSize); + const int yBottom = qFloor(qFloor(pathRect.bottom() + width) / tileSize); + + for (int tileY = yTop; tileY <= yBottom; tileY++) { + for (int tileX = xLeft; tileX <= xRight; tileX++) { + + Tile* tile = getTileFromIndex({tileX, tileY}); + + QPainter painter(&tile->pixmap()); + + painter.translate(-tile->pos()); + painter.setRenderHint(QPainter::Antialiasing, antialiasing); + painter.setPen(pen); + painter.setBrush(brush); + painter.setCompositionMode(cm); + painter.drawPath(path); + painter.end(); + + mTileBounds.extend(tile->bounds()); + } + } +} + +void TiledBuffer::clear() +{ + QHashIterator i(mTiles); + + while (i.hasNext()) { + i.next(); + Tile* tile = i.value(); + if (tile) + { + mTiles.remove(i.key()); + delete tile; + } + } + + mTileBounds = BlitRect(); +} + +QPoint TiledBuffer::getTilePos(const TileIndex& index) const +{ + return QPoint { qRound(UNIFORM_TILE_SIZE*static_cast(index.x)), + qRound(UNIFORM_TILE_SIZE*static_cast(index.y)) }; +} diff --git a/core_lib/src/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h new file mode 100644 index 0000000000..3e29cfed82 --- /dev/null +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -0,0 +1,90 @@ +/* + +Pencil2D - Traditional Animation Software +Copyright (C) 2012-2020 Matthew Chiawen Chang + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +*/ +#ifndef TILEDBUFFER_H +#define TILEDBUFFER_H + +#include +#include + +#include "blitrect.h" + +class QImage; +class QRect; +class Tile; + +struct TileIndex { + int x; + int y; +}; + +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) +inline uint qHash(const TileIndex &key, uint seed) +#else +inline size_t qHash(const TileIndex &key, size_t seed) +#endif +{ + return qHash(key.x, seed) ^ key.y; +} + +inline bool operator==(const TileIndex &e1, const TileIndex &e2) +{ + return e1.x == e2.x + && e1.y == e2.y; +} + +class TiledBuffer: public QObject +{ + Q_OBJECT +public: + TiledBuffer(QObject* parent = nullptr); + ~TiledBuffer(); + + /** Clears the content of the tiled buffer */ + void clear(); + + /** Returns true if there are any tiles, otherwise false */ + bool isValid() const { return !mTiles.isEmpty(); } + + /** Draws a brush with the specified parameters to the tiled buffer */ + void drawBrush(const QPointF& point, int brushWidth, int brushCursorWidth, QPen pen, QBrush brush, QPainter::CompositionMode cm, bool antialiasing); + /** Draws a path with the specified parameters to the tiled buffer */ + void drawPath(QPainterPath path, int cursorWidth, QPen pen, QBrush brush, + QPainter::CompositionMode cm, bool antialiasing); + /** Draws a image with the specified parameters to the tiled buffer */ + void drawImage(const QImage& image, const QRect& imageBounds, QPainter::CompositionMode cm, bool antialiasing); + + QHash tiles() const { return mTiles; } + + const QRect& bounds() const { return mTileBounds; } + +signals: + void tileUpdated(TiledBuffer* tiledBuffer, Tile* tile); + void tileCreated(TiledBuffer* tiledBuffer, Tile* tile); + +private: + + Tile* getTileFromIndex(const TileIndex& tileIndex); + + inline QPoint getTilePos(const TileIndex& index) const; + + const int UNIFORM_TILE_SIZE = 64; + + BlitRect mTileBounds; + + QHash mTiles; +}; + +#endif // TILEDBUFFER_H diff --git a/core_lib/src/graphics/vector/beziercurve.cpp b/core_lib/src/graphics/vector/beziercurve.cpp index 5410d5eef4..ebfe27eeaa 100644 --- a/core_lib/src/graphics/vector/beziercurve.cpp +++ b/core_lib/src/graphics/vector/beziercurve.cpp @@ -34,7 +34,7 @@ BezierCurve::BezierCurve() BezierCurve::BezierCurve(const QList& pointList, bool smooth) { QList pressureList; - for (int i = 0; i < pointList.size(); i++) + for (int i = 0; i < pointList.size(); i++) { pressureList << 0.5; // default pressure } @@ -297,13 +297,13 @@ BezierCurve BezierCurve::transformed(QTransform transformation) const if (isSelected(-1)) { newOrigin = transformation.map(newOrigin); } transformedCurve.setOrigin( newOrigin ); for(int i=0; i< vertex.size(); i++) { - QPointF newC1 = c1.at(i); - QPointF newC2 = c2.at(i); - QPointF newVertex = vertex.at(i); - if (isSelected(i-1)) { newC1 = transformation.map(newC1); } - if (isSelected(i)) { newC2 = transformation.map(newC2); newVertex = transformation.map(newVertex); } - transformedCurve.appendCubic( newC1, newC2, newVertex, pressure.at(i) ); - if (isSelected(i)) { transformedCurve.setSelected(i, true); } + QPointF newC1 = c1.at(i); + QPointF newC2 = c2.at(i); + QPointF newVertex = vertex.at(i); + if (isSelected(i-1)) { newC1 = transformation.map(newC1); } + if (isSelected(i)) { newC2 = transformation.map(newC2); newVertex = transformation.map(newVertex); } + transformedCurve.appendCubic( newC1, newC2, newVertex, pressure.at(i) ); + if (isSelected(i)) { transformedCurve.setSelected(i, true); } } transformedCurve.setWidth( width); transformedCurve.setVariableWidth( variableWidth ); @@ -429,9 +429,9 @@ void BezierCurve::removeVertex(int i) } } -void BezierCurve::drawPath(QPainter& painter, Object* object, QTransform transformation, bool simplified, bool showThinLines ) +void BezierCurve::drawPath(QPainter& painter, const Object& object, QTransform transformation, bool simplified, bool showThinLines ) { - QColor color = object->getColor(colorNumber).color; + QColor color = object.getColor(colorNumber).color; BezierCurve myCurve; if (isPartlySelected()) { myCurve = (transformed(transformation)); } @@ -860,12 +860,16 @@ bool BezierCurve::findIntersection(BezierCurve curve1, int i1, BezierCurve curve QPointF intersectionPoint = QPointF(50.0, 50.0); // bogus point QPointF* cubicIntersection = &intersectionPoint; +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + if ( R1.intersects(R2) || L2.intersects(L1, cubicIntersection) == QLineF::BoundedIntersection ) +#else if ( R1.intersects(R2) || L2.intersect(L1, cubicIntersection) == QLineF::BoundedIntersection ) +#endif { //if (L2.intersect(L1, intersection) == QLineF::BoundedIntersection) { //qDebug() << " FOUND rectangle intersection "; //if (intersectionPoint != curve1.getVertex(i1-1) && intersectionPoint != curve1.getVertex(i1)) { - // qDebug() << " it's not one of the points "; + // qDebug() << " it's not one of the points "; // find the cubic intersection int nSteps = 24; P1 = curve1.getVertex(i1-1); @@ -880,7 +884,11 @@ bool BezierCurve::findIntersection(BezierCurve curve1, int i1, BezierCurve curve Q2 = curve2.getPointOnCubic(i2, t); L1 = QLineF(P1, Q1); L2 = QLineF(P2, Q2); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + if (L2.intersects(L1, cubicIntersection) == QLineF::BoundedIntersection) +#else if (L2.intersect(L1, cubicIntersection) == QLineF::BoundedIntersection) +#endif { QPointF intersectionPoint = *cubicIntersection; if (intersectionPoint != curve1.getVertex(i1-1) && intersectionPoint != curve1.getVertex(i1)) diff --git a/core_lib/src/graphics/vector/beziercurve.h b/core_lib/src/graphics/vector/beziercurve.h index 0f1b67f301..47297f57f5 100644 --- a/core_lib/src/graphics/vector/beziercurve.h +++ b/core_lib/src/graphics/vector/beziercurve.h @@ -89,7 +89,7 @@ class BezierCurve QPainterPath getStrokedPath(qreal width, bool pressure); QRectF getBoundingRect(); - void drawPath(QPainter& painter, Object* object, QTransform transformation, bool simplified, bool showThinLines ); + void drawPath(QPainter& painter, const Object& object, QTransform transformation, bool simplified, bool showThinLines ); void createCurve(const QList& pointList, const QList& pressureList , bool smooth); void smoothCurve(); diff --git a/core_lib/src/graphics/vector/vectorimage.cpp b/core_lib/src/graphics/vector/vectorimage.cpp index 18108742be..061b36c2b5 100644 --- a/core_lib/src/graphics/vector/vectorimage.cpp +++ b/core_lib/src/graphics/vector/vectorimage.cpp @@ -33,7 +33,6 @@ VectorImage::VectorImage() VectorImage::VectorImage(const VectorImage& v2) : KeyFrame(v2) { deselectAll(); - mObject = v2.mObject; mCurves = v2.mCurves; mArea = v2.mArea; mOpacity = v2.mOpacity; @@ -52,7 +51,6 @@ VectorImage& VectorImage::operator=(const VectorImage& a) { deselectAll(); KeyFrame::operator=(a); - mObject = a.mObject; mCurves = a.mCurves; mArea = a.mArea; mOpacity = a.mOpacity; @@ -1090,16 +1088,6 @@ void VectorImage::paste(VectorImage& vectorImage) modification(); } -/** - * @brief VectorImage::getColor - * @param colorNumber: the color number which is referred to in the palette - * @return QColor - */ -QColor VectorImage::getColor(int colorNumber) -{ - return mObject->getColor(colorNumber).color; -} - /** * @brief VectorImage::getColorNumber * @param point: The QPoint of the BezierArea @@ -1194,15 +1182,18 @@ void VectorImage::moveColor(int start, int end) /** * @brief VectorImage::paintImage * @param painter: QPainter& + * @param object: const Object& * @param simplified: bool * @param showThinCurves: bool * @param antialiasing: bool */ void VectorImage::paintImage(QPainter& painter, + const Object& object, bool simplified, bool showThinCurves, bool antialiasing) { + painter.save(); painter.setRenderHint(QPainter::Antialiasing, antialiasing); painter.setClipping(false); @@ -1219,7 +1210,7 @@ void VectorImage::paintImage(QPainter& painter, updateArea(mArea[i]); // to do: if selected // --- fill areas ---- // - QColor color = getColor(mArea[i].mColorNumber); + QColor color = object.getColor(mArea[i].mColorNumber).color; painter.save(); painter.setWorldMatrixEnabled(false); @@ -1245,9 +1236,10 @@ void VectorImage::paintImage(QPainter& painter, // ---- draw curves ---- for (BezierCurve curve : mCurves) { - curve.drawPath(painter, mObject, mSelectionTransformation, simplified, showThinCurves); + curve.drawPath(painter, object, mSelectionTransformation, simplified, showThinCurves); painter.setClipping(false); } + painter.restore(); } /** diff --git a/core_lib/src/graphics/vector/vectorimage.h b/core_lib/src/graphics/vector/vectorimage.h index ec97458a53..8f9769debc 100644 --- a/core_lib/src/graphics/vector/vectorimage.h +++ b/core_lib/src/graphics/vector/vectorimage.h @@ -38,8 +38,6 @@ class VectorImage : public KeyFrame VectorImage* clone() const override; - void setObject(Object* pObj) { mObject = pObj; } - bool read(QString filePath); Status write(QString filePath, QString format); @@ -85,7 +83,6 @@ class VectorImage : public KeyFrame void paste(VectorImage&); - QColor getColor(int i); int getColorNumber(QPointF point); bool usesColor(int index); void removeColor(int index); @@ -93,7 +90,7 @@ class VectorImage : public KeyFrame bool isCurveVisible(int curve); void moveColor(int start, int end); - void paintImage(QPainter& painter, bool simplified, bool showThinCurves, bool antialiasing); + void paintImage(QPainter& painter, const Object& object, bool simplified, bool showThinCurves, bool antialiasing); void clear(); void clean(); @@ -165,7 +162,6 @@ class VectorImage : public KeyFrame private: QList mCurves; - Object* mObject = nullptr; QRectF mSelectionRect; QTransform mSelectionTransformation; QSize mSize; diff --git a/core_lib/src/graphics/vector/vertexref.cpp b/core_lib/src/graphics/vector/vertexref.cpp index 307873f2f2..b31765d1f1 100644 --- a/core_lib/src/graphics/vector/vertexref.cpp +++ b/core_lib/src/graphics/vector/vertexref.cpp @@ -27,17 +27,17 @@ VertexRef::VertexRef(int curveN, int vertexN) vertexNumber = vertexN; } -VertexRef VertexRef::nextVertex() +VertexRef VertexRef::nextVertex() const { return VertexRef(curveNumber, vertexNumber+1); } -VertexRef VertexRef::prevVertex() +VertexRef VertexRef::prevVertex() const { return VertexRef(curveNumber, vertexNumber-1); } -bool VertexRef::operator==(VertexRef vertexRef1) +bool VertexRef::operator==(VertexRef vertexRef1) const { if ( (curveNumber == vertexRef1.curveNumber) && (vertexNumber == vertexRef1.vertexNumber)) { @@ -49,7 +49,7 @@ bool VertexRef::operator==(VertexRef vertexRef1) } } -bool VertexRef::operator!=(VertexRef vertexRef1) +bool VertexRef::operator!=(VertexRef vertexRef1) const { if ( (curveNumber != vertexRef1.curveNumber) || (vertexNumber != vertexRef1.vertexNumber)) { diff --git a/core_lib/src/graphics/vector/vertexref.h b/core_lib/src/graphics/vector/vertexref.h index 8e5c487602..3959fa2bde 100644 --- a/core_lib/src/graphics/vector/vertexref.h +++ b/core_lib/src/graphics/vector/vertexref.h @@ -23,10 +23,10 @@ class VertexRef public: VertexRef(); VertexRef(int curveN, int vertexN); - VertexRef nextVertex(); - VertexRef prevVertex(); - bool operator==(VertexRef vertexRef1); - bool operator!=(VertexRef vertexRef1); + VertexRef nextVertex() const; + VertexRef prevVertex() const; + bool operator==(VertexRef vertexRef1) const; + bool operator!=(VertexRef vertexRef1) const; int curveNumber = -1; int vertexNumber = -1; diff --git a/core_lib/src/interface/backgroundwidget.cpp b/core_lib/src/interface/backgroundwidget.cpp index bb17b39972..b51aad4127 100644 --- a/core_lib/src/interface/backgroundwidget.cpp +++ b/core_lib/src/interface/backgroundwidget.cpp @@ -19,6 +19,7 @@ GNU General Public License for more details. #include #include +#include BackgroundWidget::BackgroundWidget(QWidget* parent) : QWidget(parent) @@ -66,11 +67,13 @@ void BackgroundWidget::settingUpdated(SETTING setting) } } -void BackgroundWidget::paintEvent(QPaintEvent *) +void BackgroundWidget::paintEvent(QPaintEvent* event) { QStyleOption opt; - opt.init(this); + opt.initFrom(this); QPainter painter(this); + painter.setClipRect(event->rect()); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); if (mHasShadow) @@ -112,7 +115,7 @@ void BackgroundWidget::loadBackgroundStyle() setStyleSheet(mStyle); } -void BackgroundWidget::drawShadow( QPainter& painter ) +void BackgroundWidget::drawShadow(QPainter& painter) { int radius1 = 12; int radius2 = 8; diff --git a/core_lib/src/interface/backgroundwidget.h b/core_lib/src/interface/backgroundwidget.h index 881a41a397..9c58fc11ff 100644 --- a/core_lib/src/interface/backgroundwidget.h +++ b/core_lib/src/interface/backgroundwidget.h @@ -35,7 +35,7 @@ public slots: protected: - void paintEvent( QPaintEvent* ) override; + void paintEvent(QPaintEvent* event) override; private slots: diff --git a/core_lib/src/interface/editor.cpp b/core_lib/src/interface/editor.cpp index b6d3d44bfc..49af52acb7 100644 --- a/core_lib/src/interface/editor.cpp +++ b/core_lib/src/interface/editor.cpp @@ -914,7 +914,7 @@ void Editor::updateObject() emit updateLayerCount(); } -Status Editor::importBitmapImage(const QString& filePath, int space) +Status Editor::importBitmapImage(const QString& filePath) { QImageReader reader(filePath); @@ -926,8 +926,7 @@ Status Editor::importBitmapImage(const QString& filePath, int space) dd << QString("Raw file path: %1").arg(filePath); QImage img(reader.size(), QImage::Format_ARGB32_Premultiplied); - if (img.isNull()) - { + if (!reader.read(&img)) { QString format = reader.format(); if (!format.isEmpty()) { @@ -955,33 +954,18 @@ Status Editor::importBitmapImage(const QString& filePath, int space) const QPoint pos(view()->getImportView().dx() - (img.width() / 2), view()->getImportView().dy() - (img.height() / 2)); - while (reader.read(&img)) + if (!layer->keyExists(mFrame)) { - int frameNumber = mFrame; - if (!layer->keyExists(frameNumber)) - { - addNewKey(); - } - BitmapImage* bitmapImage = layer->getBitmapImageAtFrame(frameNumber); - BitmapImage importedBitmapImage(pos, img); - bitmapImage->paste(&importedBitmapImage); - emit frameModified(bitmapImage->pos()); + addNewKey(); + } + BitmapImage* bitmapImage = layer->getBitmapImageAtFrame(mFrame); + BitmapImage importedBitmapImage(pos, img); + bitmapImage->paste(&importedBitmapImage); + emit frameModified(bitmapImage->pos()); - if (space > 1) { - frameNumber += space; - } else { - frameNumber += 1; - } - scrubTo(frameNumber); + scrubTo(mFrame+1); - backup(tr("Import Image")); - - // Workaround for tiff import getting stuck in this loop - if (!reader.supportsAnimation()) - { - break; - } - } + backup(tr("Import Image")); return status; } @@ -1048,17 +1032,76 @@ Status Editor::importImage(const QString& filePath) } } -Status Editor::importGIF(const QString& filePath, int numOfImages) +Status Editor::importAnimatedImage(const QString& filePath, int frameSpacing, const std::function& progressChanged, const std::function& wasCanceled) { + frameSpacing = qMax(1, frameSpacing); + + DebugDetails dd; + dd << QString("Raw file path: %1").arg(filePath); + Layer* layer = layers()->currentLayer(); if (layer->type() != Layer::BITMAP) { - DebugDetails dd; - dd << QString("Raw file path: %1").arg(filePath); dd << QString("Current layer: %1").arg(layer->type()); return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("You can only import images to a bitmap layer.")); } - return importBitmapImage(filePath, numOfImages); + LayerBitmap* bitmapLayer = static_cast(layers()->currentLayer()); + + QImageReader reader(filePath); + dd << QString("QImageReader format: %1").arg(QString(reader.format())); + if (!reader.supportsAnimation()) { + return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("The selected image has a format that does not support animation.")); + } + + QImage img(reader.size(), QImage::Format_ARGB32_Premultiplied); + const QPoint pos(view()->getImportView().dx() - (img.width() / 2), + view()->getImportView().dy() - (img.height() / 2)); + int totalFrames = reader.imageCount(); + while (reader.read(&img)) + { + if (reader.error()) + { + dd << QString("QImageReader ImageReaderError type: %1").arg(reader.errorString()); + + QString errorDesc; + switch(reader.error()) + { + case QImageReader::ImageReaderError::FileNotFoundError: + errorDesc = tr("File not found at path \"%1\". Please check the image is present at the specified location and try again.").arg(filePath); + break; + case QImageReader::UnsupportedFormatError: + errorDesc = tr("Image format is not supported. Please convert the image file to one of the following formats and try again:\n%1") + .arg((QString)reader.supportedImageFormats().join(", ")); + break; + default: + errorDesc = tr("An error has occurred while reading the image. Please check that the file is a valid image and try again."); + } + + return Status(Status::FAIL, dd, tr("Import failed"), errorDesc); + } + + if (!bitmapLayer->keyExists(mFrame)) + { + addNewKey(); + } + BitmapImage* bitmapImage = bitmapLayer->getBitmapImageAtFrame(mFrame); + BitmapImage importedBitmapImage(pos, img); + bitmapImage->paste(&importedBitmapImage); + emit frameModified(bitmapImage->pos()); + + if (wasCanceled()) + { + break; + } + + scrubTo(mFrame + frameSpacing); + + backup(tr("Import Image")); + + progressChanged(qFloor(qMin(static_cast(reader.currentImageNumber()) / totalFrames, 1.0) * 100)); + } + + return Status::OK; } void Editor::selectAll() const @@ -1109,14 +1152,9 @@ void Editor::deselectAll() const } } -void Editor::updateFrame(int frameNumber) -{ - mScribbleArea->updateFrame(frameNumber); -} - -void Editor::updateCurrentFrame() +void Editor::updateFrame() { - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void Editor::setCurrentLayerIndex(int i) diff --git a/core_lib/src/interface/editor.h b/core_lib/src/interface/editor.h index e2700bf4a1..a3ba0e179e 100644 --- a/core_lib/src/interface/editor.h +++ b/core_lib/src/interface/editor.h @@ -24,6 +24,18 @@ GNU General Public License for more details. #include "pencilerror.h" #include "pencildef.h" +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +Q_MOC_INCLUDE("colormanager.h") +Q_MOC_INCLUDE("toolmanager.h") +Q_MOC_INCLUDE("layermanager.h") +Q_MOC_INCLUDE("playbackmanager.h") +Q_MOC_INCLUDE("viewmanager.h") +Q_MOC_INCLUDE("preferencemanager.h") +Q_MOC_INCLUDE("selectionmanager.h") +Q_MOC_INCLUDE("soundmanager.h") +Q_MOC_INCLUDE("overlaymanager.h") +Q_MOC_INCLUDE("clipboardmanager.h") +#endif class QClipboard; class QTemporaryDir; @@ -152,23 +164,17 @@ class Editor : public QObject public: //slots - /** Will call update() and update the canvas - * Only call this directly If you need the cache to be intact and require the frame to be repainted - * Convenient method that does the same as updateFrame but for the current frame - */ - void updateCurrentFrame(); - /** Will call update() and update the canvas * Only call this directly If you need the cache to be intact and require the frame to be repainted */ - void updateFrame(int frameNumber); + void updateFrame(); void setModified(int layerNumber, int frameNumber); void clearCurrentFrame(); Status importImage(const QString& filePath); - Status importGIF(const QString& filePath, int numOfImages = 0); + Status importAnimatedImage(const QString& filePath, int frameSpacing, const std::function& progressChanged, const std::function& wasCanceled); void restoreKey(); void scrubNextKeyFrame(); @@ -224,7 +230,7 @@ class Editor : public QObject void resetAutoSaveCounter(); private: - Status importBitmapImage(const QString&, int space = 0); + Status importBitmapImage(const QString&); Status importVectorImage(const QString&); void pasteToCanvas(BitmapImage* bitmapImage, int frameNumber); diff --git a/core_lib/src/interface/flowlayout.cpp b/core_lib/src/interface/flowlayout.cpp index de2c5f96eb..032f065c81 100644 --- a/core_lib/src/interface/flowlayout.cpp +++ b/core_lib/src/interface/flowlayout.cpp @@ -116,7 +116,7 @@ QLayoutItem *FlowLayout::takeAt(int index) Qt::Orientations FlowLayout::expandingDirections() const { - return 0; + return {}; } bool FlowLayout::hasHeightForWidth() const @@ -147,8 +147,9 @@ QSize FlowLayout::minimumSize() const QLayoutItem *item; foreach (item, itemList) size = size.expandedTo(item->minimumSize()); - - size += QSize(2*margin(), 2*margin()); + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + size += QSize(left + right, top + bottom); return size; } diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index fcd978f421..d39359c020 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -32,6 +32,7 @@ GNU General Public License for more details. #include "bitmapimage.h" #include "vectorimage.h" #include "blitrect.h" +#include "tile.h" #include "onionskinpainteroptions.h" @@ -44,7 +45,7 @@ GNU General Public License for more details. #include "selectionmanager.h" #include "overlaymanager.h" -ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent), mCanvasPainter(mCanvas) +ScribbleArea::ScribbleArea(QWidget* parent) : QWidget(parent), mCanvasPainter(mCanvas), mCameraPainter(mCanvas) { setObjectName("ScribbleArea"); @@ -75,6 +76,9 @@ bool ScribbleArea::init() connect(mEditor->select(), &SelectionManager::selectionChanged, this, &ScribbleArea::onSelectionChanged); connect(mEditor->select(), &SelectionManager::needDeleteSelection, this, &ScribbleArea::deleteSelection); + connect(&mTiledBuffer, &TiledBuffer::tileUpdated, this, &ScribbleArea::onTileUpdated); + connect(&mTiledBuffer, &TiledBuffer::tileCreated, this, &ScribbleArea::onTileCreated); + mDoubleClickTimer->setInterval(50); mMouseFilterTimer->setInterval(50); @@ -84,11 +88,12 @@ bool ScribbleArea::init() mQuickSizing = mPrefs->isOn(SETTING::QUICK_SIZING); mMakeInvisible = false; - mIsSimplified = mPrefs->isOn(SETTING::OUTLINES); mMultiLayerOnionSkin = mPrefs->isOn(SETTING::MULTILAYER_ONION); mLayerVisibility = static_cast(mPrefs->getInt(SETTING::LAYER_VISIBILITY)); + mDeltaFactor = mEditor->preference()->isOn(SETTING::INVERT_SCROLL_ZOOM_DIRECTION) ? -1 : 1; + updateCanvasCursor(); setMouseTracking(true); // reacts to mouse move events, even if the button is not pressed @@ -157,6 +162,9 @@ void ScribbleArea::settingUpdated(SETTING setting) case SETTING::LAYER_VISIBILITY: setLayerVisibility(static_cast(mPrefs->getInt(SETTING::LAYER_VISIBILITY))); break; + case SETTING::INVERT_SCROLL_ZOOM_DIRECTION: + mDeltaFactor = mEditor->preference()->isOn(SETTING::INVERT_SCROLL_ZOOM_DIRECTION) ? -1 : 1; + break; default: break; } @@ -184,14 +192,26 @@ void ScribbleArea::setEffect(SETTING e, bool isOn) /************************************************************************************/ // update methods -void ScribbleArea::updateCurrentFrame() +void ScribbleArea::onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile) +{ + Q_UNUSED(tiledBuffer); + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); + update(mappedRect.toAlignedRect()); +} + +void ScribbleArea::onTileCreated(TiledBuffer* tiledBuffer, Tile* tile) { - updateFrame(mEditor->currentFrame()); + Q_UNUSED(tiledBuffer) + const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); + update(mappedRect.toAlignedRect()); } -void ScribbleArea::updateFrame(int frame) +void ScribbleArea::updateFrame() { - Q_ASSERT(frame >= 0); + if (currentTool()->isActive() && currentTool()->isDrawingTool()) { + return; + } + update(); } @@ -249,12 +269,14 @@ void ScribbleArea::invalidateOnionSkinsCacheAround(int frameNumber) void ScribbleArea::invalidateAllCache() { + if (currentTool()->isDrawingTool() && currentTool()->isActive()) { return; } + QPixmapCache::clear(); mPixmapCacheKeys.clear(); invalidatePainterCaches(); mEditor->layers()->currentLayer()->clearDirtyFrames(); - update(); + updateFrame(); } void ScribbleArea::invalidateCacheForFrame(int frameNumber) @@ -272,7 +294,7 @@ void ScribbleArea::invalidatePainterCaches() { mCameraPainter.resetCache(); mCanvasPainter.resetLayerCache(); - update(); + updateFrame(); } void ScribbleArea::onToolPropertyUpdated(ToolType, ToolPropertyType type) @@ -293,7 +315,7 @@ void ScribbleArea::onToolChanged(ToolType) prepOverlays(frame); prepCameraPainter(frame); invalidateCacheForFrame(frame); - updateCurrentFrame(); + updateFrame(); } @@ -308,13 +330,14 @@ void ScribbleArea::onPlayStateChanged() prepOverlays(currentFrame); prepCameraPainter(currentFrame); invalidateCacheForFrame(currentFrame); - updateFrame(currentFrame); + updateFrame(); } void ScribbleArea::onScrubbed(int frameNumber) { + Q_UNUSED(frameNumber) invalidatePainterCaches(); - updateFrame(frameNumber); + updateFrame(); } void ScribbleArea::onFramesModified() @@ -323,7 +346,7 @@ void ScribbleArea::onFramesModified() if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { invalidatePainterCaches(); } - update(); + updateFrame(); } void ScribbleArea::onFrameModified(int frameNumber) @@ -333,7 +356,7 @@ void ScribbleArea::onFrameModified(int frameNumber) invalidatePainterCaches(); } invalidateCacheForFrame(frameNumber); - updateFrame(frameNumber); + updateFrame(); } void ScribbleArea::onViewChanged() @@ -350,7 +373,7 @@ void ScribbleArea::onSelectionChanged() { int currentFrame = mEditor->currentFrame(); invalidateCacheForFrame(currentFrame); - updateFrame(currentFrame); + updateFrame(); } void ScribbleArea::onOnionSkinTypeChanged() @@ -381,8 +404,6 @@ void ScribbleArea::keyPressEvent(QKeyEvent *event) // Don't handle this event on auto repeat if (event->isAutoRepeat()) { return; } - mKeyboardInUse = true; - if (isPointerInUse()) { return; } // prevents shortcuts calls while drawing if (currentTool()->keyPressEvent(event)) @@ -496,8 +517,6 @@ void ScribbleArea::keyReleaseEvent(QKeyEvent *event) return; } - mKeyboardInUse = false; - if (event->key() == 0) { editor()->tools()->tryClearTemporaryTool(Qt::Key_unknown); @@ -520,13 +539,17 @@ void ScribbleArea::keyReleaseEvent(QKeyEvent *event) // mouse and tablet event handlers void ScribbleArea::wheelEvent(QWheelEvent* event) { - // Don't change view if tool is in use + // Don't change view if the tool is in use if (isPointerInUse()) return; static const bool isX11 = QGuiApplication::platformName() == "xcb"; const QPoint pixels = event->pixelDelta(); const QPoint angle = event->angleDelta(); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + const QPointF offset = mEditor->view()->mapScreenToCanvas(event->position()); +#else const QPointF offset = mEditor->view()->mapScreenToCanvas(event->posF()); +#endif const qreal currentScale = mEditor->view()->scaling(); // From the pixelDelta documentation: On X11 this value is driver-specific and unreliable, use angleDelta() instead @@ -536,14 +559,14 @@ void ScribbleArea::wheelEvent(QWheelEvent* event) // Zooming in is faster than zooming out and scrolling twice with delta x yields different zoom than // scrolling once with delta 2x. Someone with the ability to test this code might want to "upgrade" it. const int delta = pixels.y(); - const qreal newScale = currentScale * (1 + (delta * 0.01)); + const qreal newScale = currentScale * (1 + ((delta * mDeltaFactor) * 0.01)); mEditor->view()->scaleAtOffset(newScale, offset); } else if (!angle.isNull()) { const int delta = angle.y(); // 12 rotation steps at "standard" wheel resolution (120/step) result in 100x zoom - const qreal newScale = currentScale * std::pow(100, delta / (12.0 * 120)); + const qreal newScale = currentScale * std::pow(100, (delta * mDeltaFactor) / (12.0 * 120)); mEditor->view()->scaleAtOffset(newScale, offset); } updateCanvasCursor(); @@ -554,7 +577,11 @@ void ScribbleArea::tabletEvent(QTabletEvent *e) { PointerEvent event(e); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (event.pointerType() == QPointingDevice::PointerType::Eraser) +#else if (event.pointerType() == QTabletEvent::Eraser) +#endif { editor()->tools()->tabletSwitchToEraser(); } @@ -629,7 +656,7 @@ void ScribbleArea::tabletEvent(QTabletEvent *e) void ScribbleArea::pointerPressEvent(PointerEvent* event) { bool isCameraLayer = mEditor->layers()->currentLayer()->type() == Layer::CAMERA; - if ((currentTool()->type() != HAND || isCameraLayer) && (event->button() != Qt::RightButton) && (event->button() != Qt::MidButton || isCameraLayer)) + if ((currentTool()->type() != HAND || isCameraLayer) && (event->button() != Qt::RightButton) && (event->button() != Qt::MiddleButton || isCameraLayer)) { Layer* layer = mEditor->layers()->currentLayer(); if (!layer->visible()) @@ -642,7 +669,7 @@ void ScribbleArea::pointerPressEvent(PointerEvent* event) } } - if (event->buttons() & (Qt::MidButton | Qt::RightButton) && + if (event->buttons() & (Qt::MiddleButton | Qt::RightButton) && editor()->tools()->setTemporaryTool(HAND, event->buttons())) { currentTool()->pointerPressEvent(event); @@ -651,7 +678,6 @@ void ScribbleArea::pointerPressEvent(PointerEvent* event) const bool isPressed = event->buttons() & Qt::LeftButton; if (isPressed && mQuickSizing) { - //qDebug() << "Start Adjusting" << event->buttons(); if (currentTool()->startAdjusting(event->modifiers(), 1)) { return; @@ -691,13 +717,6 @@ void ScribbleArea::pointerReleaseEvent(PointerEvent* event) return; // [SHIFT]+drag OR [CTRL]+drag } - if (event->buttons() & (Qt::RightButton | Qt::MiddleButton)) - { - mMouseRightButtonInUse = false; - return; - } - - //qDebug() << "release event"; currentTool()->pointerReleaseEvent(event); editor()->tools()->tryClearTemporaryTool(event->button()); @@ -717,11 +736,11 @@ void ScribbleArea::handleDoubleClick() void ScribbleArea::tabletReleaseEventFired() { - // Under certain circumstances a mouse press event will fire after a tablet release event. - // This causes unexpected behaviours for some of the tools, eg. the bucket. - // The problem only seems to occur on windows and only when tapping. - // prior to this fix, the event queue would look like this: - // eg: TabletPress -> TabletRelease -> MousePress + // Under certain circumstances, a mouse press event will fire after a tablet release event. + // This causes unexpected behaviors for some tools, e.g., the bucket tool. + // The problem only seems to occur on Windows and only when tapping. + // Prior to this fix, the event queue would look like this: + // e.g.: TabletPress -> TabletRelease -> MousePress // The following will filter mouse events created after a tablet release event. mTabletReleaseMillisAgo += 50; @@ -790,12 +809,13 @@ void ScribbleArea::resizeEvent(QResizeEvent* event) QWidget::resizeEvent(event); mDevicePixelRatio = devicePixelRatioF(); mCanvas = QPixmap(QSizeF(size() * mDevicePixelRatio).toSize()); - + mCanvas.setDevicePixelRatio(mDevicePixelRatio); mEditor->view()->setCanvasSize(size()); invalidateCacheForFrame(mEditor->currentFrame()); invalidatePainterCaches(); mCanvasPainter.reset(); + mCameraPainter.reset(); } void ScribbleArea::showLayerNotVisibleWarning() @@ -818,11 +838,10 @@ void ScribbleArea::paintBitmapBuffer() // just return (since we have nothing to paint on). if (layer->getLastKeyFrameAtPosition(frameNumber) == nullptr) { - updateCurrentFrame(); + updateFrame(); return; } - // Clear the temporary pixel path BitmapImage* targetImage = currentBitmapImage(layer); if (targetImage != nullptr) { @@ -835,7 +854,7 @@ void ScribbleArea::paintBitmapBuffer() case BRUSH: case PEN: case PENCIL: - if (getTool(currentTool()->type())->properties.preserveAlpha) + if (currentTool()->properties.preserveAlpha) { cm = QPainter::CompositionMode_SourceOver; } @@ -843,35 +862,20 @@ void ScribbleArea::paintBitmapBuffer() default: //nothing break; } - targetImage->paste(&mBufferImg, cm); + targetImage->paste(&mTiledBuffer, cm); } - QRect rect = mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect(); + QRect rect = mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect(); - drawCanvas(frameNumber, rect.adjusted(-1, -1, 1, 1)); update(rect); - // Update the cache for the last key-frame. - updateFrame(frameNumber); layer->setModified(frameNumber, true); - - mBufferImg.clear(); + mTiledBuffer.clear(); } -void ScribbleArea::clearBitmapBuffer() +void ScribbleArea::clearDrawingBuffer() { - mBufferImg.clear(); -} - -void ScribbleArea::drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm) -{ - mBufferImg.drawLine(P1, P2, pen, cm, mPrefs->isOn(SETTING::ANTIALIAS)); -} - -void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) -{ - mBufferImg.drawPath(mEditor->view()->mapScreenToCanvas(path), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); - update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); + mTiledBuffer.clear(); } void ScribbleArea::paintCanvasCursor(QPainter& painter) @@ -893,9 +897,6 @@ void ScribbleArea::paintCanvasCursor(QPainter& painter) static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())), mCursorImg); - // update center of transformed img for rect only - mTransCursImg = mCursorImg.transformed(view); - mCursorCenterPos.setX(centerCal); mCursorCenterPos.setY(centerCal); } @@ -916,16 +917,17 @@ void ScribbleArea::updateCanvasCursor() } else { - mCursorImg = QPixmap(); // if above does not comply, deallocate image + mCursorImg = QPixmap(); // if the above does not comply, deallocate image } - // update cursor rect - QPoint translatedPos = QPoint(static_cast(mTransformedCursorPos.x() - mCursorCenterPos.x()), - static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())); - - update(mTransCursImg.rect().adjusted(-1, -1, 1, 1) - .translated(translatedPos)); - + // When we're using a tool, the TiledBuffer will take care of this; + // we don't want to cause needless updates + if (!currentTool()->isActive()) { + // update cursor rect + QPoint translatedPos(static_cast(mTransformedCursorPos.x() - mCursorCenterPos.x()), + static_cast(mTransformedCursorPos.y() - mCursorCenterPos.y())); + update(mCursorImg.rect().adjusted(-1, -1, 1, 1).translated(translatedPos)); + } } void ScribbleArea::handleDrawingOnEmptyFrame() @@ -1011,12 +1013,12 @@ void ScribbleArea::paintEvent(QPaintEvent* event) } else { - prepCanvas(currentFrame, event->rect()); + prepCanvas(currentFrame); prepCameraPainter(currentFrame); prepOverlays(currentFrame); mCanvasPainter.paintCached(event->rect()); - mCameraPainter.paintCached(); + mCameraPainter.paintCached(event->rect()); } if (currentTool()->type() == MOVE) @@ -1037,14 +1039,18 @@ void ScribbleArea::paintEvent(QPaintEvent* event) // paints the canvas painter.setWorldMatrixEnabled(false); - painter.drawPixmap(QPoint(0, 0), mCanvas); - currentTool()->paint(painter); + // In other places we use the blitRect to paint the buffer pixmap, however + // the main pixmap which needs to be scaled accordingly to DPI, which is not accounted for when using the event rect + // instead we can set a clipRect to avoid the area being updated needlessly + painter.setClipRect(event->rect()); + painter.drawPixmap(QPointF(), mCanvas); - Layer* layer = mEditor->layers()->currentLayer(); + currentTool()->paint(painter); if (!editor()->playback()->isPlaying()) // we don't need to display the following when the animation is playing { + Layer* layer = mEditor->layers()->currentLayer(); if (layer->type() == Layer::VECTOR) { VectorImage* vectorImage = currentVectorImage(layer); @@ -1119,8 +1125,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) paintCanvasCursor(painter); - mOverlayPainter.paint(painter); - + mOverlayPainter.paint(painter, rect()); // paints the selection outline if (mEditor->select()->somethingSelected()) @@ -1196,10 +1201,9 @@ void ScribbleArea::prepCameraPainter(int frame) onionSkinOptions.minOpacity = mPrefs->getInt(SETTING::ONION_MIN_OPACITY); mCameraPainter.setOnionSkinPainterOptions(onionSkinOptions); - mCameraPainter.setCanvas(&mCanvas); } -void ScribbleArea::prepCanvas(int frame, QRect rect) +void ScribbleArea::prepCanvas(int frame) { Object* object = mEditor->object(); @@ -1233,17 +1237,16 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) mCanvasPainter.setViewTransform(vm->getView(), vm->getViewInverse()); mCanvasPainter.setTransformedSelection(sm->mySelectionRect().toRect(), sm->selectionTransform()); - mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, rect, &mBufferImg); + mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, &mTiledBuffer); } void ScribbleArea::drawCanvas(int frame, QRect rect) { - mCanvas.setDevicePixelRatio(mDevicePixelRatio); - prepCanvas(frame, rect); + prepCanvas(frame); prepCameraPainter(frame); prepOverlays(frame); mCanvasPainter.paint(rect); - mCameraPainter.paint(); + mCameraPainter.paint(rect); } void ScribbleArea::setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset) @@ -1266,39 +1269,64 @@ void ScribbleArea::setGaussianGradient(QGradient &gradient, QColor color, qreal gradient.setColorAt(1.0 - (offset / 100.0), QColor(r, g, b, mainColorAlpha - alphaAdded)); } -void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA) +void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm) { - QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); + mTiledBuffer.drawPath(mEditor->view()->mapScreenToCanvas(path), mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), pen, brush, cm, mPrefs->isOn(SETTING::ANTIALIAS)); +} - mBufferImg.drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), - QPainter::CompositionMode_Source, useAA); - update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); +void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA) +{ + // We use Source as opposed to SourceOver here to avoid the dabs being added on top of each other + mTiledBuffer.drawBrush(thePoint, brushWidth, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_Source, useAA); } void ScribbleArea::drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity) { - drawBrush(thePoint, brushWidth, fixedBrushFeather, fillColor, opacity, true); + drawBrush(thePoint, brushWidth, fixedBrushFeather, fillColor, QPainter::CompositionMode_SourceOver, opacity, true); } -void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, QColor fillColor, qreal opacity, bool usingFeather, bool useAA) +void ScribbleArea::drawBrush(QPointF thePoint, qreal brushWidth, qreal mOffset, QColor fillColor, QPainter::CompositionMode compMode, qreal opacity, bool usingFeather, bool useAA) { - QRectF rectangle(thePoint.x() - 0.5 * brushWidth, thePoint.y() - 0.5 * brushWidth, brushWidth, brushWidth); - + QBrush brush; if (usingFeather) { QRadialGradient radialGrad(thePoint, 0.5 * brushWidth); setGaussianGradient(radialGrad, fillColor, opacity, mOffset); - - mBufferImg.drawEllipse(rectangle, Qt::NoPen, radialGrad, - QPainter::CompositionMode_SourceOver, false); + brush = radialGrad; } else { - mBufferImg.drawEllipse(rectangle, Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), - QPainter::CompositionMode_SourceOver, useAA); + brush = QBrush(fillColor, Qt::SolidPattern); } + mTiledBuffer.drawBrush(thePoint, brushWidth, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), Qt::NoPen, brush, compMode, useAA); +} + +void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) +{ + BlitRect blitRect; + + // In order to clear what was previously dirty, we need to include the previous buffer bound + // this ensures that we won't see stroke artifacts + blitRect.extend(mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect()); + + QRect updateRect = mEditor->view()->mapCanvasToScreen(path.boundingRect()).toRect(); + // Now extend with the new path bounds mapped to the local coordinate + blitRect.extend(updateRect); - update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); + mTiledBuffer.clear(); + mTiledBuffer.drawPath(path, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); + + // And update only the affected area + update(blitRect.adjusted(-1, -1, 1, 1)); +} + +void ScribbleArea::endStroke() +{ + if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP) { + paintBitmapBuffer(); + } + + onFrameModified(mEditor->currentFrame()); } void ScribbleArea::flipSelection(bool flipVertical) @@ -1354,12 +1382,10 @@ void ScribbleArea::blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF BitmapImage bmiSrcClip = bmiSource_->copy(srcRect.toAlignedRect()); BitmapImage bmiTmpClip = bmiSrcClip; // TODO: find a shorter way - bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, mPrefs->isOn(SETTING::ANTIALIAS)); + bmiTmpClip.drawRect(srcRect, Qt::NoPen, radialGrad, QPainter::CompositionMode_Source, true); bmiSrcClip.bounds().moveTo(trgRect.topLeft().toPoint()); bmiTmpClip.paste(&bmiSrcClip, QPainter::CompositionMode_SourceIn); - mBufferImg.paste(&bmiTmpClip); - - update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); + mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.bounds(), QPainter::CompositionMode_SourceOver, mPrefs->isOn(SETTING::ANTIALIAS)); } void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal mOffset_, qreal opacity_) @@ -1411,28 +1437,7 @@ void ScribbleArea::liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPoi } } } - mBufferImg.paste(&bmiTmpClip); - - update(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect().adjusted(-1, -1, 1, 1)); -} - -void ScribbleArea::drawPolyline(QPainterPath path, QPen pen, bool useAA) -{ - BlitRect blitRect; - - // In order to clear what was previous dirty, we need to include the previous buffer bound - // this ensures that we won't see stroke artifacts - blitRect.extend(mEditor->view()->mapCanvasToScreen(mBufferImg.bounds()).toRect()); - - QRect updateRect = mEditor->view()->mapCanvasToScreen(path.boundingRect()).toRect(); - // Now extend with the new path bounds mapped to the local coordinate - blitRect.extend(updateRect); - - mBufferImg.clear(); - mBufferImg.drawPath(path, pen, Qt::NoBrush, QPainter::CompositionMode_SourceOver, useAA); - - // And update only the affected area - update(blitRect.adjusted(-1, -1, 1, 1)); + mTiledBuffer.drawImage(*bmiTmpClip.image(), bmiTmpClip.bounds(), QPainter::CompositionMode_SourceOver, mPrefs->isOn(SETTING::ANTIALIAS)); } /************************************************************************************/ @@ -1480,7 +1485,7 @@ void ScribbleArea::applyTransformedSelection() mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); } - update(); + updateFrame(); } void ScribbleArea::cancelTransformedSelection() @@ -1508,37 +1513,7 @@ void ScribbleArea::cancelTransformedSelection() mOriginalPolygonF = QPolygonF(); mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); - updateCurrentFrame(); - } -} - -void ScribbleArea::displaySelectionProperties() -{ - Layer* layer = mEditor->layers()->currentLayer(); - if (layer == nullptr) { return; } - if (layer->type() == Layer::VECTOR) - { - VectorImage* vectorImage = currentVectorImage(layer); - if (vectorImage == nullptr) { return; } - //vectorImage->applySelectionTransformation(); - if (currentTool()->type() == MOVE) - { - int selectedCurve = vectorImage->getFirstSelectedCurve(); - if (selectedCurve != -1) - { - mEditor->tools()->setWidth(vectorImage->curve(selectedCurve).getWidth()); - mEditor->tools()->setFeather(vectorImage->curve(selectedCurve).getFeather()); - mEditor->tools()->setInvisibility(vectorImage->curve(selectedCurve).isInvisible()); - mEditor->tools()->setPressure(vectorImage->curve(selectedCurve).getVariableWidth()); - mEditor->color()->setColorNumber(vectorImage->curve(selectedCurve).getColorNumber()); - } - - int selectedArea = vectorImage->getFirstSelectedArea(); - if (selectedArea != -1) - { - mEditor->color()->setColorNumber(vectorImage->mArea[selectedArea].mColorNumber); - } - } + updateFrame(); } } @@ -1548,12 +1523,6 @@ void ScribbleArea::toggleThinLines() setEffect(SETTING::INVISIBLE_LINES, !previousValue); } -void ScribbleArea::toggleOutlines() -{ - mIsSimplified = !mIsSimplified; - setEffect(SETTING::OUTLINES, mIsSimplified); -} - void ScribbleArea::setLayerVisibility(LayerVisibility visibility) { mLayerVisibility = visibility; @@ -1586,21 +1555,6 @@ BaseTool* ScribbleArea::currentTool() const return editor()->tools()->currentTool(); } -BaseTool* ScribbleArea::getTool(ToolType eToolType) -{ - return editor()->tools()->getTool(eToolType); -} - -void ScribbleArea::setCurrentTool(ToolType eToolMode) -{ - Q_UNUSED(eToolMode) - - // change cursor - setCursor(currentTool()->cursor()); - updateCanvasCursor(); - updateCurrentFrame(); -} - void ScribbleArea::deleteSelection() { auto selectMan = mEditor->select(); @@ -1681,21 +1635,3 @@ void ScribbleArea::paletteColorChanged(QColor color) invalidateAllCache(); } - -void ScribbleArea::floodFillError(int errorType) -{ - QString message, error; - if (errorType == 1) { message = tr("There is a gap in your drawing (or maybe you have zoomed too much)."); } - if (errorType == 2 || errorType == 3) - { - message = tr("Sorry! This doesn't always work." - "Please try again (zoom a bit, click at another location... )
" - "if it doesn't work, zoom a bit and check that your paths are connected by pressing F1.)."); - } - - if (errorType == 1) { error = tr("Out of bound.", "Bucket tool fill error message"); } - if (errorType == 2) { error = tr("Could not find a closed path.", "Bucket tool fill error message"); } - if (errorType == 3) { error = tr("Could not find the root index.", "Bucket tool fill error message"); } - QMessageBox::warning(this, tr("Flood fill error"), tr("%1

Error: %2").arg(message, error), QMessageBox::Ok, QMessageBox::Ok); - mEditor->deselectAll(); -} diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 9f89da382e..c31ffca13b 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -39,6 +39,7 @@ GNU General Public License for more details. #include "strokemanager.h" #include "selectionpainter.h" #include "camerapainter.h" +#include "tiledbuffer.h" class Layer; class Editor; @@ -53,7 +54,6 @@ class ScribbleArea : public QWidget Q_OBJECT friend class MoveTool; - friend class EditTool; friend class SmudgeTool; friend class BucketTool; @@ -67,31 +67,23 @@ class ScribbleArea : public QWidget Editor* editor() const { return mEditor; } void deleteSelection(); - void displaySelectionProperties(); void applyTransformedSelection(); void cancelTransformedSelection(); bool isLayerPaintable() const; - QVector calcSelectionCenterPoints(); - void setEffect(SETTING e, bool isOn); LayerVisibility getLayerVisibility() const { return mLayerVisibility; } qreal getCurveSmoothing() const { return mCurveSmoothingLevel; } - bool usePressure() const { return mUsePressure; } bool makeInvisible() const { return mMakeInvisible; } - QRect getCameraRect(); QPointF getCentralPoint(); - /** Update current frame. - * calls update() behind the scene and update cache if necessary */ - void updateCurrentFrame(); /** Update frame. * calls update() behind the scene and update cache if necessary */ - void updateFrame(int frame); + void updateFrame(); /** Frame scrubbed, invalidate relevant cache */ void onScrubbed(int frameNumber); @@ -127,17 +119,11 @@ class ScribbleArea : public QWidget /** Tool changed, invalidate cache and frame if needed */ void onToolChanged(ToolType); - /** Set frame on layer to modified and invalidate current frame cache */ - void setModified(int layerNumber, int frameNumber); - void setModified(const Layer* layer, int frameNumber); + void endStroke(); void flipSelection(bool flipVertical); BaseTool* currentTool() const; - BaseTool* getTool(ToolType eToolMode); - void setCurrentTool(ToolType eToolMode); - - void floodFillError(int errorType); bool isMouseInUse() const { return mMouseInUse; } bool isTabletInUse() const { return mTabletInUse; } @@ -148,14 +134,12 @@ class ScribbleArea : public QWidget signals: void multiLayerOnionSkinChanged(bool); - void refreshPreview(); void selectionUpdated(); public slots: void clearImage(); void setCurveSmoothing(int); void toggleThinLines(); - void toggleOutlines(); void increaseLayerVisibilityIndex(); void decreaseLayerVisibilityIndex(); void setLayerVisibility(LayerVisibility visibility); @@ -164,6 +148,8 @@ public slots: void paletteColorChanged(QColor); void showLayerNotVisibleWarning(); + void onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile); + void onTileCreated(TiledBuffer* tiledBuffer, Tile* tile); protected: bool event(QEvent *event) override; @@ -180,17 +166,16 @@ public slots: public: void drawPolyline(QPainterPath path, QPen pen, bool useAA); - void drawLine(QPointF P1, QPointF P2, QPen pen, QPainter::CompositionMode cm); void drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter::CompositionMode cm); void drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA = true); void drawPencil(QPointF thePoint, qreal brushWidth, qreal fixedBrushFeather, QColor fillColor, qreal opacity); - void drawBrush(QPointF thePoint, qreal brushWidth, qreal offset, QColor fillColor, qreal opacity, bool usingFeather = true, bool useAA = false); + void drawBrush(QPointF thePoint, qreal brushWidth, qreal offset, QColor fillColor, QPainter::CompositionMode compMode, qreal opacity, bool usingFeather = true, bool useAA = false); void blurBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal offset_, qreal opacity_); void liquifyBrush(BitmapImage *bmiSource_, QPointF srcPoint_, QPointF thePoint_, qreal brushWidth_, qreal offset_, qreal opacity_); void paintBitmapBuffer(); void paintCanvasCursor(QPainter& painter); - void clearBitmapBuffer(); + void clearDrawingBuffer(); void setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset); void pointerPressEvent(PointerEvent*); @@ -203,10 +188,9 @@ public slots: /// on an empty frame, and if so, takes action according to use preference. void handleDrawingOnEmptyFrame(); - BitmapImage mBufferImg; // used to draw strokes for both bitmap and vector + TiledBuffer mTiledBuffer; QPixmap mCursorImg; - QPixmap mTransCursImg; private: @@ -231,7 +215,7 @@ public slots: void prepOverlays(int frame); void prepCameraPainter(int frame); - void prepCanvas(int frame, QRect rect); + void prepCanvas(int frame); void drawCanvas(int frame, QRect rect); void settingUpdated(SETTING setting); void paintSelectionVisuals(QPainter &painter); @@ -239,25 +223,16 @@ public slots: BitmapImage* currentBitmapImage(Layer* layer) const; VectorImage* currentVectorImage(Layer* layer) const; - MoveMode mMoveMode = MoveMode::NONE; - std::unique_ptr mStrokeManager; Editor* mEditor = nullptr; - - bool mIsSimplified = false; - bool mShowThinLines = false; bool mQuickSizing = true; LayerVisibility mLayerVisibility = LayerVisibility::ALL; - bool mUsePressure = true; bool mMakeInvisible = false; - bool mToolCursors = true; qreal mCurveSmoothingLevel = 0.0; - bool mMultiLayerOnionSkin = false; // future use. If required, just add a checkbox to updated it. - QColor mOnionColor; - -private: + bool mMultiLayerOnionSkin = false; // Future use. If required, just add a checkbox to update it. + int mDeltaFactor = 1; /* Under certain circumstances a mouse press event will fire after a tablet release event. This causes unexpected behaviours for some of the tools, eg. the bucket. @@ -267,9 +242,7 @@ public slots: The following will filter mouse events created after a tablet release event. */ void tabletReleaseEventFired(); - bool mKeyboardInUse = false; bool mMouseInUse = false; - bool mMouseRightButtonInUse = false; bool mTabletInUse = false; qreal mDevicePixelRatio = 1.; diff --git a/core_lib/src/managers/overlaymanager.cpp b/core_lib/src/managers/overlaymanager.cpp index 1f8a19e228..61f871dd9c 100644 --- a/core_lib/src/managers/overlaymanager.cpp +++ b/core_lib/src/managers/overlaymanager.cpp @@ -80,11 +80,11 @@ MoveMode OverlayManager::getMoveModeForPoint(const QPointF& pos, const QTransfor { mode = MoveMode::PERSP_SINGLE; } - else if (mTwoPointPerspectiveEnabled && QLineF(pos, transform.inverted().map(mLeftPerspectivePoint)).length() < calculatedSelectionTol) + else if ((mTwoPointPerspectiveEnabled || mThreePointPerspectiveEnabled) && QLineF(pos, transform.inverted().map(mLeftPerspectivePoint)).length() < calculatedSelectionTol) { mode = MoveMode::PERSP_LEFT; } - else if (mTwoPointPerspectiveEnabled && QLineF(pos, transform.inverted().map(mRightPerspectivePoint)).length() < calculatedSelectionTol) + else if ((mTwoPointPerspectiveEnabled || mThreePointPerspectiveEnabled) && QLineF(pos, transform.inverted().map(mRightPerspectivePoint)).length() < calculatedSelectionTol) { mode = MoveMode::PERSP_RIGHT; } @@ -101,23 +101,6 @@ double OverlayManager::selectionTolerance() return qAbs(mSelectionTolerance * mEditor->viewScaleInversed()); } -void OverlayManager::updatePerspective(int persp) -{ - switch (persp) { - case 1: - setMoveMode(MoveMode::PERSP_SINGLE); - break; - case 2: - setMoveMode(MoveMode::PERSP_LEFT); - break; - case 3: - setMoveMode(MoveMode::PERSP_LEFT); - break; - default: - break; - } -} - void OverlayManager::updatePerspective(const QPointF& point) { switch (mMoveMode) { diff --git a/core_lib/src/managers/overlaymanager.h b/core_lib/src/managers/overlaymanager.h index 97cf9df548..4fcba37b26 100644 --- a/core_lib/src/managers/overlaymanager.h +++ b/core_lib/src/managers/overlaymanager.h @@ -47,7 +47,6 @@ class OverlayManager : public BaseManager MoveMode getMoveModeForPoint(const QPointF& pos, const QTransform& transform); double selectionTolerance(); - void updatePerspective(const int persp); void updatePerspective(const QPointF& point); MoveMode getMoveMode() const { return mMoveMode; } diff --git a/core_lib/src/managers/preferencemanager.cpp b/core_lib/src/managers/preferencemanager.cpp index fe79aa87f9..229881a8fa 100644 --- a/core_lib/src/managers/preferencemanager.cpp +++ b/core_lib/src/managers/preferencemanager.cpp @@ -79,6 +79,9 @@ void PreferenceManager::loadPrefs() set(SETTING::QUICK_SIZING, settings.value(SETTING_QUICK_SIZING, true).toBool()); set(SETTING::SHOW_SELECTION_INFO, settings.value(SETTING_SHOW_SELECTION_INFO, false).toBool()); + set(SETTING::INVERT_DRAG_ZOOM_DIRECTION, settings.value(SETTING_INVERT_DRAG_ZOOM_DIRECTION, false).toBool()); + set(SETTING::INVERT_SCROLL_ZOOM_DIRECTION, settings.value(SETTING_INVERT_SCROLL_ZOOM_DIRECTION, false).toBool()); + set(SETTING::ROTATION_INCREMENT, settings.value(SETTING_ROTATION_INCREMENT, 15).toInt()); set(SETTING::WINDOW_OPACITY, settings.value(SETTING_WINDOW_OPACITY, 0).toInt()); @@ -436,6 +439,12 @@ void PreferenceManager::set(SETTING option, bool value) case SETTING::QUICK_SIZING: settings.setValue(SETTING_QUICK_SIZING, value); break; + case SETTING::INVERT_DRAG_ZOOM_DIRECTION: + settings.setValue(SETTING_INVERT_DRAG_ZOOM_DIRECTION, value); + break; + case SETTING::INVERT_SCROLL_ZOOM_DIRECTION: + settings.setValue(SETTING_INVERT_SCROLL_ZOOM_DIRECTION, value); + break; case SETTING::LAYOUT_LOCK: settings.setValue(SETTING_LAYOUT_LOCK, value); break; diff --git a/core_lib/src/managers/soundmanager.cpp b/core_lib/src/managers/soundmanager.cpp index 655d51307a..8bcc3843c5 100644 --- a/core_lib/src/managers/soundmanager.cpp +++ b/core_lib/src/managers/soundmanager.cpp @@ -68,57 +68,6 @@ Status SoundManager::save(Object*) return Status::OK; } -Status SoundManager::loadSound(Layer* soundLayer, int frameNumber, QString soundFilePath) -{ - Q_ASSERT(soundLayer); - if (soundLayer->type() != Layer::SOUND) - { - return Status::ERROR_INVALID_LAYER_TYPE; - } - - if (frameNumber < 0) - { - return Status::ERROR_INVALID_FRAME_NUMBER; - } - - if (!QFile::exists(soundFilePath)) - { - return Status::FILE_NOT_FOUND; - } - - KeyFrame* key = soundLayer->getKeyFrameAt(frameNumber); - if (key == nullptr) - { - key = new SoundClip; - soundLayer->addKeyFrame(frameNumber, key); - } - - if (!key->fileName().isEmpty()) - { - // file path should be empty. - // we can only load a audio clip to an empty key! - return Status::FAIL; - } - - QString strCopyFile = soundLayer->object()->copyFileToDataFolder(soundFilePath); - Q_ASSERT(!strCopyFile.isEmpty()); - - QString sOriginalName = QFileInfo(soundFilePath).fileName(); - - SoundClip* soundClip = dynamic_cast(key); - soundClip->init(strCopyFile); - soundClip->setSoundClipName(sOriginalName); - - Status st = createMediaPlayer(soundClip); - if (!st.ok()) - { - delete soundClip; - return st; - } - - return Status::OK; -} - Status SoundManager::loadSound(SoundClip* soundClip, QString strSoundFile) { Q_ASSERT(soundClip); diff --git a/core_lib/src/managers/soundmanager.h b/core_lib/src/managers/soundmanager.h index 31abc12d50..4085ad3b55 100644 --- a/core_lib/src/managers/soundmanager.h +++ b/core_lib/src/managers/soundmanager.h @@ -38,7 +38,6 @@ class SoundManager : public BaseManager Status load(Object*) override; Status save(Object*) override; - Status loadSound(Layer* soundLayer, int frameNumber, QString strSoundFile); Status loadSound(SoundClip* soundClip, QString strSoundFile); Status processSound(SoundClip* soundClip); diff --git a/core_lib/src/managers/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index add88969db..25c6ef2b5b 100644 --- a/core_lib/src/managers/toolmanager.cpp +++ b/core_lib/src/managers/toolmanager.cpp @@ -103,6 +103,9 @@ void ToolManager::setDefaultTool() void ToolManager::setCurrentTool(ToolType eToolType) { + // We're already using this tool + if (mCurrentTool == getTool(eToolType)) { return; } + if (mCurrentTool != nullptr) { mCurrentTool->leavingThisTool(); @@ -251,19 +254,8 @@ void ToolManager::setBucketFillExpand(int expandValue) emit toolPropertyChanged(currentTool()->type(), BUCKETFILLEXPAND); } -void ToolManager::setBucketFillToLayerMode(int layerMode) -{ - currentTool()->setFillToLayerMode(layerMode); - emit toolPropertyChanged(currentTool()->type(), BUCKETFILLLAYERMODE); -} - void ToolManager::setBucketFillReferenceMode(int referenceMode) { - // If the bucket reference mode is current layer, enforce fillTo is also set to current layer - if (bucketReferenceModeIsCurrentLayer(referenceMode)) { - currentTool()->setFillToLayerMode(0); - emit toolPropertyChanged(currentTool()->type(), BUCKETFILLLAYERMODE); - } currentTool()->setFillReferenceMode(referenceMode); emit toolPropertyChanged(currentTool()->type(), BUCKETFILLLAYERREFERENCEMODE); } @@ -334,6 +326,9 @@ int ToolManager::propertySwitch(bool condition, int tool) void ToolManager::tabletSwitchToEraser() { mTabletEraserTool = getTool(ERASER); + + // We should only notify a tool change if we're positive that the state has changed and it should only happen once + // if the user for some reason is using another temporary tool at the same time, that takes first priority if (mTemporaryTool == nullptr) { emit toolChanged(ERASER); @@ -342,9 +337,9 @@ void ToolManager::tabletSwitchToEraser() void ToolManager::tabletRestorePrevTool() { - mTabletEraserTool = nullptr; - if (mTemporaryTool == nullptr) + if (mTemporaryTool == nullptr && mTabletEraserTool != nullptr) { + mTabletEraserTool = nullptr; emit toolChanged(currentTool()->type()); } } @@ -417,7 +412,10 @@ void ToolManager::setTemporaryTool(ToolType eToolType) void ToolManager::clearTemporaryTool() { - mTemporaryTool = nullptr; + if (mTemporaryTool) { + mTemporaryTool->leavingThisTool(); + mTemporaryTool = nullptr; + } mTemporaryTriggerKeys = {}; mTemporaryTriggerModifiers = Qt::NoModifier; mTemporaryTriggerMouseButtons = Qt::NoButton; diff --git a/core_lib/src/managers/toolmanager.h b/core_lib/src/managers/toolmanager.h index 02373d2d47..6ba1cd3321 100644 --- a/core_lib/src/managers/toolmanager.h +++ b/core_lib/src/managers/toolmanager.h @@ -77,7 +77,6 @@ public slots: void setTolerance(int); void setBucketColorToleranceEnabled(bool enabled); void setBucketFillExpandEnabled(bool enabled); - void setBucketFillToLayerMode(int layerMode); void setBucketFillReferenceMode(int referenceMode); void setBucketFillExpand(int); void setUseFillContour(bool); diff --git a/core_lib/src/managers/viewmanager.cpp b/core_lib/src/managers/viewmanager.cpp index fd79d3ea05..9b85848ea8 100644 --- a/core_lib/src/managers/viewmanager.cpp +++ b/core_lib/src/managers/viewmanager.cpp @@ -171,6 +171,14 @@ void ViewManager::rotate(float degree) emit viewChanged(); } +void ViewManager::rotateRelative(float delta) +{ + mRotation = mRotation + delta; + updateViewTransforms(); + + emit viewChanged(); +} + void ViewManager::resetRotation() { rotate(0); @@ -197,11 +205,12 @@ void ViewManager::scaleUp() void ViewManager::scaleDown() { - for (size_t i = gZoomLevels.size() - 1; i >= 0; --i) + const size_t nZoomLevels = gZoomLevels.size(); + for (size_t i = 1; i <= nZoomLevels; i++) { - if (mScaling > gZoomLevels[i]) + if (mScaling > gZoomLevels[nZoomLevels - i]) { - scale(gZoomLevels[i]); + scale(gZoomLevels[nZoomLevels - i]); return; } } diff --git a/core_lib/src/managers/viewmanager.h b/core_lib/src/managers/viewmanager.h index 3a8d2d12d4..1c0009c4d5 100644 --- a/core_lib/src/managers/viewmanager.h +++ b/core_lib/src/managers/viewmanager.h @@ -58,6 +58,7 @@ class ViewManager : public BaseManager float rotation(); void rotate(float degree); + void rotateRelative(float delta); void resetRotation(); qreal scaling(); diff --git a/core_lib/src/movieexporter.cpp b/core_lib/src/movieexporter.cpp index 08d52898ce..a32726a409 100644 --- a/core_lib/src/movieexporter.cpp +++ b/core_lib/src/movieexporter.cpp @@ -27,6 +27,7 @@ GNU General Public License for more details. #include #include #include +#include #include "object.h" #include "layercamera.h" @@ -34,6 +35,12 @@ GNU General Public License for more details. #include "soundclip.h" #include "util.h" +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) +using Qt::SplitBehaviorFlags; +#else +using SplitBehaviorFlags = QString::SplitBehavior; +#endif + MovieExporter::MovieExporter() { } @@ -352,7 +359,7 @@ Status MovieExporter::generateMovie( // Run FFmpeg command - STATUS_CHECK(executeFFMpegPipe(ffmpegPath, args, progress, [&](QProcess& ffmpeg, int framesProcessed) + Status status = executeFFMpegPipe(ffmpegPath, args, progress, [&](QProcess& ffmpeg, int framesProcessed) { if(framesProcessed < 0) { @@ -377,10 +384,13 @@ Status MovieExporter::generateMovie( obj->paintImage(painter, currentFrame, false, true); painter.end(); - // Should use sizeInBytes instead of byteCount to support large images, - // but this is only supported in QT 5.10+ +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + int bytesWritten = ffmpeg.write(reinterpret_cast(imageToExport.constBits()), imageToExport.sizeInBytes()); + Q_ASSERT(bytesWritten == imageToExport.sizeInBytes()); +#else int bytesWritten = ffmpeg.write(reinterpret_cast(imageToExport.constBits()), imageToExport.byteCount()); Q_ASSERT(bytesWritten == imageToExport.byteCount()); +#endif currentFrame++; failCounter = 0; @@ -388,7 +398,8 @@ Status MovieExporter::generateMovie( } return false; - })); + }); + STATUS_CHECK(status); return Status::OK; } @@ -468,7 +479,7 @@ Status MovieExporter::generateGif( // Run FFmpeg command - STATUS_CHECK(executeFFMpegPipe(ffmpegPath, args, progress, [&](QProcess& ffmpeg, int framesProcessed) + Status status = executeFFMpegPipe(ffmpegPath, args, progress, [&](QProcess& ffmpeg, int framesProcessed) { /* The GIF FFmpeg command requires the entires stream to be * written before FFmpeg can encode the GIF. This is because @@ -494,13 +505,19 @@ Status MovieExporter::generateGif( obj->paintImage(painter, currentFrame, false, true); +#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + bytesWritten = ffmpeg.write(reinterpret_cast(imageToExport.constBits()), imageToExport.sizeInBytes()); + Q_ASSERT(bytesWritten == imageToExport.sizeInBytes()); +#else bytesWritten = ffmpeg.write(reinterpret_cast(imageToExport.constBits()), imageToExport.byteCount()); Q_ASSERT(bytesWritten == imageToExport.byteCount()); +#endif currentFrame++; return true; - })); + }); + STATUS_CHECK(status); return Status::OK; } @@ -541,7 +558,7 @@ Status MovieExporter::executeFFmpeg(const QString& cmd, const QStringList& args, if(!ffmpeg.waitForReadyRead()) break; QString output(ffmpeg.readAll()); - QStringList sList = output.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + QStringList sList = output.split(QRegularExpression("[\r\n]"), SplitBehaviorFlags::SkipEmptyParts); for (const QString& s : sList) { qDebug() << "[ffmpeg]" << s; @@ -565,7 +582,7 @@ Status MovieExporter::executeFFmpeg(const QString& cmd, const QStringList& args, } QString output(ffmpeg.readAll()); - QStringList sList = output.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + QStringList sList = output.split(QRegularExpression("[\r\n]"), SplitBehaviorFlags::SkipEmptyParts); for (const QString& s : sList) { qDebug() << "[ffmpeg]" << s; @@ -671,7 +688,7 @@ Status MovieExporter::executeFFMpegPipe(const QString& cmd, const QStringList& a if(ffmpeg.waitForReadyRead(10)) { QString output(ffmpeg.readAll()); - QStringList sList = output.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + QStringList sList = output.split(QRegularExpression("[\r\n]"), SplitBehaviorFlags::SkipEmptyParts); for (const QString& s : sList) { qDebug() << "[ffmpeg]" << s; @@ -702,7 +719,7 @@ Status MovieExporter::executeFFMpegPipe(const QString& cmd, const QStringList& a } QString output(ffmpeg.readAll()); - QStringList sList = output.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + QStringList sList = output.split(QRegularExpression("[\r\n]"), SplitBehaviorFlags::SkipEmptyParts); for (const QString& s : sList) { qDebug() << "[ffmpeg]" << s; diff --git a/core_lib/src/movieimporter.cpp b/core_lib/src/movieimporter.cpp index e4cdd89ca7..d9389bf5ae 100644 --- a/core_lib/src/movieimporter.cpp +++ b/core_lib/src/movieimporter.cpp @@ -19,6 +19,7 @@ GNU General Public License for more details. #include #include #include +#include #include #include #include @@ -121,7 +122,11 @@ Status MovieImporter::estimateFrames(const QString &filePath, int fps, int *fram if (!ffmpeg.waitForReadyRead()) break; QString output(ffmpeg.readAll()); - QStringList sList = output.split(QRegExp("[\r\n]"), QString::SkipEmptyParts); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QStringList sList = output.split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); +#else + QStringList sList = output.split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); +#endif for (const QString& s : sList) { index = s.indexOf("Duration: "); diff --git a/core_lib/src/overlaypainter.cpp b/core_lib/src/overlaypainter.cpp index a969cb96b4..b1cd48010c 100644 --- a/core_lib/src/overlaypainter.cpp +++ b/core_lib/src/overlaypainter.cpp @@ -3,10 +3,10 @@ #include "layercamera.h" #include "camera.h" #include "layer.h" +#include "util.h" -Q_CONSTEXPR static qreal LINELENGTHFACTOR = 2.0; -Q_CONSTEXPR static int LEFTANGLEOFFSET = 90; -Q_CONSTEXPR static int RIGHTANGLEOFFSET = -90; +Q_CONSTEXPR static int LEFT_ANGLE_OFFSET = 90; +Q_CONSTEXPR static int RIGHT_ANGLE_OFFSET = -90; Q_CONSTEXPR static int HANDLE_WIDTH = 12; OverlayPainter::OverlayPainter() @@ -33,7 +33,7 @@ void OverlayPainter::setViewTransform(const QTransform view) mViewTransform = view; } -void OverlayPainter::paint(QPainter &painter) +void OverlayPainter::paint(QPainter &painter, const QRect& viewport) { if (mCameraLayer == nullptr) { return; } @@ -65,17 +65,18 @@ void OverlayPainter::paint(QPainter &painter) paintOverlaySafeAreas(painter, *camera, camTransform, cameraRect); } + const QRect mappedViewport = mViewTransform.inverted().mapRect(viewport); if (mOptions.bPerspective1) { - paintOverlayPerspectiveOnePoint(painter, camTransform, cameraRect); + paintOverlayPerspectiveOnePoint(painter, mappedViewport, camTransform); } - if (mOptions.bPerspective2) + if (mOptions.bPerspective2 || mOptions.bPerspective3) { - paintOverlayPerspectiveTwoPoints(painter, *camera, camTransform, cameraRect); + paintOverlayPerspectiveTwoPoints(painter, mappedViewport, *camera, camTransform); } if (mOptions.bPerspective3) { - paintOverlayPerspectiveThreePoints(painter, *camera, camTransform, cameraRect); + paintOverlayPerspectiveThreePoints(painter, mappedViewport, *camera, camTransform); } if (mOptions.bGrid) @@ -211,7 +212,7 @@ void OverlayPainter::paintOverlaySafeAreas(QPainter &painter, const Camera& came QTransform t = scale.inverted() * rot * trans; painter.setTransform(t, true); - painter.drawText(QPoint(), QObject::tr("Safe Action area %1 %").arg(action)); + painter.drawText(QPoint(), tr("Safe Action area %1 %").arg(action)); painter.restore(); } } @@ -238,45 +239,35 @@ void OverlayPainter::paintOverlaySafeAreas(QPainter &painter, const Camera& came QTransform t = scale.inverted() * rot * trans; painter.setTransform(t, true); - painter.drawText(QPoint(), QObject::tr("Safe Title area %1 %").arg(title)); + painter.drawText(QPoint(), tr("Safe Title area %1 %").arg(title)); painter.restore(); } } painter.restore(); } -void OverlayPainter::paintOverlayPerspectiveOnePoint(QPainter& painter, const QTransform& camTransform, const QRect& camRect) const +void OverlayPainter::paintOverlayPerspectiveOnePoint(QPainter& painter, const QRect& viewport, const QTransform& camTransform) const { - painter.save(); - painter.setRenderHint(QPainter::Antialiasing, true); - qreal degrees = static_cast(mOptions.nOverlayAngle); if (degrees == 7.0) { degrees = 7.5; } - int repeats = static_cast(360 / degrees); - QLineF angleLine; QPointF singlePoint = camTransform.inverted().map(mOptions.mSinglePerspPoint); - if (singlePoint == QPointF(0, 0)) - { - // TODO: bug in Qt prevents points from being (0,0)... - singlePoint = QPointF(0.1, 0.1); - } + QLineF angleLine(singlePoint.x(), singlePoint.y(), singlePoint.x() + 1, singlePoint.y()); - angleLine.setP1(singlePoint); QVector lines; - for (int i = 0; i < repeats; i++) + for (qreal angle = 0; angle < 180; angle += degrees) { - angleLine.setAngle(i * degrees); - angleLine.setLength(camRect.width() * 2.0); - lines.append(angleLine); + angleLine.setAngle(angle); + lines.append(clipLine(angleLine, viewport, -qInf(), qInf())); } - painter.drawLines(lines); - - painter.setWorldMatrixEnabled(false); - singlePoint = mViewTransform.map(singlePoint); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.drawLines(lines); if (mOptions.bShowHandle) { + singlePoint = mViewTransform.map(singlePoint); + painter.setWorldMatrixEnabled(false); painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.setPen(mPalette.color(QPalette::HighlightedText)); painter.setBrush(mPalette.color(QPalette::Highlight)); @@ -284,57 +275,35 @@ void OverlayPainter::paintOverlayPerspectiveOnePoint(QPainter& painter, const QT } painter.restore(); - } -void OverlayPainter::paintOverlayPerspectiveTwoPoints(QPainter& painter, const Camera& camera, const QTransform& camTransform, const QRect& camRect) const +void OverlayPainter::paintOverlayPerspectiveTwoPoints(QPainter& painter, const QRect& viewport, const Camera& camera, const QTransform& camTransform) const { - painter.save(); - painter.setRenderHint(QPainter::Antialiasing, true); - qreal degrees = static_cast(mOptions.nOverlayAngle); if (degrees == 7.0) { degrees = 7.5; } - int repeats = static_cast(180 / degrees); QPointF leftPoint = camTransform.inverted().map(mOptions.mLeftPerspPoint); QPointF rightPoint = camTransform.inverted().map(mOptions.mRightPerspPoint); - if (leftPoint == QPointF(0.0, 0.0)) - { - // TODO: bug in Qt prevents points from being (0,0)... - leftPoint = QPointF(0.1, 0.1); - } + QLineF angleLineLeft(leftPoint.x(), leftPoint.y(), leftPoint.x() + 1, leftPoint.y()); + QLineF angleLineRight(rightPoint.x(), rightPoint.y(), rightPoint.x() + 1, rightPoint.y()); - if (rightPoint == QPointF(0.0, 0.0)) - { - // TODO: bug in Qt prevents points from being (0,0)... - rightPoint = QPointF(0.1, 0.1); - } - - QLineF angleLineLeft; - QLineF angleLineRight; - angleLineLeft.setAngle(LEFTANGLEOFFSET); - angleLineLeft.setP1(leftPoint); - angleLineLeft.setLength(camRect.width() * LINELENGTHFACTOR); - angleLineRight.setAngle(RIGHTANGLEOFFSET); - angleLineRight.setP1(rightPoint); - angleLineRight.setLength(camRect.width() * LINELENGTHFACTOR); QVector lines; - for (int i = 0; i <= repeats; i++) + for (qreal angle = 0; angle <= 180; angle += degrees) { - angleLineLeft.setAngle((LEFTANGLEOFFSET - i * degrees) + camera.rotation()); - angleLineRight.setAngle((RIGHTANGLEOFFSET - i * degrees) + camera.rotation()); - lines.append(angleLineRight); - lines.append(angleLineLeft); + angleLineLeft.setAngle(LEFT_ANGLE_OFFSET - angle + camera.rotation()); + angleLineRight.setAngle(RIGHT_ANGLE_OFFSET - angle + camera.rotation()); + lines.append(clipLine(angleLineLeft, viewport, 0, qInf())); + lines.append(clipLine(angleLineRight, viewport, 0, qInf())); } + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); painter.drawLines(lines); - painter.setWorldMatrixEnabled(false); - - leftPoint = mViewTransform.map(leftPoint); - rightPoint = mViewTransform.map(rightPoint); - if (mOptions.bShowHandle) { + leftPoint = mViewTransform.map(leftPoint); + rightPoint = mViewTransform.map(rightPoint); + painter.setWorldMatrixEnabled(false); painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.setPen(mPalette.color(QPalette::HighlightedText)); painter.setBrush(mPalette.color(QPalette::Highlight)); @@ -345,44 +314,29 @@ void OverlayPainter::paintOverlayPerspectiveTwoPoints(QPainter& painter, const C painter.restore(); } -void OverlayPainter::paintOverlayPerspectiveThreePoints(QPainter& painter, const Camera& camera, const QTransform& camTransform, const QRect& camRect) const +void OverlayPainter::paintOverlayPerspectiveThreePoints(QPainter& painter, const QRect& viewport, const Camera& camera, const QTransform& camTransform) const { - if (!mOptions.bPerspective2) - paintOverlayPerspectiveTwoPoints(painter, camera, camTransform, camRect); - - painter.save(); - painter.setRenderHint(QPainter::Antialiasing, true); - qreal degrees = static_cast(mOptions.nOverlayAngle); if (degrees == 7.0) { degrees = 7.5; } - int repeats = static_cast(180 / degrees); QPointF middlePoint = camTransform.inverted().map(mOptions.mMiddlePerspPoint); - if (middlePoint == QPointF(0.0, 0.0)) - { - // TODO: bug in Qt prevents points from being (0,0)... - middlePoint = QPointF(0.1, 0.1); - } + QLineF angleLine(middlePoint.x(), middlePoint.y(), middlePoint.x() + 1, middlePoint.y()); const int middleAngleOffset = mOptions.mLeftPerspPoint.y() < mOptions.mMiddlePerspPoint.y() ? 180 : 0; - - QLineF angleLine; - angleLine.setAngle(middleAngleOffset); - angleLine.setP1(middlePoint); - angleLine.setLength(camRect.width() * LINELENGTHFACTOR); QVector lines; - for (int i = 0; i <= repeats; i++) + for (qreal angle = 0; angle <= 180; angle += degrees) { - angleLine.setAngle((middleAngleOffset - i * degrees) + camera.rotation()); - lines.append(angleLine); + angleLine.setAngle(middleAngleOffset - angle + camera.rotation()); + lines.append(clipLine(angleLine, viewport, 0, qInf())); } - painter.drawLines(lines); - painter.setWorldMatrixEnabled(false); - - middlePoint = mViewTransform.map(middlePoint); + painter.save(); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.drawLines(lines); if (mOptions.bShowHandle) { + middlePoint = mViewTransform.map(middlePoint); + painter.setWorldMatrixEnabled(false); painter.setCompositionMode(QPainter::CompositionMode_SourceOver); painter.setPen(mPalette.color(QPalette::HighlightedText)); painter.setBrush(mPalette.color(QPalette::Highlight)); diff --git a/core_lib/src/overlaypainter.h b/core_lib/src/overlaypainter.h index b667625136..ce96f8f48f 100644 --- a/core_lib/src/overlaypainter.h +++ b/core_lib/src/overlaypainter.h @@ -32,8 +32,6 @@ struct OverlayPainterOptions QPointF mLeftPerspPoint; QPointF mRightPerspPoint; QPointF mMiddlePerspPoint; - - QPainter::CompositionMode cmBufferBlendMode = QPainter::CompositionMode_SourceOver; }; class OverlayPainter @@ -47,7 +45,7 @@ class OverlayPainter void preparePainter(const LayerCamera* cameraLayer, const QPalette& palette); - void paint(QPainter& painter); + void paint(QPainter& painter, const QRect& viewport); private: void initializePainter(QPainter& painter); @@ -56,9 +54,9 @@ class OverlayPainter void paintOverlayThirds(QPainter& painter, const QTransform& camTransform, const QRect& camRect) const; void paintOverlayGolden(QPainter& painter, const QTransform& camTransform, const QRect& camRect) const; void paintOverlaySafeAreas(QPainter& painter, const Camera& camera, const QTransform& camTransform, const QRect& camRect) const; - void paintOverlayPerspectiveOnePoint(QPainter& painter, const QTransform& camTransform, const QRect& camRect) const; - void paintOverlayPerspectiveTwoPoints(QPainter& painter, const Camera& camera, const QTransform& camTransform, const QRect& camRect) const; - void paintOverlayPerspectiveThreePoints(QPainter& painter, const Camera& camera, const QTransform& camTransform, const QRect& camRect) const; + void paintOverlayPerspectiveOnePoint(QPainter& painter, const QRect& viewport, const QTransform& camTransform) const; + void paintOverlayPerspectiveTwoPoints(QPainter& painter, const QRect& viewport, const Camera& camera, const QTransform& camTransform) const; + void paintOverlayPerspectiveThreePoints(QPainter& painter, const QRect& viewport, const Camera& camera, const QTransform& camTransform) const; int round100(double f, int gridSize) const; diff --git a/core_lib/src/soundplayer.cpp b/core_lib/src/soundplayer.cpp index 278c4abcc6..6120b67b03 100644 --- a/core_lib/src/soundplayer.cpp +++ b/core_lib/src/soundplayer.cpp @@ -15,6 +15,7 @@ GNU General Public License for more details. */ #include "soundplayer.h" +#include #include #include #include "soundclip.h" @@ -44,7 +45,12 @@ void SoundPlayer::init(SoundClip* clip) mBuffer.setData(file.readAll()); mBuffer.open(QBuffer::ReadOnly); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + mMediaPlayer->setAudioOutput(new QAudioOutput(this)); + mMediaPlayer->setSourceDevice(&mBuffer, QUrl::fromLocalFile(clip->fileName())); +#else mMediaPlayer->setMedia(QUrl::fromLocalFile(clip->fileName()), &mBuffer); +#endif makeConnections(); clip->attachPlayer(this); @@ -107,8 +113,12 @@ void SoundPlayer::setMediaPlayerPosition(qint64 pos) void SoundPlayer::makeConnections() { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + connect(mMediaPlayer, &QMediaPlayer::errorOccurred, this, [](QMediaPlayer::Error err, const QString&) +#else auto errorSignal = static_cast(&QMediaPlayer::error); connect(mMediaPlayer, errorSignal, this, [](QMediaPlayer::Error err) +#endif { qDebug() << "MediaPlayer Error: " << err; }); diff --git a/core_lib/src/structure/filemanager.cpp b/core_lib/src/structure/filemanager.cpp index a24db42fdf..ae9ddd1127 100644 --- a/core_lib/src/structure/filemanager.cpp +++ b/core_lib/src/structure/filemanager.cpp @@ -51,7 +51,6 @@ Object* FileManager::load(const QString& sFileName) // Test file format: new zipped .pclx or old .pcl? bool isArchive = isArchiveFormat(sFileName); - QString isArchiveStr = "Is archive: " + QString(isArchive); if (!isArchive) { @@ -837,7 +836,7 @@ Status FileManager::recoverObject(Object* object) file.close(); QDomDocument xmlDoc; - mainXmlOK &= xmlDoc.setContent(&file); + mainXmlOK &= !!xmlDoc.setContent(&file); QDomDocumentType type = xmlDoc.doctype(); mainXmlOK &= (type.name() == "PencilDocument" || type.name() == "MyObject"); @@ -846,7 +845,7 @@ Status FileManager::recoverObject(Object* object) mainXmlOK &= (!root.isNull()); QDomElement objectTag = root.firstChildElement("object"); - mainXmlOK &= (objectTag.isNull() == false); + mainXmlOK &= (!objectTag.isNull()); if (mainXmlOK == false) { diff --git a/core_lib/src/structure/layer.cpp b/core_lib/src/structure/layer.cpp index 1e5f435538..f272e59120 100644 --- a/core_lib/src/structure/layer.cpp +++ b/core_lib/src/structure/layer.cpp @@ -22,7 +22,6 @@ GNU General Public License for more details. #include #include #include "keyframe.h" -#include "object.h" // Used to sort the selected frames list bool sortAsc(int left, int right) @@ -30,15 +29,14 @@ bool sortAsc(int left, int right) return left < right; } -Layer::Layer(Object* object, LAYER_TYPE eType) +Layer::Layer(int id, LAYER_TYPE eType) { Q_ASSERT(eType != UNDEFINED); - mObject = object; meType = eType; mName = QString(tr("Undefined Layer")); - mId = object->getUniqueLayerID(); + mId = id; } Layer::~Layer() @@ -51,13 +49,6 @@ Layer::~Layer() mKeyFrames.clear(); } -void Layer::setObject(Object* obj) -{ - Q_ASSERT(obj); - mObject = obj; - mId = mObject->getUniqueLayerID(); -} - void Layer::foreachKeyFrame(std::function action) const { for (auto pair : mKeyFrames) @@ -180,7 +171,7 @@ bool Layer::addNewKeyFrameAt(int position) { if (position <= 0) return false; - KeyFrame* key = createKeyFrame(position, mObject); + KeyFrame* key = createKeyFrame(position); return addKeyFrame(position, key); } diff --git a/core_lib/src/structure/layer.h b/core_lib/src/structure/layer.h index bbb857cf87..0e82e2b1db 100644 --- a/core_lib/src/structure/layer.h +++ b/core_lib/src/structure/layer.h @@ -28,11 +28,10 @@ class QMouseEvent; class QPainter; class KeyFrame; -class Object; class TimeLineCells; class Status; -#define ProgressCallback std::function +typedef std::function ProgressCallback; class Layer : public QObject { @@ -49,15 +48,13 @@ class Layer : public QObject CAMERA = 5, }; - explicit Layer(Object*, LAYER_TYPE); + explicit Layer(int id, LAYER_TYPE eType); ~Layer() override; int id() const { return mId; } + void setId(int layerId) { mId = layerId; } LAYER_TYPE type() const { return meType; } - Object* object() const { return mObject; } - void setObject(Object* obj); - void setName(QString name) { mName = name; } QString name() const { return mName; } @@ -168,15 +165,13 @@ class Layer : public QObject void clearDirtyFrames() { mDirtyFrames.clear(); } protected: - void setId(int LayerId) { mId = LayerId; } - virtual KeyFrame* createKeyFrame(int position, Object*) = 0; + virtual KeyFrame* createKeyFrame(int position) = 0; bool loadKey(KeyFrame*); private: void removeFromSelectionList(int position); LAYER_TYPE meType = UNDEFINED; - Object* mObject = nullptr; int mId = 0; bool mVisible = true; QString mName; diff --git a/core_lib/src/structure/layerbitmap.cpp b/core_lib/src/structure/layerbitmap.cpp index 1194640462..d5eb0cc620 100644 --- a/core_lib/src/structure/layerbitmap.cpp +++ b/core_lib/src/structure/layerbitmap.cpp @@ -23,7 +23,7 @@ GNU General Public License for more details. #include "bitmapimage.h" -LayerBitmap::LayerBitmap(Object* object) : Layer(object, Layer::BITMAP) +LayerBitmap::LayerBitmap(int id) : Layer(id, Layer::BITMAP) { setName(tr("Bitmap Layer")); } @@ -99,7 +99,7 @@ Status LayerBitmap::saveKeyFrameFile(KeyFrame* keyframe, QString path) return Status::OK; } -KeyFrame* LayerBitmap::createKeyFrame(int position, Object*) +KeyFrame* LayerBitmap::createKeyFrame(int position) { BitmapImage* b = new BitmapImage; b->setPos(position); diff --git a/core_lib/src/structure/layerbitmap.h b/core_lib/src/structure/layerbitmap.h index 4865dd64a2..8ae10fe1ad 100644 --- a/core_lib/src/structure/layerbitmap.h +++ b/core_lib/src/structure/layerbitmap.h @@ -27,7 +27,7 @@ class LayerBitmap : public Layer Q_OBJECT public: - LayerBitmap(Object* object); + explicit LayerBitmap(int id); ~LayerBitmap() override; QDomElement createDomElement(QDomDocument& doc) const override; @@ -42,7 +42,7 @@ class LayerBitmap : public Layer protected: Status saveKeyFrameFile(KeyFrame*, QString strPath) override; - KeyFrame* createKeyFrame(int position, Object*) override; + KeyFrame* createKeyFrame(int position) override; private: void loadImageAtFrame(QString strFilePath, QPoint topLeft, int frameNumber, qreal opacity); diff --git a/core_lib/src/structure/layercamera.cpp b/core_lib/src/structure/layercamera.cpp index 1ff1fbc354..b072954e0f 100644 --- a/core_lib/src/structure/layercamera.cpp +++ b/core_lib/src/structure/layercamera.cpp @@ -22,7 +22,7 @@ GNU General Public License for more details. #include "camera.h" #include "pencildef.h" -LayerCamera::LayerCamera(Object* object) : Layer(object, Layer::CAMERA) +LayerCamera::LayerCamera(int id) : Layer(id, Layer::CAMERA) { setName(tr("Camera Layer")); @@ -306,14 +306,18 @@ void LayerCamera::mergeControlPointIfNeeded(int frame) const const QLineF& interpolatedLinePC = QLineF(-camPrev->translation(), camPrev->getPathControlPoint()); QPointF mergedCPoint; +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + auto intersection = interpolatedLinePC.intersects(interpolatedLineCN, &mergedCPoint); +#else auto intersection = interpolatedLinePC.intersect(interpolatedLineCN, &mergedCPoint); +#endif // Try to recover the control point if the distance is within the threshold, otherwise do nothing - if (intersection == QLineF::IntersectType::UnboundedIntersection && + if (intersection == QLineF::UnboundedIntersection && QLineF(camFrame->getPathControlPoint(), mergedCPoint).length() < mControlPointMergeThreshold) { camPrev->setPathControlPoint(mergedCPoint); camPrev->setPathControlPointMoved(true); - } else if (intersection == QLineF::IntersectType::NoIntersection) { + } else if (intersection == QLineF::NoIntersection) { camPrev->setPathControlPointMoved(false); } } @@ -535,7 +539,7 @@ Status LayerCamera::saveKeyFrameFile(KeyFrame*, QString) return Status::OK; } -KeyFrame* LayerCamera::createKeyFrame(int position, Object*) +KeyFrame* LayerCamera::createKeyFrame(int position) { Camera* c = new Camera; c->setPos(position); diff --git a/core_lib/src/structure/layercamera.h b/core_lib/src/structure/layercamera.h index 0ef4cc151d..54773ecb6b 100644 --- a/core_lib/src/structure/layercamera.h +++ b/core_lib/src/structure/layercamera.h @@ -29,7 +29,7 @@ class Camera; class LayerCamera : public Layer { public: - explicit LayerCamera(Object* object); + explicit LayerCamera(int id); ~LayerCamera() override; void loadImageAtFrame(int frame, qreal dx, qreal dy, qreal rotate, qreal scale, CameraEasingType easing, const QPointF& pathPoint, bool pathMoved); @@ -70,7 +70,7 @@ class LayerCamera : public Layer protected: Status saveKeyFrameFile(KeyFrame*, QString path) override; - KeyFrame* createKeyFrame(int position, Object*) override; + KeyFrame* createKeyFrame(int position) override; private: void linearInterpolateTransform(Camera*); diff --git a/core_lib/src/structure/layersound.cpp b/core_lib/src/structure/layersound.cpp index 83b46bc502..c3c88751de 100644 --- a/core_lib/src/structure/layersound.cpp +++ b/core_lib/src/structure/layersound.cpp @@ -20,11 +20,10 @@ GNU General Public License for more details. #include #include #include -#include "object.h" #include "soundclip.h" -LayerSound::LayerSound(Object* object) : Layer(object, Layer::SOUND) +LayerSound::LayerSound(int id) : Layer(id, Layer::SOUND) { setName(tr("Sound Layer")); } @@ -155,7 +154,7 @@ Status LayerSound::saveKeyFrameFile(KeyFrame* key, QString path) return Status::OK; } -KeyFrame* LayerSound::createKeyFrame(int position, Object*) +KeyFrame* LayerSound::createKeyFrame(int position) { SoundClip* s = new SoundClip; s->setPos(position); diff --git a/core_lib/src/structure/layersound.h b/core_lib/src/structure/layersound.h index 718c6fc383..ddcf8c6fa1 100644 --- a/core_lib/src/structure/layersound.h +++ b/core_lib/src/structure/layersound.h @@ -27,7 +27,7 @@ class LayerSound : public Layer Q_OBJECT public: - LayerSound( Object* object ); + explicit LayerSound(int id); ~LayerSound(); QDomElement createDomElement(QDomDocument& doc) const override; void loadDomElement(const QDomElement& element, QString dataDirPath, ProgressCallback progressStep) override; @@ -39,7 +39,7 @@ class LayerSound : public Layer protected: Status saveKeyFrameFile(KeyFrame*, QString path) override; - KeyFrame* createKeyFrame(int position, Object*) override; + KeyFrame* createKeyFrame(int position) override; }; #endif diff --git a/core_lib/src/structure/layervector.cpp b/core_lib/src/structure/layervector.cpp index 57ea14443c..3efd51e9f0 100644 --- a/core_lib/src/structure/layervector.cpp +++ b/core_lib/src/structure/layervector.cpp @@ -22,7 +22,7 @@ GNU General Public License for more details. #include -LayerVector::LayerVector(Object* object) : Layer(object, Layer::VECTOR) +LayerVector::LayerVector(int id) : Layer(id, Layer::VECTOR) { setName(tr("Vector Layer")); } @@ -70,7 +70,6 @@ void LayerVector::loadImageAtFrame(QString path, int frameNumber) } VectorImage* vecImg = new VectorImage; vecImg->setPos(frameNumber); - vecImg->setObject(object()); vecImg->read(path); addKeyFrame(frameNumber, vecImg); } @@ -106,11 +105,10 @@ Status LayerVector::saveKeyFrameFile(KeyFrame* keyFrame, QString path) return Status::OK; } -KeyFrame* LayerVector::createKeyFrame(int position, Object* obj) +KeyFrame* LayerVector::createKeyFrame(int position) { VectorImage* v = new VectorImage; v->setPos(position); - v->setObject(obj); return v; } diff --git a/core_lib/src/structure/layervector.h b/core_lib/src/structure/layervector.h index 563a1800b9..163cdc0a45 100644 --- a/core_lib/src/structure/layervector.h +++ b/core_lib/src/structure/layervector.h @@ -27,7 +27,7 @@ class LayerVector : public Layer Q_OBJECT public: - LayerVector(Object* object); + explicit LayerVector(int id); ~LayerVector(); // method from layerImage @@ -45,7 +45,7 @@ class LayerVector : public Layer protected: Status saveKeyFrameFile(KeyFrame*, QString path) override; - KeyFrame* createKeyFrame(int position, Object*) override; + KeyFrame* createKeyFrame(int position) override; private: QString fileName(KeyFrame* key) const; diff --git a/core_lib/src/structure/object.cpp b/core_lib/src/structure/object.cpp index 0a5871853a..bb25cdb1e0 100644 --- a/core_lib/src/structure/object.cpp +++ b/core_lib/src/structure/object.cpp @@ -25,6 +25,7 @@ GNU General Public License for more details. #include #include #include +#include #include "layer.h" #include "layerbitmap.h" @@ -96,16 +97,16 @@ bool Object::loadXML(const QDomElement& docElem, ProgressCallback progressForwar switch (element.attribute("type").toInt()) { case Layer::BITMAP: - newLayer = new LayerBitmap(this); + newLayer = new LayerBitmap(getUniqueLayerID()); break; case Layer::VECTOR: - newLayer = new LayerVector(this); + newLayer = new LayerVector(getUniqueLayerID()); break; case Layer::SOUND: - newLayer = new LayerSound(this); + newLayer = new LayerSound(getUniqueLayerID()); break; case Layer::CAMERA: - newLayer = new LayerCamera(this); + newLayer = new LayerCamera(getUniqueLayerID()); break; default: Q_UNREACHABLE(); @@ -118,7 +119,7 @@ bool Object::loadXML(const QDomElement& docElem, ProgressCallback progressForwar LayerBitmap* Object::addNewBitmapLayer() { - LayerBitmap* layerBitmap = new LayerBitmap(this); + LayerBitmap* layerBitmap = new LayerBitmap(getUniqueLayerID()); mLayers.append(layerBitmap); layerBitmap->addNewKeyFrameAt(1); @@ -128,7 +129,7 @@ LayerBitmap* Object::addNewBitmapLayer() LayerVector* Object::addNewVectorLayer() { - LayerVector* layerVector = new LayerVector(this); + LayerVector* layerVector = new LayerVector(getUniqueLayerID()); mLayers.append(layerVector); layerVector->addNewKeyFrameAt(1); @@ -138,7 +139,7 @@ LayerVector* Object::addNewVectorLayer() LayerSound* Object::addNewSoundLayer() { - LayerSound* layerSound = new LayerSound(this); + LayerSound* layerSound = new LayerSound(getUniqueLayerID()); mLayers.append(layerSound); // No default keyFrame at position 1 for Sound layer. @@ -148,7 +149,7 @@ LayerSound* Object::addNewSoundLayer() LayerCamera* Object::addNewCameraLayer() { - LayerCamera* layerCamera = new LayerCamera(this); + LayerCamera* layerCamera = new LayerCamera(getUniqueLayerID()); mLayers.append(layerCamera); layerCamera->addNewKeyFrameAt(1); @@ -303,7 +304,11 @@ bool Object::swapLayers(int i, int j) if (i != j) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) + mLayers.swapItemsAt(i, j); +#else mLayers.swap(i, j); +#endif } return true; } @@ -373,7 +378,7 @@ bool Object::addLayer(Layer* layer) { return false; } - layer->setObject(this); + layer->setId(getUniqueLayerID()); mLayers.append(layer); return true; } @@ -451,7 +456,7 @@ void Object::removeColor(int index) mPalette.removeAt(index); - // update the vector pictures using that color ! + // update the vector pictures using that color! } void Object::renameColor(int i, const QString& text) @@ -502,8 +507,8 @@ void Object::exportPalettePencil(QFile& file) const tag.setAttribute("alpha", ref.color.alpha()); root.appendChild(tag); } - int IndentSize = 2; - doc.save(out, IndentSize); + int indentSize = 2; + doc.save(out, indentSize); } bool Object::exportPalette(const QString& filePath) const @@ -537,7 +542,7 @@ void Object::importPaletteGPL(QFile& file) QTextStream in(&file); QString line; - // First line must start with "GIMP Palette" + // The first line must start with "GIMP Palette" // Displaying an error here would be nice in.readLineInto(&line); if (!line.startsWith("GIMP Palette")) return; @@ -548,16 +553,16 @@ void Object::importPaletteGPL(QFile& file) if (line.startsWith("Name: ")) { in.readLineInto(&line); - // The new format contains an optional thrid line starting with "Columns: " + // The new format contains an optional third line starting with "Columns: " if (line.startsWith("Columns: ")) { - // Skip to next line + // Skip to the next line in.readLineInto(&line); } } // Colors inherit the value from the previous color for missing channels - // Some palettes may rely on this behavior so we should try to replicate it + // Some palettes may rely on this behavior, so we should try to replicate it QColor prevColor(Qt::black); do @@ -571,8 +576,11 @@ void Object::importPaletteGPL(QFile& file) int countInLine = 0; QString name = ""; - - for(const QString& snip : line.split(QRegExp("\\s|\\t"), QString::SkipEmptyParts)) +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + for(const QString& snip : line.split(QRegularExpression("\\s|\\t"), Qt::SkipEmptyParts)) +#else + for(const QString& snip : line.split(QRegularExpression("\\s|\\t"), QString::SkipEmptyParts)) +#endif { switch (countInLine) { @@ -598,7 +606,7 @@ void Object::importPaletteGPL(QFile& file) if (countInLine < 2) green = prevColor.green(); if (countInLine < 3) blue = prevColor.blue(); - // GIMP assigns colors the name "Untitled" by default now + // GIMP assigns colors the name "Untitled" by default now, // so in addition to missing names, we also use automatic // naming for this if (name.isEmpty() || name == "Untitled") name = QString(); @@ -747,7 +755,7 @@ void Object::paintImage(QPainter& painter,int frameNumber, if (vec) { painter.setOpacity(vec->getOpacity()); - vec->paintImage(painter, false, false, antialiasing); + vec->paintImage(painter, *this, false, false, antialiasing); } } } @@ -807,7 +815,7 @@ bool Object::exportFrames(int frameStart, int frameEnd, { format = "JPG"; extension = ".jpg"; - transparency = false; // JPG doesn't support transparency so we have to include the background + transparency = false; // JPG doesn't support transparency, so we have to include the background } if (formatStr == "TIFF" || formatStr == "tiff" || formatStr == "TIF" || formatStr == "tif") { @@ -820,6 +828,10 @@ bool Object::exportFrames(int frameStart, int frameEnd, extension = ".bmp"; transparency = false; } + if (formatStr == "WEBP" || formatStr == "webp") { + format = "WEBP"; + extension = ".webp"; + } if (filePath.endsWith(extension, Qt::CaseInsensitive)) { filePath.chop(extension.size()); diff --git a/core_lib/src/structure/object.h b/core_lib/src/structure/object.h index 6ed45a3183..c7701d06b0 100644 --- a/core_lib/src/structure/object.h +++ b/core_lib/src/structure/object.h @@ -45,10 +45,10 @@ class Object final explicit Object(); ~Object(); - Object(Object const&) = delete; - Object(Object&&) = delete; - Object& operator=(Object const&) = delete; - Object& operator=(Object&&) = delete; + Object(Object const&) = delete; + Object(Object&&) = delete; + Object& operator=(Object const&) = delete; + Object& operator=(Object&&) = delete; void init(); void createWorkingDir(); diff --git a/core_lib/src/tool/basetool.cpp b/core_lib/src/tool/basetool.cpp index 56bd359bc7..d02496cb46 100644 --- a/core_lib/src/tool/basetool.cpp +++ b/core_lib/src/tool/basetool.cpp @@ -111,7 +111,7 @@ void BaseTool::pointerDoubleClickEvent(PointerEvent* event) */ bool BaseTool::isDrawingTool() { - if (type() == ToolType::HAND || type() == ToolType::MOVE || type() == ToolType::SELECT ) + if (type() == ToolType::HAND || type() == ToolType::MOVE || type() == ToolType::CAMERA || type() == ToolType::SELECT ) { return false; } @@ -164,7 +164,7 @@ QPixmap BaseTool::canvasCursor(float width, float feather, bool useFeather, floa cursorPixmap.fill(QColor(255, 255, 255, 0)); QPainter cursorPainter(&cursorPixmap); QPen cursorPen = cursorPainter.pen(); - cursorPainter.setRenderHint(QPainter::HighQualityAntialiasing); + cursorPainter.setRenderHint(QPainter::Antialiasing); // Draw cross in center cursorPen.setStyle(Qt::SolidLine); @@ -429,11 +429,6 @@ void BaseTool::setFillExpand(const int fillExpandValue) properties.bucketFillExpand = fillExpandValue; } -void BaseTool::setFillToLayerMode(int layerMode) -{ - properties.bucketFillToLayerMode = layerMode; -} - void BaseTool::setFillReferenceMode(int referenceMode) { properties.bucketFillReferenceMode = referenceMode; diff --git a/core_lib/src/tool/basetool.h b/core_lib/src/tool/basetool.h index 02f33ddecf..533b59491f 100644 --- a/core_lib/src/tool/basetool.h +++ b/core_lib/src/tool/basetool.h @@ -53,7 +53,6 @@ class Properties bool toleranceEnabled = false; int bucketFillExpand = 0; bool bucketFillExpandEnabled = 0; - int bucketFillToLayerMode = 0; int bucketFillReferenceMode = 0; bool useFillContour = false; bool showSelectionInfo = true; @@ -127,7 +126,6 @@ class BaseTool : public QObject virtual void setToleranceEnabled(const bool enabled); virtual void setFillExpand(const int fillExpandValue); virtual void setFillExpandEnabled(const bool enabled); - virtual void setFillToLayerMode(int layerMode); virtual void setFillReferenceMode(int referenceMode); virtual void setUseFillContour(const bool useFillContour); virtual void setShowSelectionInfo(const bool b); diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 8461e285d7..4bec803d58 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -132,9 +132,9 @@ QCursor BrushTool::cursor() { if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR)) { - return QCursor(QPixmap(":icons/brush.png"), 0, 13); + return QCursor(QPixmap(":icons/general/cursor-brush.svg"), 4, 14); } - return QCursor(QPixmap(":icons/cross.png"), 10, 10); + return QCursor(QPixmap(":icons/general/cross.png"), 10, 10); } void BrushTool::pointerPressEvent(PointerEvent *event) @@ -173,10 +173,9 @@ void BrushTool::pointerReleaseEvent(PointerEvent *event) drawStroke(); } - if (layer->type() == Layer::BITMAP) - paintBitmapStroke(); - else if (layer->type() == Layer::VECTOR) - paintVectorStroke(); + if (layer->type() == Layer::VECTOR) { + paintVectorStroke(layer); + } endStroke(); } @@ -196,6 +195,7 @@ void BrushTool::paintAt(QPointF point) brushWidth, properties.feather, mEditor->color()->frontColor(), + QPainter::CompositionMode_SourceOver, opacity, true); } @@ -232,6 +232,7 @@ void BrushTool::drawStroke() brushWidth, properties.feather, mEditor->color()->frontColor(), + QPainter::CompositionMode_SourceOver, opacity, true); if (i == (steps - 1)) @@ -277,25 +278,17 @@ void BrushTool::drawStroke() } } -void BrushTool::paintBitmapStroke() -{ - mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearBitmapBuffer(); -} - // This function uses the points from DrawStroke // and turns them into vector lines. -void BrushTool::paintVectorStroke() +void BrushTool::paintVectorStroke(Layer* layer) { if (mStrokePoints.empty()) return; - Layer* layer = mEditor->layers()->currentLayer(); - if (layer->type() == Layer::VECTOR && mStrokePoints.size() > -1) { // Clear the temporary pixel path - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling(); BezierCurve curve(mStrokePoints, mStrokePressures, tol); diff --git a/core_lib/src/tool/brushtool.h b/core_lib/src/tool/brushtool.h index eb1e2413be..0e82d65101 100644 --- a/core_lib/src/tool/brushtool.h +++ b/core_lib/src/tool/brushtool.h @@ -21,6 +21,7 @@ GNU General Public License for more details. #include "stroketool.h" #include +class Layer; class BrushTool : public StrokeTool { @@ -38,8 +39,7 @@ class BrushTool : public StrokeTool void pointerReleaseEvent(PointerEvent*) override; void drawStroke(); - void paintVectorStroke(); - void paintBitmapStroke(); + void paintVectorStroke(Layer* layer); void paintAt(QPointF point); void setWidth(const qreal width) override; diff --git a/core_lib/src/tool/buckettool.cpp b/core_lib/src/tool/buckettool.cpp index 2ef4f01aa5..6ef11c59c1 100644 --- a/core_lib/src/tool/buckettool.cpp +++ b/core_lib/src/tool/buckettool.cpp @@ -62,7 +62,6 @@ void BucketTool::loadSettings() properties.bucketFillExpand = settings.value(SETTING_BUCKET_FILL_EXPAND, 2.0).toInt(); properties.bucketFillExpandEnabled = settings.value(SETTING_BUCKET_FILL_EXPAND_ON, true).toBool(); - properties.bucketFillToLayerMode = settings.value(SETTING_BUCKET_FILL_TO_LAYER_MODE, 0).toInt(); properties.bucketFillReferenceMode = settings.value(SETTING_BUCKET_FILL_REFERENCE_MODE, 0).toInt(); properties.fillMode = settings.value(SETTING_FILL_MODE, 0).toInt(); } @@ -74,7 +73,6 @@ void BucketTool::resetToDefault() setFillMode(0); setFillExpand(2); setFillExpandEnabled(true); - setFillToLayerMode(0); setToleranceEnabled(false); setFillReferenceMode(0); } @@ -83,15 +81,11 @@ QCursor BucketTool::cursor() { if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR)) { - QPixmap pixmap(":icons/bucketTool.png"); - QPainter painter(&pixmap); - painter.end(); - - return QCursor(pixmap, 4, 20); + return QCursor(QPixmap(":icons/general/cursor-bucket.svg"), -1, 17); } else { - return QCursor(QPixmap(":icons/cross.png"), 10, 10); + return QCursor(QPixmap(":icons/general/cross.png"), 10, 10); } } @@ -163,16 +157,6 @@ void BucketTool::setFillExpand(const int fillExpandValue) settings.sync(); } -void BucketTool::setFillToLayerMode(int layerMode) -{ - properties.bucketFillToLayerMode = layerMode; - - // Update settings - QSettings settings(PENCIL2D, PENCIL2D); - settings.setValue(SETTING_BUCKET_FILL_TO_LAYER_MODE, layerMode); - settings.sync(); -} - void BucketTool::setFillReferenceMode(int referenceMode) { properties.bucketFillReferenceMode = referenceMode; @@ -195,7 +179,7 @@ void BucketTool::pointerPressEvent(PointerEvent* event) mBitmapBucket = BitmapBucket(mEditor, mEditor->color()->frontColor(), - layerCam ? layerCam->getViewRect() : QRect(), + layerCam ? layerCam->getViewAtFrame(mEditor->currentFrame()).inverted().mapRect(layerCam->getViewRect()) : QRect(), getCurrentPoint(), properties); @@ -271,20 +255,13 @@ void BucketTool::paintBitmap() else if (progress == BucketState::DidFillTarget) { mEditor->setModified(layerIndex, frameIndex); - - // Need to invalidate layer pixmap cache when filling anything else but current layer - // otherwise dragging won't show until release event - if (properties.bucketFillToLayerMode == 1) - { - mScribbleArea->invalidatePainterCaches(); - } } }); } void BucketTool::paintVector(Layer* layer) { - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); VectorImage* vectorImage = static_cast(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing @@ -324,8 +301,6 @@ void BucketTool::drawStroke() if (layer->type() == Layer::VECTOR) { mCurrentWidth = 30; - int rad = qRound((mCurrentWidth / 2 + 2) * mEditor->view()->scaling()); - QColor pathColor = qPremultiply(mEditor->color()->frontColor().rgba()); QPen pen(pathColor, diff --git a/core_lib/src/tool/buckettool.h b/core_lib/src/tool/buckettool.h index 631c19938a..4f9660a61c 100644 --- a/core_lib/src/tool/buckettool.h +++ b/core_lib/src/tool/buckettool.h @@ -47,7 +47,6 @@ class BucketTool : public StrokeTool void setWidth(const qreal width) override; void setFillExpand(const int fillExpandValue) override; void setFillExpandEnabled(const bool enabled) override; - void setFillToLayerMode(int layerMode) override; void setFillReferenceMode(int referenceMode) override; void setFillMode(int mode) override; diff --git a/core_lib/src/tool/cameratool.cpp b/core_lib/src/tool/cameratool.cpp index f4fd3c0309..e82edb4879 100644 --- a/core_lib/src/tool/cameratool.cpp +++ b/core_lib/src/tool/cameratool.cpp @@ -122,24 +122,24 @@ QCursor CameraTool::cursor() case CameraMoveType::TOPLEFT: case CameraMoveType::BOTTOMRIGHT: { - moveTypeImage = QImage("://icons/new/svg/cursor-diagonal-left.svg"); + moveTypeImage = QImage("://icons/general/cursor-diagonal-left.svg"); break; } case CameraMoveType::TOPRIGHT: case CameraMoveType::BOTTOMLEFT: { - moveTypeImage = QImage("://icons/new/svg/cursor-diagonal-right.svg"); + moveTypeImage = QImage("://icons/general/cursor-diagonal-right.svg"); break; } case CameraMoveType::ROTATION: { - moveTypeImage = QImage("://icons/new/svg/cursor-rotate.svg"); + moveTypeImage = QImage("://icons/general/cursor-rotate.svg"); break; } case CameraMoveType::PATH: case CameraMoveType::CENTER: { - moveTypeImage = QImage("://icons/new/svg/cursor-move.svg"); + moveTypeImage = QImage("://icons/general/cursor-move.svg"); break; } default: @@ -229,7 +229,7 @@ void CameraTool::resetCameraPath() Q_ASSERT(layer->type() == Layer::CAMERA); layer->setPathMovedAtFrame(mEditor->currentFrame(), false); - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void CameraTool::resetTransform(CameraFieldOption option) @@ -261,7 +261,7 @@ void CameraTool::transformCamera(Qt::KeyboardModifiers keyMod) transformView(layer, mCamMoveMode, getCurrentPoint(), mTransformOffset, -angleDeg, mEditor->currentFrame()); - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); mTransformOffset = getCurrentPoint(); } @@ -271,7 +271,7 @@ void CameraTool::transformCameraPath() LayerCamera* layer = static_cast(editor()->layers()->currentLayer()); layer->updatePathControlPointAtFrame(getCurrentPoint(), mDragPathFrame); - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } int CameraTool::constrainedRotation(const qreal rotatedAngle, const int rotationIncrement) const @@ -304,7 +304,7 @@ void CameraTool::pointerMoveEvent(PointerEvent* event) } mScribbleArea->updateToolCursor(); mEditor->view()->forceUpdateViewTransform(); - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void CameraTool::pointerReleaseEvent(PointerEvent* event) @@ -566,9 +566,9 @@ void CameraTool::paintInterpolations(QPainter& painter, const QTransform& worldT painter.save(); QColor color = cameraDotColor; if (currentFrame > frame && currentFrame < nextFrame) - color.setAlphaF(0.5); + color.setAlphaF(.5f); else - color.setAlphaF(0.2); + color.setAlphaF(.2f); painter.setPen(Qt::black); painter.setBrush(color); diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index ba7ecf6379..7d57ade7ef 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -144,7 +144,7 @@ void EraserTool::setStabilizerLevel(const int level) QCursor EraserTool::cursor() { - return QCursor(QPixmap(":icons/cross.png"), 10, 10); + return QCursor(QPixmap(":icons/general/cross.png"), 10, 10); } void EraserTool::pointerPressEvent(PointerEvent *event) @@ -180,6 +180,7 @@ void EraserTool::pointerReleaseEvent(PointerEvent *event) { drawStroke(); } + removeVectorPaint(); endStroke(); } @@ -199,6 +200,7 @@ void EraserTool::paintAt(QPointF point) brushWidth, properties.feather, QColor(255, 255, 255, 255), + QPainter::CompositionMode_SourceOver, opacity, properties.useFeather, properties.useAA == ON); @@ -238,6 +240,7 @@ void EraserTool::drawStroke() brushWidth, properties.feather, Qt::white, + QPainter::CompositionMode_SourceOver, opacity, properties.useFeather, properties.useAA == ON); @@ -272,14 +275,9 @@ void EraserTool::drawStroke() void EraserTool::removeVectorPaint() { Layer* layer = mEditor->layers()->currentLayer(); - if (layer->type() == Layer::BITMAP) - { - mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearBitmapBuffer(); - } - else if (layer->type() == Layer::VECTOR) + if (layer->type() == Layer::VECTOR) { - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); VectorImage* vectorImage = static_cast(layer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); if (vectorImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing // Clear the area containing the last point diff --git a/core_lib/src/tool/eyedroppertool.cpp b/core_lib/src/tool/eyedroppertool.cpp index 0007c1d867..d72c3f71a3 100644 --- a/core_lib/src/tool/eyedroppertool.cpp +++ b/core_lib/src/tool/eyedroppertool.cpp @@ -49,17 +49,17 @@ QCursor EyedropperTool::cursor() { if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR)) { - return QCursor(QPixmap(":icons/eyedropper.png"), 0, 15); + return QCursor(QPixmap(":icons/general/cursor-eyedropper.svg"), 0, 15); } else { - return QCursor(QPixmap(":icons/cross.png"), 10, 10); + return QCursor(QPixmap(":icons/general/cross.png"), 10, 10); } } QCursor EyedropperTool::cursor(const QColor color) { - QPixmap icon(":icons/eyedropper.png"); + QPixmap icon(":icons/general/cursor-eyedropper.svg"); QPixmap pixmap(32, 32); pixmap.fill(Qt::transparent); diff --git a/core_lib/src/tool/handtool.cpp b/core_lib/src/tool/handtool.cpp index 5d3dc795d0..d741a2ed12 100644 --- a/core_lib/src/tool/handtool.cpp +++ b/core_lib/src/tool/handtool.cpp @@ -41,11 +41,28 @@ void HandTool::loadSettings() properties.useFeather = false; properties.stabilizerLevel = -1; properties.useAA = -1; + + mDeltaFactor = mEditor->preference()->isOn(SETTING::INVERT_DRAG_ZOOM_DIRECTION) ? -1 : 1; + connect(mEditor->preference(), &PreferenceManager::optionChanged, this, &HandTool::updateSettings); +} + +void HandTool::updateSettings(const SETTING setting) +{ + switch (setting) + { + case SETTING::INVERT_DRAG_ZOOM_DIRECTION: + { + mDeltaFactor = mEditor->preference()->isOn(SETTING::INVERT_DRAG_ZOOM_DIRECTION) ? -1 : 1; + break; + } + default: + break; + } } QCursor HandTool::cursor() { - return mIsHeld ? Qt::ClosedHandCursor : Qt::OpenHandCursor; + return mIsHeld ? QCursor(Qt::ClosedHandCursor) : QCursor(Qt::OpenHandCursor); } void HandTool::pointerPressEvent(PointerEvent*) @@ -105,13 +122,15 @@ void HandTool::transformView(Qt::KeyboardModifiers keyMod, Qt::MouseButtons butt qreal angleOffset = static_cast(std::atan2(curV.y(), curV.x()) - std::atan2(startV.y(), startV.x())); angleOffset = qRadiansToDegrees(angleOffset); - float newAngle = viewMgr->rotation() + static_cast(angleOffset); - viewMgr->rotate(newAngle); + // Invert rotation direction if view is flipped either vertically or horizontally + const float delta = viewMgr->isFlipHorizontal() == !viewMgr->isFlipVertical() + ? static_cast(angleOffset * -1) : static_cast(angleOffset); + viewMgr->rotateRelative(delta); } else if (isScale) { - float delta = (static_cast(getCurrentPixel().y() - mLastPixel.y())) / 100.f; - qreal scaleValue = viewMgr->scaling() * (1 + delta); + const float delta = (static_cast(getCurrentPixel().y() - mLastPixel.y())) / 100.f; + const qreal scaleValue = viewMgr->scaling() * (1 + (delta * mDeltaFactor)); viewMgr->scaleAtOffset(scaleValue, mStartPoint); } } diff --git a/core_lib/src/tool/handtool.h b/core_lib/src/tool/handtool.h index 8d8e4742de..1f17747b74 100644 --- a/core_lib/src/tool/handtool.h +++ b/core_lib/src/tool/handtool.h @@ -19,6 +19,7 @@ GNU General Public License for more details. #define HANDTOOL_H #include "basetool.h" +#include "preferencemanager.h" class HandTool : public BaseTool @@ -38,11 +39,13 @@ class HandTool : public BaseTool virtual bool isActive() override { return false; } private: + void updateSettings(const SETTING setting); void transformView(Qt::KeyboardModifiers keyMod, Qt::MouseButtons buttons); QPointF mLastPixel; QPointF mStartPoint; bool mIsHeld = false; + int mDeltaFactor = 1; }; #endif diff --git a/core_lib/src/tool/movetool.cpp b/core_lib/src/tool/movetool.cpp index 20f459382c..ed3757da73 100644 --- a/core_lib/src/tool/movetool.cpp +++ b/core_lib/src/tool/movetool.cpp @@ -101,12 +101,12 @@ void MoveTool::updateSettings(const SETTING setting) void MoveTool::pointerPressEvent(PointerEvent* event) { - mCurrentLayer = currentPaintableLayer(); - if (mCurrentLayer == nullptr) return; + Layer* currentLayer = currentPaintableLayer(); + if (currentLayer == nullptr) return; if (mEditor->select()->somethingSelected()) { - beginInteraction(event->modifiers(), mCurrentLayer); + beginInteraction(event->modifiers(), currentLayer); } if (mEditor->overlays()->anyOverlayEnabled()) { @@ -119,13 +119,13 @@ void MoveTool::pointerPressEvent(PointerEvent* event) mEditor->overlays()->updatePerspective(mapped); } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void MoveTool::pointerMoveEvent(PointerEvent* event) { - mCurrentLayer = currentPaintableLayer(); - if (mCurrentLayer == nullptr) return; + Layer* currentLayer = currentPaintableLayer(); + if (currentLayer == nullptr) return; if (mScribbleArea->isPointerInUse()) // the user is also pressing the mouse (dragging) { @@ -149,12 +149,12 @@ void MoveTool::pointerMoveEvent(PointerEvent* event) mEditor->select()->setMoveModeForAnchorInRange(getCurrentPoint()); mScribbleArea->updateToolCursor(); - if (mCurrentLayer->type() == Layer::VECTOR) + if (currentLayer->type() == Layer::VECTOR) { - storeClosestVectorCurve(mCurrentLayer); + storeClosestVectorCurve(currentLayer); } } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void MoveTool::pointerReleaseEvent(PointerEvent*) @@ -305,12 +305,6 @@ void MoveTool::storeClosestVectorCurve(Layer* layer) selectMan->setCurves(pVecImg->getCurvesCloseTo(getCurrentPoint(), selectMan->selectionTolerance())); } -void MoveTool::cancelChanges() -{ - mScribbleArea->cancelTransformedSelection(); - mEditor->deselectAll(); -} - void MoveTool::applyTransformation() { SelectionManager* selectMan = mEditor->select(); @@ -325,14 +319,9 @@ void MoveTool::applyTransformation() bool MoveTool::leavingThisTool() { - if (mCurrentLayer) + if (currentPaintableLayer()) { - switch (mCurrentLayer->type()) - { - case Layer::BITMAP: applyTransformation(); break; - case Layer::VECTOR: applyTransformation(); break; - default: break; - } + applyTransformation(); } return true; } @@ -367,7 +356,7 @@ QCursor MoveTool::cursor(MoveMode mode) const cursorPixmap.fill(QColor(255, 255, 255, 0)); QPainter cursorPainter(&cursorPixmap); - cursorPainter.setRenderHint(QPainter::HighQualityAntialiasing); + cursorPainter.setRenderHint(QPainter::Antialiasing); switch(mode) { @@ -376,37 +365,36 @@ QCursor MoveTool::cursor(MoveMode mode) const case MoveMode::PERSP_MIDDLE: case MoveMode::PERSP_SINGLE: { - cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/svg/cursor-move.svg")); + cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-move.svg")); break; } case MoveMode::TOPLEFT: case MoveMode::BOTTOMRIGHT: { - cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/svg/cursor-diagonal-left.svg")); + cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-diagonal-left.svg")); break; } case MoveMode::TOPRIGHT: case MoveMode::BOTTOMLEFT: { - cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/svg/cursor-diagonal-right.svg")); + cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-diagonal-right.svg")); break; } case MoveMode::ROTATIONLEFT: case MoveMode::ROTATIONRIGHT: case MoveMode::ROTATION: { - cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/svg/cursor-rotate.svg")); + cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-rotate.svg")); break; } case MoveMode::MIDDLE: case MoveMode::CENTER: { - cursorPainter.drawImage(QPoint(6,6),QImage("://icons/new/svg/cursor-move.svg")); + cursorPainter.drawImage(QPoint(6,6),QImage("://icons/general/cursor-move.svg")); break; } default: return Qt::ArrowCursor; - break; } cursorPainter.end(); diff --git a/core_lib/src/tool/movetool.h b/core_lib/src/tool/movetool.h index 7e5c210e87..38107d642d 100644 --- a/core_lib/src/tool/movetool.h +++ b/core_lib/src/tool/movetool.h @@ -46,7 +46,6 @@ class MoveTool : public BaseTool void setShowSelectionInfo(const bool b) override; private: - void cancelChanges(); void applyTransformation(); void updateSettings(const SETTING setting); @@ -60,7 +59,6 @@ class MoveTool : public BaseTool Layer* currentPaintableLayer(); - Layer* mCurrentLayer = nullptr; qreal mRotatedAngle = 0.0; qreal mPreviousAngle = 0.0; int mRotationIncrement = 0; diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index e1249b67f4..cd2169870b 100644 --- a/core_lib/src/tool/penciltool.cpp +++ b/core_lib/src/tool/penciltool.cpp @@ -29,7 +29,6 @@ GNU General Public License for more details. #include "editor.h" #include "scribblearea.h" -#include "blitrect.h" #include "layervector.h" #include "vectorimage.h" @@ -141,9 +140,9 @@ QCursor PencilTool::cursor() { if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR)) { - return QCursor(QPixmap(":icons/pencil2.png"), 0, 16); + return QCursor(QPixmap(":icons/general/cursor-pencil.svg"), 4, 14); } - return QCursor(QPixmap(":icons/cross.png"), 10, 10); + return QCursor(QPixmap(":icons/general/cross.png"), 10, 10); } void PencilTool::pointerPressEvent(PointerEvent *event) @@ -187,10 +186,9 @@ void PencilTool::pointerReleaseEvent(PointerEvent *event) } Layer* layer = mEditor->layers()->currentLayer(); - if (layer->type() == Layer::BITMAP) - paintBitmapStroke(); - else if (layer->type() == Layer::VECTOR) + if (layer->type() == Layer::VECTOR) { paintVectorStroke(layer); + } endStroke(); } @@ -275,20 +273,13 @@ void PencilTool::drawStroke() } } - -void PencilTool::paintBitmapStroke() -{ - mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearBitmapBuffer(); -} - void PencilTool::paintVectorStroke(Layer* layer) { if (mStrokePoints.empty()) return; // Clear the temporary pixel path - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling(); BezierCurve curve(mStrokePoints, mStrokePressures, tol); diff --git a/core_lib/src/tool/penciltool.h b/core_lib/src/tool/penciltool.h index adb5019da3..2cb823296f 100644 --- a/core_lib/src/tool/penciltool.h +++ b/core_lib/src/tool/penciltool.h @@ -40,7 +40,6 @@ class PencilTool : public StrokeTool void drawStroke(); void paintAt(QPointF point); void paintVectorStroke(Layer* layer); - void paintBitmapStroke(); void setWidth(const qreal width) override; void setFeather(const qreal feather) override; @@ -52,9 +51,7 @@ class PencilTool : public StrokeTool void setUseFillContour(const bool useFillContour) override; private: - QColor mCurrentPressuredColor{ 0, 0, 0, 255 }; QPointF mLastBrushPoint{ 0, 0 }; - qreal mOpacity = 1.0f; QPointF mMouseDownPoint; }; diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 62f9996591..64dbed9708 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -111,9 +111,9 @@ QCursor PenTool::cursor() { if (mEditor->preference()->isOn(SETTING::TOOL_CURSOR)) { - return QCursor(QPixmap(":icons/pen.png"), -5, 0); + return QCursor(QPixmap(":icons/general/cursor-pen.svg"), 5, 14); } - return QCursor(QPixmap(":icons/cross.png"), 10, 10); + return QCursor(QPixmap(":icons/general/cross.png"), 10, 10); } void PenTool::pointerPressEvent(PointerEvent *event) @@ -153,10 +153,9 @@ void PenTool::pointerReleaseEvent(PointerEvent *event) drawStroke(); } - if (layer->type() == Layer::BITMAP) - paintBitmapStroke(); - else if (layer->type() == Layer::VECTOR) + if (layer->type() == Layer::VECTOR) { paintVectorStroke(layer); + } endStroke(); } @@ -235,19 +234,13 @@ void PenTool::drawStroke() } } -void PenTool::paintBitmapStroke() -{ - mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearBitmapBuffer(); -} - void PenTool::paintVectorStroke(Layer* layer) { if (mStrokePoints.empty()) return; // Clear the temporary pixel path - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); qreal tol = mScribbleArea->getCurveSmoothing() / mEditor->view()->scaling(); BezierCurve curve(mStrokePoints, mStrokePressures, tol); diff --git a/core_lib/src/tool/pentool.h b/core_lib/src/tool/pentool.h index ff58abc11c..a904944adc 100644 --- a/core_lib/src/tool/pentool.h +++ b/core_lib/src/tool/pentool.h @@ -39,7 +39,6 @@ class PenTool : public StrokeTool void drawStroke(); void paintAt(QPointF point); void paintVectorStroke(Layer *layer); - void paintBitmapStroke(); void setWidth(const qreal width) override; void setPressure(const bool pressure) override; diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index 866753e002..4d723b487b 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -107,7 +107,7 @@ bool PolylineTool::isActive() QCursor PolylineTool::cursor() { - return QCursor(QPixmap(":icons/cross.png"), 10, 10); + return QCursor(QPixmap(":icons/general/cross.png"), 10, 10); } void PolylineTool::clearToolData() @@ -243,14 +243,13 @@ void PolylineTool::drawPolyline(QList points, QPointF endPoint) void PolylineTool::cancelPolyline() { // Clear the in-progress polyline from the bitmap buffer. - mScribbleArea->clearBitmapBuffer(); - mScribbleArea->updateCurrentFrame(); + mScribbleArea->clearDrawingBuffer(); + mScribbleArea->updateFrame(); } void PolylineTool::endPolyline(QList points) { Layer* layer = mEditor->layers()->currentLayer(); - mScribbleArea->clearBitmapBuffer(); if (layer->type() == Layer::VECTOR) { @@ -274,11 +273,7 @@ void PolylineTool::endPolyline(QList points) if (layer->type() == Layer::BITMAP) { drawPolyline(points, points.last()); - BitmapImage *bitmapImage = static_cast(layer)->getLastBitmapImageAtFrame(mEditor->currentFrame(), 0); - if (bitmapImage == nullptr) { return; } // Can happen if the first frame is deleted while drawing - bitmapImage->paste(&mScribbleArea->mBufferImg); } - - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->endStroke(); mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); } diff --git a/core_lib/src/tool/selecttool.cpp b/core_lib/src/tool/selecttool.cpp index 6b9d8725df..1fa4d639a9 100644 --- a/core_lib/src/tool/selecttool.cpp +++ b/core_lib/src/tool/selecttool.cpp @@ -43,43 +43,42 @@ void SelectTool::loadSettings() QCursor SelectTool::cursor() { // Don't update cursor while we're moving the selection - if (mScribbleArea->isPointerInUse()) { return mCursorPixmap; } + if (mScribbleArea->isPointerInUse()) { return QCursor(mCursorPixmap); } mEditor->select()->setMoveModeForAnchorInRange(getCurrentPoint()); MoveMode mode = mEditor->select()->getMoveMode(); mCursorPixmap.fill(QColor(255, 255, 255, 0)); QPainter cursorPainter(&mCursorPixmap); - cursorPainter.setRenderHint(QPainter::HighQualityAntialiasing); + cursorPainter.setRenderHint(QPainter::Antialiasing); switch(mode) { case MoveMode::TOPLEFT: case MoveMode::BOTTOMRIGHT: { - cursorPainter.drawPixmap(QPoint(6, 6), QPixmap("://icons/new/svg/cursor-diagonal-left.svg")); + cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-diagonal-left.svg")); break; } case MoveMode::TOPRIGHT: case MoveMode::BOTTOMLEFT: { - cursorPainter.drawPixmap(QPoint(6, 6), QPixmap("://icons/new/svg/cursor-diagonal-right.svg")); + cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-diagonal-right.svg")); break; } case MoveMode::MIDDLE: { - cursorPainter.drawPixmap(QPoint(6, 6), QPixmap("://icons/new/svg/cursor-move.svg")); + cursorPainter.drawPixmap(QPoint(6,6),QPixmap("://icons/general/cursor-move.svg")); break; } case MoveMode::NONE: - { - cursorPainter.drawPixmap(QPoint(2, 2), QPixmap(":icons/cross.png")); + cursorPainter.drawPixmap(QPoint(3,3), QPixmap(":icons/general/cross.png")); break; - } default: Q_UNREACHABLE(); + break; } - return mCursorPixmap; + return QCursor(mCursorPixmap); } void SelectTool::resetToDefault() @@ -95,15 +94,15 @@ void SelectTool::setShowSelectionInfo(const bool b) settings.setValue("ShowSelectionInfo", b); } -void SelectTool::beginSelection() +void SelectTool::beginSelection(Layer* currentLayer) { auto selectMan = mEditor->select(); if (selectMan->somethingSelected() && mMoveMode != MoveMode::NONE) // there is something selected { - if (mCurrentLayer->type() == Layer::VECTOR) + if (currentLayer->type() == Layer::VECTOR) { - VectorImage* vectorImage = static_cast(mCurrentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); + VectorImage* vectorImage = static_cast(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); if (vectorImage != nullptr) { vectorImage->deselectAll(); } @@ -116,14 +115,14 @@ void SelectTool::beginSelection() mAnchorOriginPoint = getLastPoint(); } - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void SelectTool::pointerPressEvent(PointerEvent* event) { - mCurrentLayer = mEditor->layers()->currentLayer(); - if (mCurrentLayer == nullptr) return; - if (!mCurrentLayer->isPaintable()) { return; } + Layer* currentLayer = mEditor->layers()->currentLayer(); + if (currentLayer == nullptr) return; + if (!currentLayer->isPaintable()) { return; } if (event->button() != Qt::LeftButton) { return; } auto selectMan = mEditor->select(); @@ -131,14 +130,14 @@ void SelectTool::pointerPressEvent(PointerEvent* event) mMoveMode = selectMan->getMoveMode(); mStartMoveMode = mMoveMode; - beginSelection(); + beginSelection(currentLayer); } void SelectTool::pointerMoveEvent(PointerEvent*) { - mCurrentLayer = mEditor->layers()->currentLayer(); - if (mCurrentLayer == nullptr) { return; } - if (!mCurrentLayer->isPaintable()) { return; } + Layer* currentLayer = mEditor->layers()->currentLayer(); + if (currentLayer == nullptr) { return; } + if (!currentLayer->isPaintable()) { return; } auto selectMan = mEditor->select(); if (!selectMan->somethingSelected()) { return; } @@ -151,22 +150,22 @@ void SelectTool::pointerMoveEvent(PointerEvent*) { controlOffsetOrigin(getCurrentPoint(), mAnchorOriginPoint); - if (mCurrentLayer->type() == Layer::VECTOR) + if (currentLayer->type() == Layer::VECTOR) { - VectorImage* vectorImage = static_cast(mCurrentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); + VectorImage* vectorImage = static_cast(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); if (vectorImage != nullptr) { vectorImage->select(selectMan->mapToSelection(QPolygonF(selectMan->mySelectionRect())).boundingRect()); } } } - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void SelectTool::pointerReleaseEvent(PointerEvent* event) { - mCurrentLayer = mEditor->layers()->currentLayer(); - if (mCurrentLayer == nullptr) return; + Layer* currentLayer = mEditor->layers()->currentLayer(); + if (currentLayer == nullptr) return; if (event->button() != Qt::LeftButton) return; // if there's a small very small distance between current and last point @@ -182,14 +181,14 @@ void SelectTool::pointerReleaseEvent(PointerEvent* event) } else { - keepSelection(); + keepSelection(currentLayer); } mStartMoveMode = MoveMode::NONE; mSelectionRect = mEditor->select()->mapToSelection(mEditor->select()->mySelectionRect()).boundingRect(); mScribbleArea->updateToolCursor(); - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } bool SelectTool::maybeDeselect() @@ -201,12 +200,12 @@ bool SelectTool::maybeDeselect() * @brief SelectTool::keepSelection * Keep selection rect and normalize if invalid */ -void SelectTool::keepSelection() +void SelectTool::keepSelection(Layer* currentLayer) { auto selectMan = mEditor->select(); - if (mCurrentLayer->type() == Layer::VECTOR) + if (currentLayer->type() == Layer::VECTOR) { - VectorImage* vectorImage = static_cast(mCurrentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); + VectorImage* vectorImage = static_cast(currentLayer)->getLastVectorImageAtFrame(mEditor->currentFrame(), 0); if (vectorImage == nullptr) { return; } selectMan->setSelection(vectorImage->getSelectionRect(), false); } @@ -214,12 +213,6 @@ void SelectTool::keepSelection() void SelectTool::controlOffsetOrigin(QPointF currentPoint, QPointF anchorPoint) { - QPointF offset = offsetFromPressPos(); - - if (editor()->layers()->currentLayer()->type() == Layer::BITMAP) { - offset = QPointF(offset).toPoint(); - } - // when the selection is none, manage the selection Origin if (mStartMoveMode != MoveMode::NONE) { QRectF rect = mSelectionRect; @@ -303,7 +296,7 @@ bool SelectTool::keyPressEvent(QKeyEvent* event) break; } - // Follow the generic behaviour anyway + // Follow the generic behavior anyway return BaseTool::keyPressEvent(event); } diff --git a/core_lib/src/tool/selecttool.h b/core_lib/src/tool/selecttool.h index 5383e81938..16e7328468 100644 --- a/core_lib/src/tool/selecttool.h +++ b/core_lib/src/tool/selecttool.h @@ -50,21 +50,20 @@ class SelectTool : public BaseTool void manageSelectionOrigin(QPointF currentPoint, QPointF originPoint); void controlOffsetOrigin(QPointF currentPoint, QPointF anchorPoint); - void beginSelection(); - void keepSelection(); + void beginSelection(Layer* currentLayer); + void keepSelection(Layer* currentLayer); QPointF offsetFromPressPos(); inline bool isSelectionPointValid() { return mAnchorOriginPoint != getLastPoint(); } bool maybeDeselect(); - // Store selection origin so we can calculate + // Store selection origin, so we can calculate // the selection rectangle in mousePressEvent. QPointF mAnchorOriginPoint; MoveMode mMoveMode; MoveMode mStartMoveMode = MoveMode::NONE; QRectF mSelectionRect; - Layer* mCurrentLayer = nullptr; QPixmap mCursorPixmap = QPixmap(24, 24); }; diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index d11c56fbf9..de94077e66 100644 --- a/core_lib/src/tool/smudgetool.cpp +++ b/core_lib/src/tool/smudgetool.cpp @@ -104,12 +104,12 @@ bool SmudgeTool::emptyFrameActionEnabled() QCursor SmudgeTool::cursor() { - qDebug() << "smudge tool"; if (toolMode == 0) { //normal mode - return QCursor(QPixmap(":icons/smudge.png"), 0, 16); + return QCursor(QPixmap(":icons/general/cursor-smudge.svg"), 4, 18); + } else { // blured mode - return QCursor(QPixmap(":icons/liquify.png"), -4, 16); + return QCursor(QPixmap(":icons/general/cursor-smudge-liquify.svg"), 4, 18); } } @@ -257,7 +257,7 @@ void SmudgeTool::pointerReleaseEvent(PointerEvent* event) { drawStroke(); mScribbleArea->paintBitmapBuffer(); - mScribbleArea->clearBitmapBuffer(); + mScribbleArea->clearDrawingBuffer(); endStroke(); } else if (layer->type() == Layer::VECTOR) @@ -317,7 +317,7 @@ void SmudgeTool::drawStroke() QPointF sourcePoint = mLastBrushPoint; for (int i = 0; i < steps; i++) { - targetImage.paste(&mScribbleArea->mBufferImg); + targetImage.paste(&mScribbleArea->mTiledBuffer); QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance; mScribbleArea->liquifyBrush(&targetImage, sourcePoint, @@ -342,9 +342,8 @@ void SmudgeTool::drawStroke() QPointF sourcePoint = mLastBrushPoint; for (int i = 0; i < steps; i++) { - targetImage.paste(&mScribbleArea->mBufferImg); + targetImage.paste(&mScribbleArea->mTiledBuffer); QPointF targetPoint = mLastBrushPoint + (i + 1) * (brushStep) * (b - mLastBrushPoint) / distance; - rect.extend(targetPoint.toPoint()); mScribbleArea->blurBrush(&targetImage, sourcePoint, targetPoint, diff --git a/core_lib/src/tool/strokemanager.cpp b/core_lib/src/tool/strokemanager.cpp index b4e9a6b4f1..d94ba58b1c 100644 --- a/core_lib/src/tool/strokemanager.cpp +++ b/core_lib/src/tool/strokemanager.cpp @@ -179,7 +179,7 @@ QPointF StrokeManager::interpolateStart(QPointF firstPoint) // Clear queue strokeQueue.clear(); pressureQueue.clear(); - + const int sampleSize = 5; Q_ASSERT(sampleSize > 0); @@ -334,8 +334,7 @@ QList StrokeManager::meanInpolOp(QList points, qreal x, qreal pressure /= strokeQueue.size(); // Use our interpolated points - QPointF mNewInterpolated = mLastInterpolated; - mNewInterpolated = QPointF(x, y); + QPointF mNewInterpolated(x, y); points << mLastPixel << mLastInterpolated << mNewInterpolated << mCurrentPixel; diff --git a/core_lib/src/tool/stroketool.cpp b/core_lib/src/tool/stroketool.cpp index cf1b4b6e7c..6b8e8c1122 100644 --- a/core_lib/src/tool/stroketool.cpp +++ b/core_lib/src/tool/stroketool.cpp @@ -101,6 +101,7 @@ void StrokeTool::endStroke() enableCoalescing(); mEditor->setModified(mEditor->currentLayerIndex(), mEditor->currentFrame()); + mScribbleArea->endStroke(); } void StrokeTool::drawStroke() diff --git a/core_lib/src/tool/stroketool.h b/core_lib/src/tool/stroketool.h index e4b5c7d924..7f97c793b5 100644 --- a/core_lib/src/tool/stroketool.h +++ b/core_lib/src/tool/stroketool.h @@ -57,7 +57,7 @@ class StrokeTool : public BaseTool virtual bool emptyFrameActionEnabled(); private: - QPointF mLastPixel { 0, 0 }; + QPointF mLastPixel { 0, 0 }; }; #endif // STROKETOOL_H diff --git a/core_lib/src/util/blitrect.cpp b/core_lib/src/util/blitrect.cpp index 3b0645cec4..f378fd93fe 100644 --- a/core_lib/src/util/blitrect.cpp +++ b/core_lib/src/util/blitrect.cpp @@ -52,7 +52,12 @@ void BlitRect::extend(const QPoint p) void BlitRect::extend(const QRect& rect) { - extend(rect.topLeft(), rect.size()); + // For historical reasons the values returned by the bottom() and + // right() functions deviate from the true bottom-right corner of the rectangle: + // The right() function returns left() + width() - 1 and the bottom() + // function returns top() + height() - 1 + // In order to counter that, we subtract 1 from width and height + extend(rect.topLeft(), QSize(rect.width() - 1, rect.height() - 1)); } void BlitRect::extend(const QPoint& p, const QSize& size) @@ -66,8 +71,8 @@ void BlitRect::extend(const QPoint& p, const QSize& size) else { if (left() > p.x()) { setLeft(p.x()); } - if (right() < p.x() + size.width()) { setRight(p.x() + size.width()); } if (top() > p.y()) { setTop(p.y()); } - if (bottom() < p.y() + size.height()) { setBottom(p.y() + size.height()); } + if (right() - size.width() < p.x()) { setRight(p.x() + size.width()); } + if (bottom() - size.height() < p.y()) { setBottom(p.y() + size.height()); } } } diff --git a/core_lib/src/util/fileformat.cpp b/core_lib/src/util/fileformat.cpp index 55707cfa57..3d4eb77079 100644 --- a/core_lib/src/util/fileformat.cpp +++ b/core_lib/src/util/fileformat.cpp @@ -59,6 +59,7 @@ QString detectFormatByFileNameExtension(const QString& fileName) { "tif", "TIF" }, { "tiff", "TIF" }, { "bmp", "BMP" }, + { "webp", "WEBP" }, { "mp4", "MP4" }, { "avi", "AVI" }, { "gif", "GIF" }, diff --git a/core_lib/src/util/fileformat.h b/core_lib/src/util/fileformat.h index 2ce07e5fa0..6935d0aa44 100644 --- a/core_lib/src/util/fileformat.h +++ b/core_lib/src/util/fileformat.h @@ -40,10 +40,10 @@ GNU General Public License for more details. ";;SWF(*.swf);;FLV(*.flv);;WEBM(*.webm);;WMV(*.wmv)" #define PFF_IMAGE_FILTER \ - QCoreApplication::translate("FileFormat", "Image formats") + " (*.png *.jpg *.jpeg *.bmp *.tif *.tiff);;PNG (*.png);;JPG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tif *.tiff)" + QCoreApplication::translate("FileFormat", "Image formats") + " (*.png *.jpg *.jpeg *.bmp *.tif *.tiff *.webp);;PNG (*.png);;JPG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tif *.tiff);;WEBP(*.webp)" #define PFF_IMAGE_SEQ_FILTER \ - QCoreApplication::translate("FileFormat", "Image formats") + " (*.png *.jpg *.jpeg *.bmp *.tif *.tiff);;PNG (*.png);;JPG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tif *.tiff)" + QCoreApplication::translate("FileFormat", "Image formats") + " (*.png *.jpg *.jpeg *.bmp *.tif *.tiff *.webp);;PNG (*.png);;JPG(*.jpg *.jpeg);;BMP(*.bmp);;TIFF(*.tif *.tiff);;WEBP(*.webp)" #define PFF_PALETTE_EXT_FILTER \ QCoreApplication::translate("FileFormat", "Palette formats") + " (*.xml *.gpl);;" + QCoreApplication::translate("FileFormat", "Pencil2D Palette") + " (*.xml);;" + QCoreApplication::translate("FileFormat", "GIMP Palette") + " (*.gpl)" @@ -51,6 +51,9 @@ GNU General Public License for more details. #define PFF_GIF_EXT_FILTER \ QCoreApplication::translate("FileFormat", "Animated GIF") + " (*.gif)" +#define PFF_ANIMATED_IMAGE_EXT_FILTER \ + QCoreApplication::translate("FileFormat", "Animated image formats") + " (*.gif *.webp);;GIF(*.gif);;WEBP(*.webp)" + #define PFF_SOUND_EXT_FILTER \ QCoreApplication::translate("FileFormat", "Sound formats") + " (*.wav *.mp3 *.wma *.ogg *.flac *.opus *.aiff *.aac *.caf);;WAV (*.wav);;MP3 (*.mp3);;WMA (*.wma);;OGG (*.ogg);;FLAC (*.flac);;Opus (*.opus);;AIFF (*.aiff);;AAC (*.aac);;CAF (*.caf)" diff --git a/core_lib/src/util/filetype.h b/core_lib/src/util/filetype.h index a0558fa5aa..9a88fad1e8 100644 --- a/core_lib/src/util/filetype.h +++ b/core_lib/src/util/filetype.h @@ -7,6 +7,7 @@ enum class FileType IMAGE, IMAGE_SEQUENCE, GIF, + ANIMATED_IMAGE, MOVIE, SOUND, PALETTE diff --git a/core_lib/src/util/pencildef.h b/core_lib/src/util/pencildef.h index 80dc4fe54d..8a546097e6 100644 --- a/core_lib/src/util/pencildef.h +++ b/core_lib/src/util/pencildef.h @@ -64,7 +64,6 @@ enum ToolPropertyType USETOLERANCE, BUCKETFILLEXPAND, USEBUCKETFILLEXPAND, - BUCKETFILLLAYERMODE, BUCKETFILLLAYERREFERENCEMODE, CAMERAPATH, }; @@ -256,6 +255,9 @@ const static float RotationHandleOffset = 50; #define SETTING_LOAD_DEFAULT_PRESET "LoadDefaultPreset" #define SETTING_DEFAULT_PRESET "DefaultPreset" +#define SETTING_INVERT_DRAG_ZOOM_DIRECTION "InvertDragZoomDirection" +#define SETTING_INVERT_SCROLL_ZOOM_DIRECTION "InvertScrollZoomDirection" + #define SETTING_ANTIALIAS "Antialiasing" #define SETTING_SHOW_GRID "ShowGrid" #define SETTING_COUNT "Count" @@ -304,7 +306,6 @@ const static float RotationHandleOffset = 50; #define SETTING_BUCKET_TOLERANCE_ON "BucketToleranceEnabled" #define SETTING_BUCKET_FILL_EXPAND "BucketFillExpand" #define SETTING_BUCKET_FILL_EXPAND_ON "BucketFillExpandEnabled" -#define SETTING_BUCKET_FILL_TO_LAYER_MODE "BucketFillToLayerMode" #define SETTING_BUCKET_FILL_REFERENCE_MODE "BucketFillReferenceMode" #define SETTING_FILL_MODE "FillMode" diff --git a/core_lib/src/util/pointerevent.cpp b/core_lib/src/util/pointerevent.cpp index 3bef76394f..b65c720ac8 100644 --- a/core_lib/src/util/pointerevent.cpp +++ b/core_lib/src/util/pointerevent.cpp @@ -234,6 +234,28 @@ PointerEvent::InputType PointerEvent::inputType() const return InputType::Unknown; } +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + +QInputDevice::DeviceType PointerEvent::device() const +{ + if (mTabletEvent) + { + return mTabletEvent->deviceType(); + } + return QInputDevice::DeviceType::Unknown; +} + +QPointingDevice::PointerType PointerEvent::pointerType() const +{ + if (mTabletEvent) + { + return mTabletEvent->pointerType(); + } + return QPointingDevice::PointerType::Unknown; +} + +#else // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QTabletEvent::TabletDevice PointerEvent::device() const { if (mTabletEvent) @@ -252,4 +274,4 @@ QTabletEvent::PointerType PointerEvent::pointerType() const return QTabletEvent::PointerType::UnknownPointer; } -//QEvent::device +#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) diff --git a/core_lib/src/util/pointerevent.h b/core_lib/src/util/pointerevent.h index d22c6b4837..871fe69a14 100644 --- a/core_lib/src/util/pointerevent.h +++ b/core_lib/src/util/pointerevent.h @@ -73,8 +73,13 @@ class PointerEvent QEvent::Type eventType() const; InputType inputType() const; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QInputDevice::DeviceType device() const; + QPointingDevice::PointerType pointerType() const; +#else QTabletEvent::TabletDevice device() const; QTabletEvent::PointerType pointerType() const; +#endif private: QTabletEvent* mTabletEvent = nullptr; diff --git a/core_lib/src/util/preferencesdef.h b/core_lib/src/util/preferencesdef.h index 551c12d1ea..b71878eed9 100644 --- a/core_lib/src/util/preferencesdef.h +++ b/core_lib/src/util/preferencesdef.h @@ -76,6 +76,8 @@ enum class SETTING TITLE_SAFE_ON, TITLE_SAFE, QUICK_SIZING, + INVERT_DRAG_ZOOM_DIRECTION, + INVERT_SCROLL_ZOOM_DIRECTION, MULTILAYER_ONION, LANGUAGE, LAYOUT_LOCK, diff --git a/core_lib/src/util/util.cpp b/core_lib/src/util/util.cpp index bf67211423..622fb4e44b 100644 --- a/core_lib/src/util/util.cpp +++ b/core_lib/src/util/util.cpp @@ -19,32 +19,40 @@ GNU General Public License for more details. #include #include -QTransform RectMapTransform( QRectF source, QRectF target ) +static inline bool clipLineToEdge(qreal& t0, qreal& t1, qreal p, qreal q) { - qreal x1 = source.left(); - qreal y1 = source.top(); - qreal x2 = source.right(); - qreal y2 = source.bottom(); - qreal x1P = target.left(); - qreal y1P = target.top(); - qreal x2P = target.right(); - qreal y2P = target.bottom(); - - QTransform matrix; - if ( ( x1 != x2 ) && ( y1 != y2 ) ) - { - matrix = QTransform( ( x2P - x1P ) / ( x2 - x1 ), // scale x - 0, - 0, - ( y2P - y1P ) / ( y2 - y1 ), // scale y - ( x1P * x2 - x2P * x1 ) / ( x2 - x1 ), // dx - ( y1P * y2 - y2P * y1 ) / ( y2 - y1 ) ); // dy + if (p < 0) { // Line entering the clipping window + t0 = qMax(t0, q / p); + return t0 < t1; } - else - { - matrix.reset(); + if (p > 0) { // Line leaving the clipping window + t1 = qMin(t1, q / p); + return t0 < t1; } - return matrix; + return q >= 0; +} + +QLineF clipLine(const QLineF& line, const QRect& clip, qreal t0, qreal t1) +{ + int left = clip.left(), right = left + clip.width(), top = clip.top(), bottom = top + clip.height(); + qreal x1 = line.x1(), x2 = line.x2(), dx = line.dx(), y1 = line.y1(), y2 = line.y2(), dy = line.dy(); + + if ((t0 == 0 && t1 == 1 && ((x1 < left && x2 < left) || + (x1 > right && x2 > right) || + (y1 < top && y2 < top) || + (y1 > bottom && y2 > bottom))) || + !clipLineToEdge(t0, t1, -dx, x1 - left) || + !clipLineToEdge(t0, t1, dx, right - x1) || + !clipLineToEdge(t0, t1, -dy, y1 - top) || + !clipLineToEdge(t0, t1, dy, bottom - y1)) { + return {}; + } + + Q_ASSERT(t0 < t1); + return {line.x1() + line.dx() * t0, + line.y1() + line.dy() * t0, + line.x1() + line.dx() * t1, + line.y1() + line.dy() * t1}; } void clearFocusOnFinished(QAbstractSpinBox *spinBox) @@ -106,14 +114,14 @@ quint64 imageSize(const QImage& img) QString uniqueString(int len) { static const char alphanum[] = "0123456789abcdefghijklmnopqrstuvwxyz"; - const int alphanum_len = sizeof(alphanum); + const int alphanumLen = sizeof(alphanum); if (len > 128) len = 128; char s[128 + 1]; for (int i = 0; i < len; ++i) { - s[i] = alphanum[rand() % (alphanum_len - 1)]; + s[i] = alphanum[rand() % (alphanumLen - 1)]; } s[len] = 0; return QString::fromUtf8(s); diff --git a/core_lib/src/util/util.h b/core_lib/src/util/util.h index 4f19feecd4..6a172502dc 100644 --- a/core_lib/src/util/util.h +++ b/core_lib/src/util/util.h @@ -22,7 +22,17 @@ GNU General Public License for more details. class QAbstractSpinBox; -QTransform RectMapTransform( QRectF source, QRectF target ); +/** + * Clips a given line to a clipping window using the Liang-Barsky algorithm. + * @see https://www2.eecs.berkeley.edu/Pubs/TechRpts/1992/6271.html + * + * @param line The line to be clipped + * @param clip The clipping window to use + * @param t0 The starting point of the line to check, as a percentage + * @param t0 The ending point of the line to check, as a percentage + * @return The clipped line, or a null line if the line is completely outside the clipping window + */ +QLineF clipLine(const QLineF& line, const QRect& clip, qreal t0, qreal t1); void clearFocusOnFinished(QAbstractSpinBox *spinBox); diff --git a/docs/build_linux.md b/docs/build_linux.md index 930b4fc1c6..81195359dd 100644 --- a/docs/build_linux.md +++ b/docs/build_linux.md @@ -3,7 +3,7 @@ Building Pencil2D on Linux {#build_linux} These are instructions for building Pencil2D on Linux. If you are using Windows go [here](@ref build_windows), and macOS go [here](@ref build_macos). This guide is primarily targeted towards developers. If you just want to use the latest version you can just download one of our [nightly builds](https://www.pencil2d.org/download/nightly/). -This tutorial was made with Ubuntu Xenial Xerus (16.04) and Arch Linux in mind, however you should be able to adapt this guide to other versions or distributions if necessary. +This tutorial was made with Ubuntu and Arch Linux in mind, however you should be able to adapt this guide to other distributions if necessary. ## Installing Dependencies @@ -21,14 +21,20 @@ Pencil2D relies on the %Qt application framework so you must install it before y - Executing this file will start the %Qt installer application. If you can't open it right away, you may have to right click on it and go to *Properties*, then in the *Permissions* tab select *Allow executing file as program* and then try opening it again. - Click Next. You have to create a free %Qt account if you don't have one. Don't worry, it won't cost you a penny. - Next, specify a location for %Qt, put it somewhere you can find it in case you ever need to navigate to the %Qt files manually. -- Next, you can select the components you wish to install. At the very least you should have Desktop GCC selected under the latest %Qt version. Also make sure %Qt Creator under the Tools section is being installed. +- Next, you can select the components you wish to install. At the very least you should have Desktop GCC selected under the latest %Qt version, as well as its Multimedia module from the Additional Libraries section if you are using Qt 6. Also make sure %Qt Creator under the Tools section is being installed. - Agree to the license and begin the installation. It will take a long time to download all of the files, so be patient. When the installation is complete, press Done and it will launch %Qt Creator for you. ##### Command-line method -Pencil2D must be built with the %Qt 5 framework. To install %Qt 5, run this command: +Pencil2D must be built with the %Qt framework, version 5.6 or newer. Therefore, you will need at least Ubuntu 16.10 (Yakkety Yak) to use this method, as Canonical does not provide recent enough versions of Qt for older versions of Ubuntu. To install %Qt 5, run this command: - sudo apt install qt5-default qt5-qmake libqt5xmlpatterns5-dev libqt5svg5-dev qtmultimedia5-dev + sudo apt install qt5-default qtbase5-dev qtmultimedia5-dev qttools5-dev-tools libqt5svg5-dev + +@note If you are using Ubuntu 20.04 (Focal Fossa) or newer, you can omit qt5-default from the command above. + +If you are using Ubuntu 22.04 (Jammy Jellyfish) or later, you can alternatively install %Qt 6 by running this command: + + sudo apt install qt6-base-dev qt6-l10n-tools qt6-multimedia-dev libqt6svg6-dev For a more pleasant development experience, you might want to install %Qt Creator as well (recommended). To do so, run the following command: @@ -36,9 +42,11 @@ For a more pleasant development experience, you might want to install %Qt Creato #### Arch Linux -Pencil uses version 5 of the %Qt framework. To install all required components of %Qt, run this command: +Pencil uses the %Qt framework, version 5.6 or newer. To install all required components of %Qt 5, run this command: + + sudo pacman -S --needed qt5-base qt5-multimedia qt5-svg qt5-tools gst-plugins-good - sudo pacman -S --needed qt5-multimedia qt5-svg qt5-xmlpatterns qt-gstreamer +If you would like to use %Qt 6 instead, simply replace the version number in the command above. For a more pleasant development experience, you might want to install %Qt Creator as well (recommended). To do so, run the following command: @@ -46,7 +54,7 @@ For a more pleasant development experience, you might want to install %Qt Creato ### Make and GCC/Clang -You will need GNU Make and either GCC or CLANG to build Pencil2D. +You will need GNU Make and either GCC or Clang to build Pencil2D. #### Ubuntu @@ -60,6 +68,7 @@ These are usually installed by default, so you don't have to worry about them. I sudo apt install make clang + #### Arch Linux On most Arch systems, these are installed early on, but if your system does not have them yet, you can install them by running the following commands. @@ -72,6 +81,7 @@ On most Arch systems, these are installed early on, but if your system does not sudo pacman -S --needed make clang + # Get the source code - Simply download a [source code archive](https://github.com/pencil2d/pencil/archive/master.zip), or @@ -103,6 +113,8 @@ Substitute \ for the mkspec of your desired configuration and run the c mkdir build; pushd build; qmake -r -spec CONFIG+=debug ..; popd +@note In order to use %Qt 6, you might need to replace `qmake` with `qmake6`. + Next you have to use GNU Make to actually compile the source code. Run the command: make -C build diff --git a/docs/build_mac.md b/docs/build_mac.md index 1252dac939..0c3897af71 100644 --- a/docs/build_mac.md +++ b/docs/build_mac.md @@ -43,6 +43,7 @@ A dialog should pop up asking if you want to install the command line developer - Next, specify a location for %Qt, put it somewhere you can find it in case you ever need to navigate to the %Qt files manually. - Then choose the %Qt version and components you wish to install. - If you have no idea what to do, select `%Qt 5.15.x -> macOS`. + - If you are using Qt 6, make sure to also select its Multimedia module in the Additional Libraries section. - Also make sure %Qt Creator under the Tools section is being installed. - Agree to the license and begin the installation. It will take a long time to download all of the files, so be patient. When the installation is complete, press `Done` and it will launch %Qt Creator for you. @@ -50,7 +51,11 @@ A dialog should pop up asking if you want to install the command line developer If you have `Homebrew` installed, you can install %Qt 5 framework via Homebrew as well. Run this command: - brew install qt5 + brew install qt@5 + +If you would like to use %Qt 6 instead, simply replace the version number in the command above. + +@warning Please note that there are known issues with the Homebrew build of Qt 6 which may cause Pencil2D to crash. As a workaround, you can try disabling the "Add build library search path to DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH" option in your run configuration after configuring the project in Qt Creator. Otherwise, please use the official builds instead (see above), as they are not affected. For more details, see [this discussion](https://github.com/orgs/Homebrew/discussions/4362). And also run the following commands to install %Qt Creator: diff --git a/docs/build_win.md b/docs/build_win.md index 2364ac3f2d..9b467b40ce 100644 --- a/docs/build_win.md +++ b/docs/build_win.md @@ -26,6 +26,7 @@ Pencil2D is built upon Qt, you need to install it before you can compile the pro - You have to create a free Qt account if you don't have one. Don't worry, it won't cost you a penny. - In the next step, choose the Qt version that matches your C++ compiler. - For example, select `MSVC 2019 64-bit` if you have Visual C++ 2019 installed. + - If you are using Qt 6, make sure to also select its Multimedia module in the Additional Libraries section. - If you have no idea what to do, select the latest `Qt 5.15.x -> MinGW 8.x` and `Developer and Design Tools -> MinGW 8.x 64-bit`. - Agree to the license and start the installation. It will take a long time to download all of the files, so be patient. When the installation is complete, press `Done` and it will launch Qt Creator for you. diff --git a/tests/src/test_bitmapbucket.cpp b/tests/src/test_bitmapbucket.cpp index 3fa0624e30..cf27e52c1d 100644 --- a/tests/src/test_bitmapbucket.cpp +++ b/tests/src/test_bitmapbucket.cpp @@ -75,7 +75,6 @@ TEST_CASE("BitmapBucket - Fill drag behaviour across four segments") dir.mkpath(resultsPath); properties.bucketFillReferenceMode = 0; - properties.bucketFillToLayerMode = 0; properties.bucketFillExpandEnabled = false; properties.fillMode = 0; @@ -92,7 +91,6 @@ TEST_CASE("BitmapBucket - Fill drag behaviour across four segments") // The dragging logic is based around that we only fill on either transparent or the same color as the fill color. SECTION("Filling on current layer - layer is not pre filled") { - properties.bucketFillToLayerMode = 0; Layer* strokeLayer = editor->layers()->currentLayer(); SECTION("When reference is current layer, only transparent color is filled") { @@ -121,7 +119,6 @@ TEST_CASE("BitmapBucket - Fill drag behaviour across four segments") } SECTION("Filling on current layer - layer is pre-filled") { - properties.bucketFillToLayerMode = 0; // Fill mode is set to `replace` because it makes it easier to compare colors... properties.fillMode = 1; @@ -159,73 +156,4 @@ TEST_CASE("BitmapBucket - Fill drag behaviour across four segments") verifyOnlyPixelsInsideSegmentsAreFilled(pressPoint, image, fillColor.rgba()); } } - - // The behaviour changes here because we'll be filling on the layer below, but that layer is blank, - // yet the reference mode tells the flood fill algorithm to fill using the pixel data from the the reference layer. - // In this case it means that all pixels will be filled as we drag across the segments. - SECTION("Filling on layer below - layer is not pre-filled") { - properties.bucketFillToLayerMode = 1; - - Layer* fillLayer = editor->layers()->currentLayer(-1); - SECTION("When reference is current layer, then all pixels not matching the fill color are filled once") - { - properties.bucketFillReferenceMode = 0; - dragAndFill(pressPoint, editor, fillColor, beforeFill.bounds(), properties, 4); - - // Verify that colors are correct on layer below - BitmapImage* image = static_cast(fillLayer)->getLastBitmapImageAtFrame(1); - - image->writeFile(resultsPath + "test4a.png"); - - verifyOnlyPixelsInsideSegmentsAreFilled(pressPoint, image, qPremultiply(fillColor.rgba())); - } - - - SECTION("When reference is all layers, then all pixels not matching the fill color are filled") - { - properties.bucketFillReferenceMode = 1; - - dragAndFill(pressPoint, editor, fillColor, beforeFill.bounds(), properties, 4); - - BitmapImage* image = static_cast(fillLayer)->getLastBitmapImageAtFrame(1); - - image->writeFile(resultsPath + "test5a.png"); - - verifyOnlyPixelsInsideSegmentsAreFilled(pressPoint, image, qPremultiply(fillColor.rgba())); - } - } - - SECTION("Filling on layer below - layer is pre-filled") { - properties.bucketFillToLayerMode = 1; - properties.fillMode = 1; - - SECTION("when reference is all layers, then only pixels matching the fill color are filled") - { - properties.bucketFillReferenceMode = 1; - - Layer* strokeLayer = editor->layers()->currentLayer(); - Layer* fillLayer = editor->layers()->currentLayer(-1); - - // Because the layer is blank, all pixels will be filled once - dragAndFill(pressPoint, editor, fillColor, beforeFill.bounds(), properties, 4); - BitmapImage* image1 = static_cast(strokeLayer)->getLastBitmapImageAtFrame(1); - image1->writeFile(resultsPath + "test6a-first.png"); - - fillColor = QColor(0,255,0,255); - - // Changes fillTo mode to current layer - properties.bucketFillToLayerMode = 0; - - editor->layers()->setCurrentLayer(strokeLayer); - - // Now the layer has been filled with pixel data, so we'll only fill when the color matches the fill color - dragAndFill(pressPoint, editor, fillColor, beforeFill.bounds(), properties, 4); - - BitmapImage* image2 = static_cast(fillLayer)->getLastBitmapImageAtFrame(1); - image1->writeFile(resultsPath + "test6a-second.png"); - image2->writeFile(resultsPath + "test6a-third.png"); - - verifyOnlyPixelsInsideSegmentsAreFilled(pressPoint, image1, fillColor.rgba()); - } - } } diff --git a/tests/src/test_layer.cpp b/tests/src/test_layer.cpp index 39eb0be604..73478d105d 100644 --- a/tests/src/test_layer.cpp +++ b/tests/src/test_layer.cpp @@ -26,43 +26,37 @@ GNU General Public License for more details. TEST_CASE("LayerType") { - Object* object = new Object; - SECTION("Bitmap Layer") { - Layer* bitmapLayer = new LayerBitmap(object); + Layer* bitmapLayer = new LayerBitmap(1); REQUIRE(bitmapLayer->type() == Layer::BITMAP); delete bitmapLayer; } SECTION("Vector Layer") { - Layer* vecLayer = new LayerVector(object); + Layer* vecLayer = new LayerVector(2); REQUIRE(vecLayer->type() == Layer::VECTOR); delete vecLayer; } SECTION("Camera Layer") { - Layer* cameraLayer = new LayerCamera(object); + Layer* cameraLayer = new LayerCamera(3); REQUIRE(cameraLayer->type() == Layer::CAMERA); delete cameraLayer; } SECTION("Sound Layer") { - Layer* soundLayer = new LayerSound(object); + Layer* soundLayer = new LayerSound(4); REQUIRE(soundLayer->type() == Layer::SOUND); delete soundLayer; } - - delete object; } SCENARIO("Add key frames into a Layer", "[Layer]") { - Object* object = new Object; - GIVEN("A Bitmap Layer") { - Layer* layer = new LayerBitmap(object); + Layer* layer = new LayerBitmap(1); REQUIRE(layer->addNewKeyFrameAt(0) == false); // first key position is 1. REQUIRE(layer->keyFrameCount() == 0); @@ -96,7 +90,7 @@ SCENARIO("Add key frames into a Layer", "[Layer]") GIVEN("A Vector Layer") { - Layer* layer = new LayerVector(object); + Layer* layer = new LayerVector(2); REQUIRE(layer->addNewKeyFrameAt(0) == false); // first key position is 1. REQUIRE(layer->keyFrameCount() == 0); @@ -117,7 +111,7 @@ SCENARIO("Add key frames into a Layer", "[Layer]") GIVEN("A Camera Layer") { - Layer* layer = new LayerCamera(object); + Layer* layer = new LayerCamera(3); REQUIRE(layer->addNewKeyFrameAt(0) == false); // first key position is 1. REQUIRE(layer->keyFrameCount() == 0); @@ -135,7 +129,6 @@ SCENARIO("Add key frames into a Layer", "[Layer]") } delete layer; } - delete object; } TEST_CASE("Test Layer::keyExists()", "[Layer]") diff --git a/tests/src/test_layercamera.cpp b/tests/src/test_layercamera.cpp index c37afdc26d..f6899d73f1 100644 --- a/tests/src/test_layercamera.cpp +++ b/tests/src/test_layercamera.cpp @@ -26,11 +26,9 @@ GNU General Public License for more details. SCENARIO("Create camera keyframe with linear easing") { - Object* object = new Object; - GIVEN("A Camera Layer with one keyframe") { - Layer* layer = new LayerCamera(object); + Layer* layer = new LayerCamera(1); LayerCamera* camLayer = static_cast(layer); WHEN("Adding a keyframe") @@ -46,17 +44,13 @@ SCENARIO("Create camera keyframe with linear easing") } } } - - delete object; } SCENARIO("Add a second keyframe and see that the path point of the first keyframe is updated") { - Object* object = new Object; - GIVEN("A Camera layer with multiple keyframes") { - Layer* layer = new LayerCamera(object); + Layer* layer = new LayerCamera(1); LayerCamera* camLayer = static_cast(layer); layer->addNewKeyFrameAt(1); @@ -75,17 +69,13 @@ SCENARIO("Add a second keyframe and see that the path point of the first keyfram } } } - - delete object; } SCENARIO("Add keyframe after having interpolated the previous keyframe and see that the translation is kept") { - Object* object = new Object; - GIVEN("A Camera layer with multiple keyframes") { - Layer* layer = new LayerCamera(object); + Layer* layer = new LayerCamera(1); LayerCamera* camLayer = static_cast(layer); layer->addNewKeyFrameAt(1); @@ -102,17 +92,13 @@ SCENARIO("Add keyframe after having interpolated the previous keyframe and see t } } } - - delete object; } SCENARIO("Remove a camera keyframe and see that the path is properly reset") { - Object* object = new Object; - GIVEN("A Camera layer with multiple keyframes") { - Layer* layer = new LayerCamera(object); + Layer* layer = new LayerCamera(1); LayerCamera* camLayer = static_cast(layer); layer->addNewKeyFrameAt(1); @@ -132,17 +118,13 @@ SCENARIO("Remove a camera keyframe and see that the path is properly reset") } } } - - delete object; } SCENARIO("When deleting an in between keyframe, the previous keyframe will try to recover its initial control point, if possible") { - Object* object = new Object; - GIVEN("A Camera layer with multiple keyframes where the keys are added sequentially") { - Layer* layer = new LayerCamera(object); + Layer* layer = new LayerCamera(1); LayerCamera* camLayer = static_cast(layer); layer->addNewKeyFrameAt(1); @@ -169,7 +151,7 @@ SCENARIO("When deleting an in between keyframe, the previous keyframe will try t GIVEN("A Camera layer with multiple keyframes where the third frame is added in-between") { - Layer* layer = new LayerCamera(object); + Layer* layer = new LayerCamera(2); LayerCamera* camLayer = static_cast(layer); layer->addNewKeyFrameAt(1); @@ -196,17 +178,13 @@ SCENARIO("When deleting an in between keyframe, the previous keyframe will try t } } } - - delete object; } SCENARIO("When adding a keyframe in-between two othes where the control points has been modified, the curve will be preserved") { - Object* object = new Object; - GIVEN("A Camera layer with multiple keyframes where the third frame is added in-between") { - Layer* layer = new LayerCamera(object); + Layer* layer = new LayerCamera(1); LayerCamera* camLayer = static_cast(layer); layer->addNewKeyFrameAt(1); @@ -233,8 +211,6 @@ SCENARIO("When adding a keyframe in-between two othes where the control points h } } } - - delete object; } SCENARIO("Loading a project and see that all camera properties are set, if applicable") diff --git a/tests/tests.pro b/tests/tests.pro index c0a5a8224b..b18992808e 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -6,18 +6,12 @@ ! include( ../util/common.pri ) { error( Could not find the common.pri file! ) } -QT += core widgets gui xml xmlpatterns multimedia svg testlib - TEMPLATE = app - -TARGET = tests - CONFIG += console CONFIG -= app_bundle +QT += core widgets gui xml multimedia svg -MOC_DIR = .moc -OBJECTS_DIR = .obj -DESTDIR = bin +TARGET = tests RESOURCES += data/tests.qrc @@ -52,8 +46,9 @@ SOURCES += \ INCLUDEPATH += $$PWD/../core_lib/src -CONFIG(debug,debug|release) BUILDTYPE = debug -CONFIG(release,debug|release) BUILDTYPE = release +BUILDTYPE = +debug_and_release:CONFIG(debug,debug|release) BUILDTYPE = debug +debug_and_release:CONFIG(release,debug|release) BUILDTYPE = release win32-msvc* { LIBS += -L$$OUT_PWD/../core_lib/$$BUILDTYPE/ -lcore_lib diff --git a/translations/pencil.ts b/translations/pencil.ts index 0642f3a876..144272dbc1 100644 --- a/translations/pencil.ts +++ b/translations/pencil.ts @@ -69,10 +69,10 @@ - - - - + + + + Layer name: @@ -94,7 +94,7 @@ - + You currently have a total of %1 sound clips. Due to current limitations, you will be unable to export any animation exceeding %2 sound clips. We recommend splitting up larger projects into multiple smaller project to stay within this limit. @@ -131,8 +131,8 @@ - - + + Warning @@ -142,74 +142,74 @@ - + Remove selected frames Windows title of remove selected frames pop-up. - + Are you sure you want to remove the selected frames? This action is irreversible currently! - + %1 (copy) Default duplicate layer name - - - + + + Layer Properties - + Bitmap Layer - + Vector Layer - + Layer Properties A popup when creating a new layer - + Camera Layer - + Sound Layer - + Delete Layer Windows title of Delete current layer pop-up. - + Are you sure you want to delete layer: %1? This cannot be undone. - + Please keep at least one camera layer in project text when failed to delete camera layer - + The temporary directory is meant to be used only by Pencil2D. Do not modify it unless you know what you are doing. @@ -802,74 +802,74 @@ CheckUpdatesDialog - + Checking for Updates... status description in the check-for-update dialog - + Download - + Close - + <b>You are using a Pencil2D nightly build</b> - + Please go %1 here %2 to check new nightly builds. - - - + + + <b>An error occurred while checking for updates</b> error msg of check-for-update - + Please check your internet connection and try again later. error msg of check-for-update - + Network response is empty error msg of check-for-update - + Couldn't retrieve the version information error msg of check-for-update - + <b>A new version of Pencil2D is available!</b> - + Pencil2D %1 is now available -- you have %2. Would you like to download it? - + <b>Pencil2D is up to date</b> - + Version %1 @@ -2443,42 +2443,42 @@ CommandLineExporter - + Error: No input file specified. An input project file argument is required when output path(s) are specified. - + Warning: the specified camera layer %1 was not found, ignoring. - + Warning: Output format is not specified or unsupported. Using PNG. Command line warning - + Warning: Transparency is not currently supported in movie files Command line warning - + Exporting movie... Command line task progress - - + + Done. Command line task done - + Exporting image sequence... Command line task progress @@ -2487,101 +2487,101 @@ CommandLineParser - + Pencil2D is an animation/drawing software for Mac OS X, Windows, and Linux. It lets you create traditional hand-drawn animation (cartoon) using both bitmap and vector graphics. - + Path to the input pencil file. - + Render the file to <output_path> - + output_path - + Name of the camera layer to use - + layer_name - + Width of the output frames - - + + integer - + Height of the output frames - + The first frame you want to include in the exported movie - - + + frame - + The last frame you want to include in the exported movie. Can also be last or last-sound to automatically use the last frame containing animation or sound, respectively - + Render transparency when possible - + Warning: width value %1 is not an integer, ignoring. - + Warning: height value %1 is not an integer, ignoring. - + Warning: start value %1 is not an integer, ignoring. - + Warning: start value must be at least 1, ignoring. - + Warning: end value %1 is not an integer, last or last-sound, ignoring. - + Warning: end value %1 is smaller than start value %2, ignoring. @@ -3064,99 +3064,99 @@ FileManager - - - - + + + + Invalid Save Path - + The path is empty. - + The path ("%1") points to a directory. - + The directory ("%1") does not exist. - + The path ("%1") is not writable. - - + + Cannot Create Data Directory - + Failed to create directory "%1". Please make sure you have sufficient permissions. - + "%1" is a file. Please delete the file and try again. - + Miniz Error - - - + + + An internal error occurred. Your file may not be saved successfully. - - + + Internal Error - + Could not open file - + The file does not exist, so we are unable to open it.Please check to make sure the path is correct and try again. - + No permission to read the file. Please check you have read permissions for this file and try again. - + There was an error processing your file. This usually means that your project has been at least partially corrupted. Try again with a newer version of Pencil2D, or try to use a backup file if you have one. If you contact us through one of our official channels we may be able to help you.For reporting issues, the best places to reach us are: - + Bitmap Layer %1 - + Vector Layer %1 - + Sound Layer %1 @@ -3351,17 +3351,27 @@ + Scroll Wheel Zoom + + + + + Invert Scroll Direction + + + + Advanced groupBox title in Preference - + Memory Cache Budget - + MB @@ -3496,12 +3506,12 @@ - + Restart Required - + The language change will take effect after a restart of Pencil2D @@ -4183,7 +4193,7 @@ Read the instructions and try again - + Play @@ -4538,179 +4548,179 @@ Read the instructions and try again - + color palette:<br>use <b>(C)</b><br>toggle at cursor - + Color inspector - + Open Recent - - + + Dialog is already open! - + Please select at least 2 frames! - + Opening document... - - - + + + Abort - - + + Warning - + This program does not currently have permission to write to the file you have selected. Please make sure you have write permission for this file before attempting to save it. Alternatively, you can use the Save As... menu option to save to a writable location. - + Saving document... - + <br><br>An error has occurred and your file may not have saved successfully.If you believe that this error is an issue with Pencil2D, please create a new issue at:<br><a href='https://github.com/pencil2d/pencil/issues'>https://github.com/pencil2d/pencil/issues</a><br>Please be sure to include the following details in your issue: - + This animation has been modified. Do you want to save your changes? - + AutoSave Reminder - + The animation is not saved yet. Do you want to save now? - + Never ask again AutoSave reminder button - + Import failed - + You can only import files ending with .gif. - + Importing Animated GIF... - - + + Undo Menu item text - + Redo Menu item text - + Opening a palette will replace the old palette. Color(s) in strokes will be altered by this action! - + Open Palette - + Stop - + Restore Project? - + Pencil2D didn't close correctly. Would you like to restore the project? - + Restore project - + Recovery Failed. - + Sorry! Pencil2D is unable to restore your project - + Recovery Succeeded! - + Please save your work immediately to prevent loss of data - + Main Toolbar - + View Toolbar - + Overlay Toolbar @@ -4718,47 +4728,47 @@ Color(s) in strokes will be altered by this action! MovieExporter - + Checking environment... - + Generating GIF... - + Assembling audio... - + Generating movie... - + Done - - - - + + + + Something went wrong - - + + Looks like our video backend did not exit normally. Your movie may not have exported correctly. Please try again and report this if it persists. - - + + Couldn't start the video backend, please try again. @@ -4766,99 +4776,99 @@ Color(s) in strokes will be altered by this action! MovieImporter - - + + Bitmap only - - + + You need to be on the bitmap layer to import a movie clip - + Loading video failed - + Could not get duration from the specified video. Are you sure you are importing a valid video file? - + Error creating folder - + Unable to create a temporary folder, cannot import video. - + Imported movie too big! - + The movie clip is too long. Pencil2D can only hold %1 frames, but this movie would go up to about frame %2. Please make your video shorter and try again. - + Unknown error - + This should not happen... - + Video processed, adding frames... - + Failed import - + Was unable to find internal files, import unsuccessful. - + Sound only - + You need to be on a sound layer to import the audio - + Move to an empty frame - + A frame already exists on frame: %1 Move the scrubber to a empty position on the timeline and try again - + FFmpeg Not Found - + Please place the ffmpeg binary in plugins directory and try again @@ -4866,127 +4876,127 @@ Color(s) in strokes will be altered by this action! Object - + error - + Black - + Red - + Dark Red - + Orange - + Dark Orange - + Yellow - + Dark Yellow - + Green - + Dark Green - + Cyan - + Dark Cyan - + Blue - + Dark Blue - + White - + Very Light Grey - + Light Grey - + Grey - + Dark Grey - + Pale Orange Yellow - + Pale Grayish Orange Yellow - + Orange Yellow - + Grayish Orange Yellow - + Light Orange Yellow - + Light Grayish Orange Yellow @@ -5324,63 +5334,63 @@ or cancel ScribbleArea - + Warning - + You are trying to modify a hidden layer! Please select another layer (or make the current layer visible). - + Delete Selection Undo Step: clear the selection area. - - + + Clear Image Undo step text - + There is a gap in your drawing (or maybe you have zoomed too much). - + Sorry! This doesn't always work.Please try again (zoom a bit, click at another location... )<br>if it doesn't work, zoom a bit and check that your paths are connected by pressing F1.). - + Out of bound. Bucket tool fill error message - + Could not find a closed path. Bucket tool fill error message - + Could not find the root index. Bucket tool fill error message - + Flood fill error - + %1<br><br>Error: %2 @@ -5428,572 +5438,572 @@ or cancel - + Action Shortcut table header - + Shortcut Shortcut table header - + Shortcut Conflict! - + %1 is already used, overwrite? - + Save Pencil2D Shortcut file - + untitled.pcls - - + + Pencil2D Shortcut File(*.pcls) - + Open Pencil2D Shortcut file - + Add Frame Shortcut - + Clear Frame Shortcut - + Copy Shortcut - + Paste from Previous Keyframe Shortcut - + Cut Shortcut - + Delete Current Layer Shortcut - + Deselect All Shortcut - + Duplicate Frame Shortcut - + Exit Shortcut - + Export Image Shortcut - + Export Image Sequence Shortcut - + Export Movie Shortcut - + Export Palette Shortcut - + Export Sound Shortcut - + Horizontal Flip Shortcut - + Flip In-Between Shortcut - + Flip Rolling Shortcut - + Vertical Flip Shortcut - + Next Frame Shortcut - + Next Keyframe Shortcut - + Previous Frame Shortcut - + Previous Keyframe Shortcut - + Selection: Add Frame Exposure Shortcut - + Selection: Subtract Frame Exposure Shortcut - + Selection: Reverse Keyframes Shortcut - + Selection: Remove Keyframes Shortcut - + Toggle Grid Shortcut - + Import Image Shortcut - + Import Image Sequence Shortcut - + Import Sound Shortcut - + Show All Layers Shortcut - + Show Current Layer Only Shortcut - + Show Layers Relative to Current Layer Shortcut - + Toggle Loop Shortcut - + Move Frame Backward Shortcut - + Move Frame Forward Shortcut - + New Bitmap Layer Shortcut - + New Camera Layer Shortcut - + New File Shortcut - + New Sound Layer Shortcut - + New Vector Layer Shortcut - + Toggle Next Onion Skin Shortcut - + Toggle Previous Onion Skin Shortcut - + Open File Shortcut - + Paste Shortcut - + Play/Stop Shortcut - + Preferences Shortcut - + Preview Shortcut - + Redo Shortcut - + Remove Frame Shortcut - + Reset Windows Shortcut - + Reset View Shortcut - + Center View Shortcut - + Rotate Anticlockwise Shortcut - + Rotate Clockwise Shortcut - + Reset Rotation Shortcut - + Save File As Shortcut - + Save File Shortcut - + Select All Shortcut - + Toggle Status Bar Visibility Shortcut - + Toggle Color Inspector Window Visibility Shortcut - + Toggle Color Palette Window Visibility Shortcut - + Toggle Color Box Window Visibility Shortcut - + Toggle Onion Skins Window Visibility Shortcut - + Toggle Timeline Window Visibility Shortcut - + Toggle Tools Window Visibility Shortcut - + Toggle Options Window Visibility Shortcut - + Brush Tool Shortcut - + Bucket Tool Shortcut - + Eraser Tool Shortcut - + Eyedropper Tool Shortcut - + Hand Tool Shortcut - + Move Tool Shortcut - + Pen Tool Shortcut - + Pencil Tool Shortcut - + Polyline Tool Shortcut - + Select Tool Shortcut - + Smudge Tool Shortcut - + Undo Shortcut - + Set Zoom to 100% Shortcut - + Set Zoom to 200% Shortcut - + Set Zoom to 25% Shortcut - + Set Zoom to 300% Shortcut - + Set Zoom to 33% Shortcut - + Set Zoom to 400% Shortcut - + Set Zoom to 50% Shortcut - + Zoom In Shortcut - + Zoom Out Shortcut @@ -6035,73 +6045,73 @@ or cancel StatusBar - - + + Click to draw. Hold Ctrl and Shift to erase or Alt to select a color from the canvas. - + Click to erase. - + Click and drag to create or modify a selection. Hold Alt to modify its contents or press Backspace to clear them. - + Click and drag to move an object. Hold Ctrl to rotate. - + Click and drag to move the camera. While on in-between frames, drag handle to change interpolation. - + Click and drag to pan. Hold Ctrl to zoom or Alt to rotate. - + Click to liquefy pixels or modify a vector line. Hold Alt to smooth. - + Click to continue the polyline. Double-click or press enter to complete the line or press Escape to discard it. - + Click to create a new polyline. Hold Ctrl and Shift to erase. - + Click to fill an area with the current color. Hold Alt to select a color from the canvas. - + Click to select a color from the canvas. - + Click to paint. Hold Ctrl and Shift to erase or Alt to select a color from the canvas. - + This file has unsaved changes - + This file has no unsaved changes @@ -6303,12 +6313,12 @@ or cancel TimeLineCells - + Layer Properties - + Layer name: @@ -6742,7 +6752,22 @@ or cancel - + + Hand Tool + + + + + Zoom in by dragging the cursor up instead of down + + + + + Invert Zoom Direction + + + + %1 degrees diff --git a/util/appveyor-mingw.yml b/util/appveyor-mingw.yml index 8eceeea8c6..06f45461a3 100644 --- a/util/appveyor-mingw.yml +++ b/util/appveyor-mingw.yml @@ -35,11 +35,11 @@ build_script: - mingw32-make -j2 after_build: - - windeployqt "%APPVEYOR_BUILD_FOLDER%\build\bin\pencil2d.exe" - + - windeployqt "%APPVEYOR_BUILD_FOLDER%\build\app\release\pencil2d.exe" + test_script: - echo "Running tests" - - cd "%APPVEYOR_BUILD_FOLDER%\build\tests\bin" + - cd "%APPVEYOR_BUILD_FOLDER%\build\tests\release" - tests.exe for: @@ -70,4 +70,4 @@ for: matrix: exclude: - image: Visual Studio 2015 - platform: x64 \ No newline at end of file + platform: x64 diff --git a/util/appveyor-msvc.yml b/util/appveyor-msvc.yml index 96820dca61..a5cf034496 100644 --- a/util/appveyor-msvc.yml +++ b/util/appveyor-msvc.yml @@ -34,11 +34,11 @@ build_script: - nmake after_build: - - windeployqt "%APPVEYOR_BUILD_FOLDER%\build\bin\pencil2d.exe" + - windeployqt "%APPVEYOR_BUILD_FOLDER%\build\app\release\pencil2d.exe" test_script: - echo "Running tests" - - tests\bin\tests.exe + - tests\release\tests.exe for: - matrix: @@ -87,4 +87,4 @@ for: matrix: exclude: - image: Visual Studio 2013 - platform: x64 \ No newline at end of file + platform: x64 diff --git a/util/common.pri b/util/common.pri index f9fc6dea96..f23947401b 100644 --- a/util/common.pri +++ b/util/common.pri @@ -1,4 +1,3 @@ - VERSION = 0.6.6 DEFINES += APP_VERSION=\\\"$$VERSION\\\"