From bb5598a299e4c27b9424584f5051679e4cbf412f Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Fri, 4 Aug 2023 10:45:22 -0600 Subject: [PATCH 01/26] Deselect all frames when duplicating a key This fixes a bug where duplicating a selected frame results in the layer selected frame list not matching the frame's selected member. --- app/src/actioncommands.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 6d1bac132..28f1ed6ee 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -733,6 +733,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; @@ -757,6 +761,7 @@ void ActionCommands::duplicateKey() } mEditor->layers()->notifyAnimationLengthChanged(); + emit mEditor->layers()->currentLayerChanged(mEditor->layers()->currentLayerIndex()); // trigger timeline repaint. } void ActionCommands::moveFrameForward() From c4e61e3472637e2f9b2f3358675aad4fa225c9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Corr=C3=AAa?= <75134774+HeCorr@users.noreply.github.com> Date: Sun, 6 Aug 2023 08:34:17 -0300 Subject: [PATCH 02/26] Implement preferences for inverting drag and scroll zoom (#1780) * add 'Invert Zoom Direction' preference option adds a `Hand Tool` section in the Tools preference UI containing a `Invert Zoom Direction` checkbox which defaults to false. also adds PreferenceManager logic around the checkbox. * change HandTool's zoom behavior based on preference it takes into account the INVERT_ZOOM_DIRECTION setting when applying zoom. when it's true, zoom in by dragging the cursor up, down otherwise. * rename INVERT_ZOOM_DIRECTION to INVERT_DRAG_ZOOM_DIRECTION because I need to add another enum that controls scroll wheel zoom and they would look too similar. * add scroll wheel zoom direction preference UI + PreferenceManager logic * invert scroll wheel zoom direction based on preference it takes the INVERT_SCROLL_ZOOM_DIRECTION setting into consideration when applying zoom. * change phrasing on checkbox tooltip * replace mInvertScrollDirection with mDeltaFactor ...on ScribbleArea as suggested by Jakob * forgot to initialize mDeltaFactor on ScribbleArea::init() oops! * replace mInvertZoomDirection with mDeltaFactor ...on HandTool as suggested by Jakob --- app/src/generalpage.cpp | 8 +++++ app/src/generalpage.h | 1 + app/src/toolspage.cpp | 7 +++++ app/src/toolspage.h | 1 + app/ui/generalpage.ui | 34 ++++++++++++++++++-- app/ui/toolspage.ui | 35 +++++++++++++++++++-- core_lib/src/interface/scribblearea.cpp | 9 ++++-- core_lib/src/interface/scribblearea.h | 1 + core_lib/src/managers/preferencemanager.cpp | 9 ++++++ core_lib/src/tool/handtool.cpp | 21 +++++++++++-- core_lib/src/tool/handtool.h | 3 ++ core_lib/src/util/pencildef.h | 3 ++ core_lib/src/util/preferencesdef.h | 2 ++ 13 files changed, 125 insertions(+), 9 deletions(-) diff --git a/app/src/generalpage.cpp b/app/src/generalpage.cpp index 66288a7b2..07a203801 100644 --- a/app/src/generalpage.cpp +++ b/app/src/generalpage.cpp @@ -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) @@ -296,3 +299,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 d36b86e49..cdfdac634 100644 --- a/app/src/generalpage.h +++ b/app/src/generalpage.h @@ -56,6 +56,7 @@ private slots: void curveSmoothingChanged(int value); void backgroundChanged(int value); void frameCacheNumberChanged(int value); + void invertScrollDirectionBoxStateChanged(int b); private: diff --git a/app/src/toolspage.cpp b/app/src/toolspage.cpp index 6c7176dfb..e090fb92c 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 3755f61ff..dcf642e8c 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/generalpage.ui b/app/ui/generalpage.ui index 0418f4d27..5586b0b6b 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/toolspage.ui b/app/ui/toolspage.ui index 3d968d4bc..30754fce7 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/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index fcd978f42..fc9bcee7a 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -89,6 +89,8 @@ bool ScribbleArea::init() 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 +159,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; } @@ -536,14 +541,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(); diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 9f89da382..6b443ee2f 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -256,6 +256,7 @@ public slots: qreal mCurveSmoothingLevel = 0.0; bool mMultiLayerOnionSkin = false; // future use. If required, just add a checkbox to updated it. QColor mOnionColor; + int mDeltaFactor = 1; private: diff --git a/core_lib/src/managers/preferencemanager.cpp b/core_lib/src/managers/preferencemanager.cpp index fe79aa87f..229881a8f 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/tool/handtool.cpp b/core_lib/src/tool/handtool.cpp index 5d3dc795d..8b21aeb57 100644 --- a/core_lib/src/tool/handtool.cpp +++ b/core_lib/src/tool/handtool.cpp @@ -41,6 +41,23 @@ 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() @@ -110,8 +127,8 @@ void HandTool::transformView(Qt::KeyboardModifiers keyMod, Qt::MouseButtons butt } 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 8d8e4742d..1f17747b7 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/util/pencildef.h b/core_lib/src/util/pencildef.h index a125b0697..040a2793d 100644 --- a/core_lib/src/util/pencildef.h +++ b/core_lib/src/util/pencildef.h @@ -255,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" diff --git a/core_lib/src/util/preferencesdef.h b/core_lib/src/util/preferencesdef.h index 551c12d1e..b71878eed 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, From 7b0f1207179852b4203984940aacf9e76408e219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Corr=C3=AAa?= <75134774+HeCorr@users.noreply.github.com> Date: Tue, 8 Aug 2023 02:53:57 -0300 Subject: [PATCH 03/26] Invert canvas rotation direction if view is flipped (#1778) * invert action rotation direction if view is flipped see #1777 * invert hand tool rotation direction if view is flipped see #1777 * create ViewManager::rotateRelative function similar to `rotate()` but doesn't require caller to know the current view rotation. * replace XOR operator on HandTool::transformView as requested by MrStevns. * refactor ActionCommands rotation functions - replace XOR operators with `A == !B` - replace `if` with ternary operator - use rotateRelative() instead of rotate() - define a delta constant and call rotateRelative() once * simplify rotation code in HandTool::transformView() - replace `if` with ternary operator - replace rotate() with rotateRelative() --- app/src/actioncommands.cpp | 10 ++++++---- core_lib/src/managers/viewmanager.cpp | 8 ++++++++ core_lib/src/managers/viewmanager.h | 1 + core_lib/src/tool/handtool.cpp | 6 ++++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 28f1ed6ee..1b8102e6a 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -547,14 +547,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() diff --git a/core_lib/src/managers/viewmanager.cpp b/core_lib/src/managers/viewmanager.cpp index fd79d3ea0..928d11d18 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); diff --git a/core_lib/src/managers/viewmanager.h b/core_lib/src/managers/viewmanager.h index 3a8d2d12d..1c0009c4d 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/tool/handtool.cpp b/core_lib/src/tool/handtool.cpp index 8b21aeb57..e28e20299 100644 --- a/core_lib/src/tool/handtool.cpp +++ b/core_lib/src/tool/handtool.cpp @@ -122,8 +122,10 @@ 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) { From 852b73baa22113fa3143a3149803497a61743f9a Mon Sep 17 00:00:00 2001 From: Oliver Stevns <1045397+MrStevns@users.noreply.github.com> Date: Tue, 8 Aug 2023 23:32:17 +0200 Subject: [PATCH 04/26] Fix three point perspective overlay anchors (#1771) * Fix Three point perspective can't drag left/right point * Remove dead code --- core_lib/src/managers/overlaymanager.cpp | 21 ++------------------- core_lib/src/managers/overlaymanager.h | 1 - 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/core_lib/src/managers/overlaymanager.cpp b/core_lib/src/managers/overlaymanager.cpp index 1f8a19e22..61f871dd9 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 97cf9df54..4fcba37b2 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; } From ec22990d99fdc3e80fa6efe8b5a364e8d81de750 Mon Sep 17 00:00:00 2001 From: Jakob Date: Wed, 9 Aug 2023 20:08:12 +0200 Subject: [PATCH 05/26] Add support for Qt 6 (#1743) * Add support for Qt 6 * Add Qt 6 to CI * Update build guides for Qt 6 --- .github/actions/create-package/action.yml | 4 ++ .../actions/create-package/create-package.sh | 18 +++++---- .../actions/install-dependencies/action.yml | 6 +++ .../install-dependencies.sh | 30 ++++++++++----- .../setup-environment/setup-environment.sh | 4 +- .github/workflows/ci.yml | 36 +++++++++++++++--- .github/workflows/docs.yml | 2 +- Doxyfile | 3 -- app/app.pro | 2 +- app/src/basedockwidget.cpp | 4 +- app/src/checkupdatesdialog.cpp | 7 ++-- app/src/colorinspector.cpp | 6 +-- app/src/commandlineexporter.cpp | 28 ++++++++------ app/src/commandlineparser.cpp | 18 ++++++--- app/src/generalpage.cpp | 20 ++++------ app/src/generalpage.h | 3 +- app/src/importimageseqdialog.cpp | 2 +- app/src/mainwindow2.cpp | 11 +++++- app/src/pencil2d.cpp | 4 ++ app/src/shortcutspage.cpp | 1 - app/src/spinslider.cpp | 2 +- app/src/statusbar.cpp | 4 ++ app/src/timeline.cpp | 10 ++--- app/src/timelinecells.cpp | 11 +++--- core_lib/core_lib.pro | 4 +- core_lib/src/external/linux/linux.cpp | 2 + core_lib/src/external/win32/win32.cpp | 2 + core_lib/src/graphics/vector/beziercurve.cpp | 8 ++++ core_lib/src/graphics/vector/vertexref.cpp | 8 ++-- core_lib/src/graphics/vector/vertexref.h | 8 ++-- core_lib/src/interface/backgroundwidget.cpp | 2 +- core_lib/src/interface/editor.h | 12 ++++++ core_lib/src/interface/flowlayout.cpp | 7 ++-- core_lib/src/interface/scribblearea.cpp | 12 +++++- core_lib/src/movieexporter.cpp | 37 ++++++++++++++----- core_lib/src/movieimporter.cpp | 7 +++- core_lib/src/soundplayer.cpp | 10 +++++ core_lib/src/structure/filemanager.cpp | 5 +-- core_lib/src/structure/layercamera.cpp | 8 +++- core_lib/src/structure/object.cpp | 12 +++++- core_lib/src/tool/basetool.cpp | 2 +- core_lib/src/tool/movetool.cpp | 2 +- core_lib/src/tool/selecttool.cpp | 6 +-- core_lib/src/util/pointerevent.cpp | 24 +++++++++++- core_lib/src/util/pointerevent.h | 5 +++ docs/build_linux.md | 24 +++++++++--- docs/build_mac.md | 6 ++- tests/tests.pro | 2 +- 48 files changed, 324 insertions(+), 127 deletions(-) diff --git a/.github/actions/create-package/action.yml b/.github/actions/create-package/action.yml index aa3420ddb..989fb4a96 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 edd252288..7c6405f2c 100755 --- a/.github/actions/create-package/create-package.sh +++ b/.github/actions/create-package/create-package.sh @@ -39,12 +39,13 @@ 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::" } @@ -67,8 +68,9 @@ 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 @@ -80,8 +82,9 @@ 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() { @@ -105,8 +108,9 @@ create_package_windows() { local _xbits="_x${platform#win}" cp "${IQTA_TOOLS}\\OpenSSL\\Win${_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 91df7a19e..a1d840d26 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,11 @@ 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 b199caff3..d07845258 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 fcb4db2d8..37236bb95 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 fadad09ab..70196cd4f 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+=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 @@ -83,11 +106,14 @@ jobs: - 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 de5c077c1..ae372cbed 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 dc73fff5e..a537ef806 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/app/app.pro b/app/app.pro index e79852972..714312b77 100644 --- a/app/app.pro +++ b/app/app.pro @@ -12,7 +12,7 @@ TEMPLATE = app TARGET = pencil2d QMAKE_APPLICATION_BUNDLE_NAME = Pencil2D -CONFIG += qt precompile_header lrelease embed_translations +CONFIG += precompile_header lrelease embed_translations DESTDIR = ../bin MOC_DIR = .moc diff --git a/app/src/basedockwidget.cpp b/app/src/basedockwidget.cpp index a99a30c8d..d052dd1cc 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/checkupdatesdialog.cpp b/app/src/checkupdatesdialog.cpp index 0a2dc1356..8723062da 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 3196090fa..6b8c72cdf 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/commandlineexporter.cpp b/app/src/commandlineexporter.cpp index 2b258f449..e4851a395 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 dc8e8e0dd..3685da12c 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/generalpage.cpp b/app/src/generalpage.cpp index 07a203801..ff0406835 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); @@ -196,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); } diff --git a/app/src/generalpage.h b/app/src/generalpage.h index cdfdac634..239e6b8b9 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,7 +55,7 @@ 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); diff --git a/app/src/importimageseqdialog.cpp b/app/src/importimageseqdialog.cpp index a4ac70378..896067a88 100644 --- a/app/src/importimageseqdialog.cpp +++ b/app/src/importimageseqdialog.cpp @@ -364,7 +364,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/mainwindow2.cpp b/app/src/mainwindow2.cpp index bbe92fea0..14fec8f62 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); @@ -1003,7 +1006,11 @@ void MainWindow2::importGIF() 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) { diff --git a/app/src/pencil2d.cpp b/app/src/pencil2d.cpp index 3c52edf80..d7c40e9dd 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 0eab0dc7b..3cc3e4798 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 b7efb602c..a9bf22627 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 73ace9bd0..a34e51d27 100644 --- a/app/src/statusbar.cpp +++ b/app/src/statusbar.cpp @@ -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()) { diff --git a/app/src/timeline.cpp b/app/src/timeline.cpp index 77baecc4d..af5bd22c1 100644 --- a/app/src/timeline.cpp +++ b/app/src/timeline.cpp @@ -92,7 +92,7 @@ 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); @@ -112,7 +112,7 @@ 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); @@ -167,14 +167,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 +189,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 a37a6400a..a25805aab 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" @@ -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/core_lib/core_lib.pro b/core_lib/core_lib.pro index e928b977f..2b25e8d86 100644 --- a/core_lib/core_lib.pro +++ b/core_lib/core_lib.pro @@ -6,10 +6,10 @@ ! include( ../util/common.pri ) { error( Could not find the common.pri file! ) } -QT += core widgets gui xml xmlpatterns multimedia svg +QT += core widgets gui xml multimedia svg TEMPLATE = lib -CONFIG += qt staticlib precompile_header +CONFIG += staticlib precompile_header RESOURCES += data/core_lib.qrc diff --git a/core_lib/src/external/linux/linux.cpp b/core_lib/src/external/linux/linux.cpp index 2b0f3e665..16e86c17d 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/win32/win32.cpp b/core_lib/src/external/win32/win32.cpp index 82222e592..eef2c1737 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/vector/beziercurve.cpp b/core_lib/src/graphics/vector/beziercurve.cpp index 5410d5eef..97c2a79d6 100644 --- a/core_lib/src/graphics/vector/beziercurve.cpp +++ b/core_lib/src/graphics/vector/beziercurve.cpp @@ -860,7 +860,11 @@ 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 "; @@ -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/vertexref.cpp b/core_lib/src/graphics/vector/vertexref.cpp index 307873f2f..b31765d1f 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 8e5c48760..3959fa2bd 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 bb17b3997..ebee13a2b 100644 --- a/core_lib/src/interface/backgroundwidget.cpp +++ b/core_lib/src/interface/backgroundwidget.cpp @@ -69,7 +69,7 @@ void BackgroundWidget::settingUpdated(SETTING setting) void BackgroundWidget::paintEvent(QPaintEvent *) { QStyleOption opt; - opt.init(this); + opt.initFrom(this); QPainter painter(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); diff --git a/core_lib/src/interface/editor.h b/core_lib/src/interface/editor.h index e2700bf4a..0b5723d3d 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; diff --git a/core_lib/src/interface/flowlayout.cpp b/core_lib/src/interface/flowlayout.cpp index de2c5f96e..032f065c8 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 fc9bcee7a..998c39897 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -531,7 +531,11 @@ void ScribbleArea::wheelEvent(QWheelEvent* event) 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 @@ -559,7 +563,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(); } @@ -634,7 +642,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()) @@ -647,7 +655,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); diff --git a/core_lib/src/movieexporter.cpp b/core_lib/src/movieexporter.cpp index 08d52898c..a32726a40 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 e4cdd89ca..d9389bf5a 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/soundplayer.cpp b/core_lib/src/soundplayer.cpp index 278c4abcc..6120b67b0 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 a24db42fd..ae9ddd112 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/layercamera.cpp b/core_lib/src/structure/layercamera.cpp index 1ff1fbc35..c41507f5b 100644 --- a/core_lib/src/structure/layercamera.cpp +++ b/core_lib/src/structure/layercamera.cpp @@ -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); } } diff --git a/core_lib/src/structure/object.cpp b/core_lib/src/structure/object.cpp index 0a5871853..30391fed5 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" @@ -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; } @@ -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) { diff --git a/core_lib/src/tool/basetool.cpp b/core_lib/src/tool/basetool.cpp index 56bd359bc..c6029edce 100644 --- a/core_lib/src/tool/basetool.cpp +++ b/core_lib/src/tool/basetool.cpp @@ -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); diff --git a/core_lib/src/tool/movetool.cpp b/core_lib/src/tool/movetool.cpp index 20f459382..8ddc3a3da 100644 --- a/core_lib/src/tool/movetool.cpp +++ b/core_lib/src/tool/movetool.cpp @@ -367,7 +367,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) { diff --git a/core_lib/src/tool/selecttool.cpp b/core_lib/src/tool/selecttool.cpp index 6b9d8725d..df24930ae 100644 --- a/core_lib/src/tool/selecttool.cpp +++ b/core_lib/src/tool/selecttool.cpp @@ -43,14 +43,14 @@ 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) { @@ -79,7 +79,7 @@ QCursor SelectTool::cursor() default: Q_UNREACHABLE(); } - return mCursorPixmap; + return QCursor(mCursorPixmap); } void SelectTool::resetToDefault() diff --git a/core_lib/src/util/pointerevent.cpp b/core_lib/src/util/pointerevent.cpp index 3bef76394..b65c720ac 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 d22c6b483..871fe69a1 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/docs/build_linux.md b/docs/build_linux.md index 930b4fc1c..f86ccda82 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 @@ -26,9 +26,15 @@ Pencil2D relies on the %Qt application framework so you must install it before y ##### 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 qt-gstreamer - 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 1252dac93..7ce2127a9 100644 --- a/docs/build_mac.md +++ b/docs/build_mac.md @@ -50,7 +50,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/tests/tests.pro b/tests/tests.pro index c0a5a8224..9bba5c9f3 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -6,7 +6,7 @@ ! include( ../util/common.pri ) { error( Could not find the common.pri file! ) } -QT += core widgets gui xml xmlpatterns multimedia svg testlib +QT += core widgets gui xml multimedia svg testlib TEMPLATE = app From 160435c6700b6bfc791b8dd70faf027a4f4478c9 Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Wed, 9 Aug 2023 20:25:41 +0200 Subject: [PATCH 06/26] Update translation sources --- translations/pencil.ts | 665 +++++++++++++++++++++-------------------- 1 file changed, 345 insertions(+), 320 deletions(-) diff --git a/translations/pencil.ts b/translations/pencil.ts index 0642f3a87..144272dbc 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 From 339d074e2f39085f7a576141ad47891bae5df613 Mon Sep 17 00:00:00 2001 From: thomnath <91194645+thomnath@users.noreply.github.com> Date: Thu, 31 Aug 2023 14:14:46 -0500 Subject: [PATCH 07/26] Update README.md to improve the presentation of the project (#1781) * Update README.md Small changes to improve the presentation of the project: removed broken link, added tutorials link, added additional text * Update README.md --------- Co-authored-by: Jakob Gahde --- README.md | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index a8c1e1dc0..005d611c7 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ **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. -Here is a video demonstrating the project: - Pencil2D Website: ### User Showcase @@ -14,42 +12,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 + +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. -[user-man]: https://www.pencil2d.org/doc/user-manual.html +[Download Nightly Buildls](https://www.pencil2d.org/download/nightly/) -If you run into any issues, make sure to check out the [FAQ][p2d-faq] +## Using Pencil2D -[p2d-faq]: https://www.pencil2d.org/doc/faq.html +Note that the following guides and documentation are outdated. +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 +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! -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. +[user-man]: https://www.pencil2d.org/doc/user-manual.html -[Download Nightly Buildls](https://www.pencil2d.org/download/nightly/) +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 From 19eabfedfc10587e6c7999ef393613634308fecc Mon Sep 17 00:00:00 2001 From: minhhuynh <71852804+minhhuynh103@users.noreply.github.com> Date: Fri, 1 Sep 2023 02:39:40 +0700 Subject: [PATCH 08/26] Update README.md to make it sound more welcoming (#1783) * Update README.md Add some welcome introductions to make it sounds more welcoming and the community standard that is not explicitly stated for newcomers. * Update README.md --------- Co-authored-by: Jakob Gahde --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 005d611c7..0da5a5895 100644 --- a/README.md +++ b/README.md @@ -2,9 +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. -Pencil2D Website: +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. + +Don’t forget to check out our official website: ### User Showcase From b99ceb658a6f40913c4a06225cfeb477dc434835 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sun, 24 Sep 2023 14:24:11 +0200 Subject: [PATCH 09/26] Fix OpenSSL build failure (#1788) --- .github/actions/create-package/create-package.sh | 8 +++++--- .github/actions/install-dependencies/action.yml | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/actions/create-package/create-package.sh b/.github/actions/create-package/create-package.sh index 7c6405f2c..8a516415e 100755 --- a/.github/actions/create-package/create-package.sh +++ b/.github/actions/create-package/create-package.sh @@ -104,9 +104,11 @@ create_package_windows() { 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" local qtsuffix="-qt${INPUT_QT}" "${WINDIR}\\System32\\tar" caf "pencil2d${qtsuffix/-qt5/}-${platform}-$1-$(date +%F).zip" Pencil2D diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index a1d840d26..ab8d9f5f8 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -19,6 +19,5 @@ runs: 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' || ''}} From 1395c86cb17dafbb32de44cbabe1f4c58636468d Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Sun, 24 Sep 2023 21:38:23 +0200 Subject: [PATCH 10/26] Remove nonexistent package qt-gstreamer from Arch build instructions --- docs/build_linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build_linux.md b/docs/build_linux.md index f86ccda82..c591ec298 100644 --- a/docs/build_linux.md +++ b/docs/build_linux.md @@ -44,7 +44,7 @@ For a more pleasant development experience, you might want to install %Qt Creato 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 qt-gstreamer + sudo pacman -S --needed qt5-base qt5-multimedia qt5-svg qt5-tools gst-plugins-good If you would like to use %Qt 6 instead, simply replace the version number in the command above. From a4d63308eb43f374a673becebd0e2b275743c4ea Mon Sep 17 00:00:00 2001 From: Oliver Stevns <1045397+MrStevns@users.noreply.github.com> Date: Sun, 1 Oct 2023 09:10:57 +0200 Subject: [PATCH 11/26] Painting performance improvements - Tiled buffer (#1776) * Canvaspainter improvement WIP * Implement tiled buffer for faster painting WIP * CanvasPainter painting improvements - Split painting for current frame and onion skinning - Introduce blitRect for updating only the dirty portion of the frame - Optimization: Only do an expensive fill when the size changes, otherwise rely on the blitRect to clear the dirty portion. * Only update the dirty potion Aside from cleaning up various extra update methods that are no longer necessary, I've made a minor change in vectorImage, in order to get it's correct dirty region. * Fix vector selection being slightly wider than the overall bounds This was a necessary change in order to calculate the correct bounds that wouldn't draw artifacts. * Fix stroke placement for vector polyline This also fixes some inconsistency in the CanvasPainter that I couldn't make sense of.. * Implement faster hashing * Do some cleanup * Fix next onion skin being drawn on current frame when it's the last * Re-add loadFile if the bitmap image hasn't been loaded yet Although i don't like that this exists in our painter, I believe we added it because for some reason the image hasn't been loaded yet, so let's leave it for now... ideally though i've really like to get rid of such weird mutable things that has little to do with painting. * Update CanvasPainter interface to better match its usage * Cleanup TiledBuffer * Make polyline work * Fix blur logic of smudge tool Additionally this will fix some artifacts when smudging * Replace use of BitmapImage buffer with TiledBuffer - Note that VectorImage hasn't been implemented yet. * Make TiledBuffer work for VectorImage * Cleanup dead code in TiledBuffer * Fix not painting bitmap layers when not on current frame * Add missing license * Remove dead code * Fix drawImage would not get proper update bounds * Cleanup TiledBuffer and fix warnings * Fix Eraser should use destinationOut composition * Fix anti aliasing was causing unwanted floating precision error This was causing the gaps to be drawn between the tiled buffer tiles in some cases... particularly when erasing and you had zoomed in or out. * Fix Polyline can't be removed after applying on vector * Fix transformed selection was being painted in wrong order * Cleanup after testing * Fix tabletRestorePrevTool would always ask for a full canvas update * Remove unnecessary clear of cache when setting tool * Fix update artifacts when using dotted cursor - In addition this fix makes it possible to ignore an additional update event which would previously be done by updateCanvasCursor, but because the TiledBuffer will handle this now, we only update the cursor when a tool is not active. * Refactor TiledBuffer - Also add missing onClearTile callback, which is unused but implementation wise is correct now. * Some cleanup * Remove redundant clear of tile before deleting it * Update signal/slot naming convention * Apply some refactoring to tile * Fix blitRect::extend would make tiles odd numbered * Remove unused methods * Avoid invalidating cache till we're done drawing * Remove various dead code from ScribbleArea * Fix ScribbleArea repaint when tiled buffer does not cover update rect * Fix canvas pixmap being too big when using devicePixelRatio > 1 * Try to fix seams appearing in Qt 6 * Fix canvas not being updated when using camera tool * Cleanup CanvasPainter * Adjust blitRect explanation * Remove the need to allocate an empty QPointF to draw pixmaps * TiledBuffer: Remove unused signals * Fix typo * Fix compiler warning --------- Co-authored-by: Jakob Gahde --- app/src/actioncommands.cpp | 4 +- app/src/colorpalettewidget.cpp | 2 +- app/src/mainwindow2.cpp | 4 +- core_lib/core_lib.pro | 4 + core_lib/src/canvaspainter.cpp | 82 +++-- core_lib/src/canvaspainter.h | 16 +- core_lib/src/graphics/bitmap/bitmapimage.cpp | 31 +- core_lib/src/graphics/bitmap/bitmapimage.h | 3 + core_lib/src/graphics/bitmap/tile.cpp | 47 +++ core_lib/src/graphics/bitmap/tile.h | 50 +++ core_lib/src/graphics/bitmap/tiledbuffer.cpp | 166 ++++++++++ core_lib/src/graphics/bitmap/tiledbuffer.h | 86 +++++ core_lib/src/graphics/vector/vectorimage.cpp | 2 + core_lib/src/interface/editor.cpp | 9 +- core_lib/src/interface/editor.h | 8 +- core_lib/src/interface/scribblearea.cpp | 316 +++++++------------ core_lib/src/interface/scribblearea.h | 46 +-- core_lib/src/managers/toolmanager.cpp | 10 +- core_lib/src/tool/basetool.cpp | 2 +- core_lib/src/tool/brushtool.cpp | 21 +- core_lib/src/tool/brushtool.h | 4 +- core_lib/src/tool/buckettool.cpp | 4 +- core_lib/src/tool/cameratool.cpp | 8 +- core_lib/src/tool/erasertool.cpp | 12 +- core_lib/src/tool/movetool.cpp | 4 +- core_lib/src/tool/penciltool.cpp | 15 +- core_lib/src/tool/penciltool.h | 1 - core_lib/src/tool/pentool.cpp | 13 +- core_lib/src/tool/pentool.h | 1 - core_lib/src/tool/polylinetool.cpp | 11 +- core_lib/src/tool/selecttool.cpp | 6 +- core_lib/src/tool/smudgetool.cpp | 7 +- core_lib/src/tool/stroketool.cpp | 1 + core_lib/src/tool/stroketool.h | 2 +- core_lib/src/util/blitrect.cpp | 11 +- 35 files changed, 643 insertions(+), 366 deletions(-) create mode 100644 core_lib/src/graphics/bitmap/tile.cpp create mode 100644 core_lib/src/graphics/bitmap/tile.h create mode 100644 core_lib/src/graphics/bitmap/tiledbuffer.cpp create mode 100644 core_lib/src/graphics/bitmap/tiledbuffer.h diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 1b8102e6a..35727c69e 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -885,7 +885,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(); } } @@ -900,7 +900,7 @@ void ActionCommands::changeallKeyframeLineColor() if (layer->keyExists(i)) layer->getBitmapImageAtFrame(i)->fillNonAlphaPixels(color); } - mEditor->updateFrame(mEditor->currentFrame()); + mEditor->updateFrame(); } } diff --git a/app/src/colorpalettewidget.cpp b/app/src/colorpalettewidget.cpp index 7f86fcd60..b65b656dc 100644 --- a/app/src/colorpalettewidget.cpp +++ b/app/src/colorpalettewidget.cpp @@ -636,7 +636,7 @@ void ColorPaletteWidget::clickRemoveColorButton() { fitSwatchSize(); } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } bool ColorPaletteWidget::showPaletteWarning() diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index 14fec8f62..f59aaa32c 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -879,7 +879,7 @@ void MainWindow2::importImage() return; } - ui->scribbleArea->updateCurrentFrame(); + ui->scribbleArea->updateFrame(); mTimeLine->updateContent(); } @@ -1449,7 +1449,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); diff --git a/core_lib/core_lib.pro b/core_lib/core_lib.pro index 2b25e8d86..69bd7df0f 100644 --- a/core_lib/core_lib.pro +++ b/core_lib/core_lib.pro @@ -34,6 +34,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 +121,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/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 529149764..ef1324065 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,7 +159,7 @@ 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, QRect rect, TiledBuffer* tiledBuffer) { Q_UNUSED(rect) Q_ASSERT(object); @@ -160,7 +168,7 @@ void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int CANVASPAINTER_LOG("Set CurrentLayerIndex = %d", currentLayer); mCurrentLayerIndex = currentLayer; mFrameNumber = frame; - mBuffer = buffer; + mTiledBuffer = tiledBuffer; } void CanvasPainter::paint(const QRect& blitRect) @@ -176,7 +184,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 +194,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 +238,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) @@ -245,10 +253,10 @@ void CanvasPainter::paintVectorOnionSkinFrame(QPainter& painter, const QRect& bl initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); vectorImage->paintImage(onionSkinPainter, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); - paintOnionSkinFrame(painter, onionSkinPainter, blitRect, nFrame, colorize, vectorImage->getOpacity()); + 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 +280,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 +291,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 +299,35 @@ 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) + { + // Certain tools require being painted continuously, for example, the Polyline tool. + // The tiled buffer does not update the area outside which it paints, + // so in that case, in order to see the previously laid-down polyline stroke, + // the surrounding area must be drawn again before + // applying the new tiled output on top + if (!blitRect.contains(mTiledBuffer->bounds()) || mOptions.bIgnoreCanvasBuffer) { + currentBitmapPainter.setCompositionMode(QPainter::CompositionMode_Source); + 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); + 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()); } - painter.drawPixmap(blitRect, mCurrentLayerPixmap, blitRect); + // We do not wish to draw selection transformations on anything but the current layer + if (isCurrentLayer && mRenderTransform) { + paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); + } + + painter.drawPixmap(mPointZero, mCurrentLayerPixmap); } void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blitRect, Layer* layer, bool isCurrentLayer) @@ -318,7 +342,7 @@ 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); @@ -326,7 +350,11 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit 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 +364,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 58ad6e993..66c6e1d98 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,10 @@ struct CanvasPainterOptions bool bAntiAlias = false; bool bThinLines = false; bool bOutlines = false; + + /// When using a tool that can't rely on canvas buffer, + /// for example Polyline because we're continously clearing the surface + bool bIgnoreCanvasBuffer = false; LayerVisibility eLayerVisibility = LayerVisibility::RELATED; float fLayerVisibilityThreshold = 0.f; float scaling = 1.0f; @@ -61,7 +67,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, QRect rect, TiledBuffer* tilledBuffer); void paint(const QRect& blitRect); void paintCached(const QRect& blitRect); void resetLayerCache(); @@ -88,7 +94,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 +108,7 @@ class CanvasPainter int mCurrentLayerIndex = 0; int mFrameNumber = 0; - BitmapImage* mBuffer = nullptr; + TiledBuffer* mTiledBuffer = nullptr; QImage mScaledBitmap; @@ -119,6 +125,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/graphics/bitmap/bitmapimage.cpp b/core_lib/src/graphics/bitmap/bitmapimage.cpp index c4be6a77c..e2be3641e 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 6122b9255..ead411bfb 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 000000000..7727e0517 --- /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 000000000..b94bf3dc4 --- /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 000000000..5d4929e64 --- /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 000000000..7439c628c --- /dev/null +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -0,0 +1,86 @@ +/* + +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; +}; + +inline uint qHash(const TileIndex &key, uint seed) +{ + 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/vectorimage.cpp b/core_lib/src/graphics/vector/vectorimage.cpp index 18108742b..9dfe0980c 100644 --- a/core_lib/src/graphics/vector/vectorimage.cpp +++ b/core_lib/src/graphics/vector/vectorimage.cpp @@ -1203,6 +1203,7 @@ void VectorImage::paintImage(QPainter& painter, bool showThinCurves, bool antialiasing) { + painter.save(); painter.setRenderHint(QPainter::Antialiasing, antialiasing); painter.setClipping(false); @@ -1248,6 +1249,7 @@ void VectorImage::paintImage(QPainter& painter, curve.drawPath(painter, mObject, mSelectionTransformation, simplified, showThinCurves); painter.setClipping(false); } + painter.restore(); } /** diff --git a/core_lib/src/interface/editor.cpp b/core_lib/src/interface/editor.cpp index b6d3d44bf..7c4d5bc60 100644 --- a/core_lib/src/interface/editor.cpp +++ b/core_lib/src/interface/editor.cpp @@ -1109,14 +1109,9 @@ void Editor::deselectAll() const } } -void Editor::updateFrame(int frameNumber) +void Editor::updateFrame() { - mScribbleArea->updateFrame(frameNumber); -} - -void Editor::updateCurrentFrame() -{ - 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 0b5723d3d..eef05b266 100644 --- a/core_lib/src/interface/editor.h +++ b/core_lib/src/interface/editor.h @@ -166,14 +166,8 @@ class Editor : public QObject /** 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); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 998c39897..028068eda 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" @@ -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,7 +88,6 @@ 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)); @@ -189,14 +192,36 @@ 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) + Layer::LAYER_TYPE layerType = mEditor->layers()->currentLayer()->type(); + if (layerType == Layer::BITMAP) { + const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); + const QImage& image = *bitmapImage->image(); + tile->load(image, bitmapImage->topLeft()); + } else if (layerType == Layer::VECTOR) { + + // Not used, we only use the buffer to paint the stroke before painting the real vector stroke + } + + 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(); } @@ -254,12 +279,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) @@ -277,7 +304,7 @@ void ScribbleArea::invalidatePainterCaches() { mCameraPainter.resetCache(); mCanvasPainter.resetLayerCache(); - update(); + updateFrame(); } void ScribbleArea::onToolPropertyUpdated(ToolType, ToolPropertyType type) @@ -298,7 +325,7 @@ void ScribbleArea::onToolChanged(ToolType) prepOverlays(frame); prepCameraPainter(frame); invalidateCacheForFrame(frame); - updateCurrentFrame(); + updateFrame(); } @@ -313,13 +340,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() @@ -328,7 +356,7 @@ void ScribbleArea::onFramesModified() if (mPrefs->isOn(SETTING::PREV_ONION) || mPrefs->isOn(SETTING::NEXT_ONION)) { invalidatePainterCaches(); } - update(); + updateFrame(); } void ScribbleArea::onFrameModified(int frameNumber) @@ -338,7 +366,7 @@ void ScribbleArea::onFrameModified(int frameNumber) invalidatePainterCaches(); } invalidateCacheForFrame(frameNumber); - updateFrame(frameNumber); + updateFrame(); } void ScribbleArea::onViewChanged() @@ -355,7 +383,7 @@ void ScribbleArea::onSelectionChanged() { int currentFrame = mEditor->currentFrame(); invalidateCacheForFrame(currentFrame); - updateFrame(currentFrame); + updateFrame(); } void ScribbleArea::onOnionSkinTypeChanged() @@ -386,8 +414,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)) @@ -501,8 +527,6 @@ void ScribbleArea::keyReleaseEvent(QKeyEvent *event) return; } - mKeyboardInUse = false; - if (event->key() == 0) { editor()->tools()->tryClearTemporaryTool(Qt::Key_unknown); @@ -525,7 +549,7 @@ 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"; @@ -664,7 +688,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; @@ -704,13 +727,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()); @@ -730,11 +746,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; @@ -803,7 +819,7 @@ 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()); @@ -831,60 +847,27 @@ 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) { - QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver; - switch (currentTool()->type()) - { - case ERASER: - cm = QPainter::CompositionMode_DestinationOut; - break; - case BRUSH: - case PEN: - case PENCIL: - if (getTool(currentTool()->type())->properties.preserveAlpha) - { - cm = QPainter::CompositionMode_SourceOver; - } - break; - default: //nothing - break; - } - targetImage->paste(&mBufferImg, cm); + targetImage->paste(&mTiledBuffer, QPainter::CompositionMode_Source); } - 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) @@ -906,9 +889,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); } @@ -929,16 +909,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() @@ -1050,14 +1031,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); @@ -1134,7 +1119,6 @@ void ScribbleArea::paintEvent(QPaintEvent* event) mOverlayPainter.paint(painter); - // paints the selection outline if (mEditor->select()->somethingSelected()) { @@ -1224,6 +1208,7 @@ void ScribbleArea::prepCanvas(int frame, QRect rect) o.fLayerVisibilityThreshold = mPrefs->getFloat(SETTING::LAYER_VISIBILITY_THRESHOLD); o.scaling = mEditor->view()->scaling(); o.cmBufferBlendMode = mEditor->tools()->currentTool()->type() == ToolType::ERASER ? QPainter::CompositionMode_DestinationOut : QPainter::CompositionMode_SourceOver; + o.bIgnoreCanvasBuffer = currentTool()->type() == POLYLINE; OnionSkinPainterOptions onionSkinOptions; onionSkinOptions.enabledWhilePlaying = mPrefs->getInt(SETTING::ONION_WHILE_PLAYBACK); @@ -1246,12 +1231,11 @@ 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, rect, &mTiledBuffer); } void ScribbleArea::drawCanvas(int frame, QRect rect) { - mCanvas.setDevicePixelRatio(mDevicePixelRatio); prepCanvas(frame, rect); prepCameraPainter(frame); prepOverlays(frame); @@ -1279,39 +1263,63 @@ 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) +{ + mTiledBuffer.drawBrush(thePoint, brushWidth, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, 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); +} - 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 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); + + 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) @@ -1367,12 +1375,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_) @@ -1424,28 +1430,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)); } /************************************************************************************/ @@ -1493,7 +1478,7 @@ void ScribbleArea::applyTransformedSelection() mEditor->setModified(mEditor->layers()->currentLayerIndex(), mEditor->currentFrame()); } - update(); + updateFrame(); } void ScribbleArea::cancelTransformedSelection() @@ -1521,37 +1506,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(); } } @@ -1561,12 +1516,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; @@ -1599,21 +1548,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(); @@ -1694,21 +1628,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 6b443ee2f..41c62ca6a 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: @@ -239,27 +223,17 @@ 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; + bool mMultiLayerOnionSkin = false; // Future use. If required, just add a checkbox to update it. int mDeltaFactor = 1; -private: - /* 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. @@ -268,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/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index add88969d..f319c59de 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(); @@ -334,6 +337,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 +348,9 @@ void ToolManager::tabletSwitchToEraser() void ToolManager::tabletRestorePrevTool() { - mTabletEraserTool = nullptr; - if (mTemporaryTool == nullptr) + if (mTemporaryTool == nullptr && mTabletEraserTool != nullptr) { + mTabletEraserTool = nullptr; emit toolChanged(currentTool()->type()); } } diff --git a/core_lib/src/tool/basetool.cpp b/core_lib/src/tool/basetool.cpp index c6029edce..759d49c8e 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; } diff --git a/core_lib/src/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 8461e285d..88b6b6e22 100644 --- a/core_lib/src/tool/brushtool.cpp +++ b/core_lib/src/tool/brushtool.cpp @@ -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 eb1e2413b..0e82d6510 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 2ef4f01aa..69d4d1776 100644 --- a/core_lib/src/tool/buckettool.cpp +++ b/core_lib/src/tool/buckettool.cpp @@ -284,7 +284,7 @@ void BucketTool::paintBitmap() 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 +324,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/cameratool.cpp b/core_lib/src/tool/cameratool.cpp index f4fd3c030..feb0e5462 100644 --- a/core_lib/src/tool/cameratool.cpp +++ b/core_lib/src/tool/cameratool.cpp @@ -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) diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index ba7ecf637..0ae100520 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -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_DestinationOut, opacity, properties.useFeather, properties.useAA == ON); @@ -238,6 +240,7 @@ void EraserTool::drawStroke() brushWidth, properties.feather, Qt::white, + QPainter::CompositionMode_DestinationOut, 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/movetool.cpp b/core_lib/src/tool/movetool.cpp index 8ddc3a3da..ec02bafc0 100644 --- a/core_lib/src/tool/movetool.cpp +++ b/core_lib/src/tool/movetool.cpp @@ -119,7 +119,7 @@ void MoveTool::pointerPressEvent(PointerEvent* event) mEditor->overlays()->updatePerspective(mapped); } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void MoveTool::pointerMoveEvent(PointerEvent* event) @@ -154,7 +154,7 @@ void MoveTool::pointerMoveEvent(PointerEvent* event) storeClosestVectorCurve(mCurrentLayer); } } - mEditor->updateCurrentFrame(); + mEditor->updateFrame(); } void MoveTool::pointerReleaseEvent(PointerEvent*) diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index e1249b67f..5e5e04636 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" @@ -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 adb5019da..0b15fb719 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; diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 62f999659..482d5c277 100644 --- a/core_lib/src/tool/pentool.cpp +++ b/core_lib/src/tool/pentool.cpp @@ -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 ff58abc11..a904944ad 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 866753e00..c404e343c 100644 --- a/core_lib/src/tool/polylinetool.cpp +++ b/core_lib/src/tool/polylinetool.cpp @@ -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 df24930ae..52d57fa68 100644 --- a/core_lib/src/tool/selecttool.cpp +++ b/core_lib/src/tool/selecttool.cpp @@ -116,7 +116,7 @@ void SelectTool::beginSelection() mAnchorOriginPoint = getLastPoint(); } - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void SelectTool::pointerPressEvent(PointerEvent* event) @@ -160,7 +160,7 @@ void SelectTool::pointerMoveEvent(PointerEvent*) } } - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } void SelectTool::pointerReleaseEvent(PointerEvent* event) @@ -189,7 +189,7 @@ void SelectTool::pointerReleaseEvent(PointerEvent* event) mSelectionRect = mEditor->select()->mapToSelection(mEditor->select()->mySelectionRect()).boundingRect(); mScribbleArea->updateToolCursor(); - mScribbleArea->updateCurrentFrame(); + mScribbleArea->updateFrame(); } bool SelectTool::maybeDeselect() diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index d11c56fbf..f55538000 100644 --- a/core_lib/src/tool/smudgetool.cpp +++ b/core_lib/src/tool/smudgetool.cpp @@ -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/stroketool.cpp b/core_lib/src/tool/stroketool.cpp index cf1b4b6e7..6b8e8c112 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 e4b5c7d92..7f97c793b 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 3b0645cec..f378fd93f 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()); } } } From f09d0ca87aaa9d1a3b5c1f45f425416e8b0cf025 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sun, 1 Oct 2023 09:14:51 +0200 Subject: [PATCH 12/26] Add custom Windows tile (#1787) * Add custom Windows tile * Fix debug_and_release configuration for core_lib and tests --- .../actions/create-package/create-package.sh | 12 ++++-- .github/workflows/ci.yml | 4 +- app/app.pro | 37 ++++++++++++------ app/data/pencil2d.VisualElementsManifest.xml | 8 ++++ app/data/resources.xml | 10 +++++ app/data/resources/tile150.scale-100.png | Bin 0 -> 13136 bytes app/data/resources/tile150.scale-140.png | Bin 0 -> 19058 bytes app/data/resources/tile150.scale-180.png | Bin 0 -> 24575 bytes app/data/resources/tile150.scale-80.png | Bin 0 -> 10126 bytes app/data/resources/tile70.scale-100.png | Bin 0 -> 4226 bytes app/data/resources/tile70.scale-140.png | Bin 0 -> 6538 bytes app/data/resources/tile70.scale-180.png | Bin 0 -> 9155 bytes app/data/resources/tile70.scale-80.png | Bin 0 -> 3067 bytes core_lib/core_lib.pro | 7 +--- tests/tests.pro | 15 +++---- util/appveyor-mingw.yml | 8 ++-- util/appveyor-msvc.yml | 6 +-- util/common.pri | 1 - 18 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 app/data/pencil2d.VisualElementsManifest.xml create mode 100644 app/data/resources.xml create mode 100644 app/data/resources/tile150.scale-100.png create mode 100644 app/data/resources/tile150.scale-140.png create mode 100644 app/data/resources/tile150.scale-180.png create mode 100644 app/data/resources/tile150.scale-80.png create mode 100644 app/data/resources/tile70.scale-100.png create mode 100644 app/data/resources/tile70.scale-140.png create mode 100644 app/data/resources/tile70.scale-180.png create mode 100644 app/data/resources/tile70.scale-80.png diff --git a/.github/actions/create-package/create-package.sh b/.github/actions/create-package/create-package.sh index 8a516415e..71a5ddf9f 100755 --- a/.github/actions/create-package/create-package.sh +++ b/.github/actions/create-package/create-package.sh @@ -52,7 +52,8 @@ wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-inte create_package_macos() { echo "::group::Clean" make clean - mv bin Pencil2D + mkdir Pencil2D + mv app/Pencil2D.app Pencil2D/ pushd Pencil2D >/dev/null echo "::endgroup::" @@ -88,16 +89,19 @@ create_package_macos() { } 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" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70196cd4f..bd7be4bd7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,7 +92,7 @@ jobs: - name: Configure build run: mkdir build; ${{runner.os == 'Linux' && matrix.qt == 6 && 'qmake6' || 'qmake'}} - -o build PREFIX=/usr CONFIG+=release CONFIG+=GIT + -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' || ''}} @@ -102,7 +102,7 @@ jobs: - name: Run tests env: { QT_QPA_PLATFORM: minimal } - run: build/tests/bin/tests + run: build/tests/tests - name: Create package id: package diff --git a/app/app.pro b/app/app.pro index 714312b77..5ebdfa065 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! ) } -QT += core widgets gui xml multimedia svg network - TEMPLATE = app -TARGET = pencil2d -QMAKE_APPLICATION_BUNDLE_NAME = Pencil2D - CONFIG += precompile_header lrelease embed_translations +QT += core widgets gui xml multimedia svg network -DESTDIR = ../bin -MOC_DIR = .moc -OBJECTS_DIR = .obj -UI_DIR = .ui +TARGET = pencil2d 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/pencil2d.VisualElementsManifest.xml b/app/data/pencil2d.VisualElementsManifest.xml new file mode 100644 index 000000000..3a00d0349 --- /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 000000000..f45b34273 --- /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 0000000000000000000000000000000000000000..14c3ed0fb8c84ee1082f545a58bf4e8b4d17e328 GIT binary patch literal 13136 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%0AwmEG5|A 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 literal 0 HcmV?d00001 diff --git a/app/data/resources/tile150.scale-180.png b/app/data/resources/tile150.scale-180.png new file mode 100644 index 0000000000000000000000000000000000000000..724ae3f7076ad31b266c9e548b4c7f7268df7096 GIT binary patch literal 24575 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 NRKS`Fl_1NA{{el6+SUL7 literal 0 HcmV?d00001 diff --git a/app/data/resources/tile150.scale-80.png b/app/data/resources/tile150.scale-80.png new file mode 100644 index 0000000000000000000000000000000000000000..e94cf43fbb42bbe590ea1db630540a5e5cf2fae4 GIT binary patch literal 10126 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 literal 0 HcmV?d00001 diff --git a/app/data/resources/tile70.scale-100.png b/app/data/resources/tile70.scale-100.png new file mode 100644 index 0000000000000000000000000000000000000000..c2816b92cc6a19ac3aa2ecca6d846ab2d0e5a12f GIT binary patch literal 4226 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 literal 0 HcmV?d00001 diff --git a/app/data/resources/tile70.scale-180.png b/app/data/resources/tile70.scale-180.png new file mode 100644 index 0000000000000000000000000000000000000000..e50c2d32064569199e743f441fb9fe2e6d5828de GIT binary patch literal 9155 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 literal 0 HcmV?d00001 diff --git a/app/data/resources/tile70.scale-80.png b/app/data/resources/tile70.scale-80.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5a9c2f0b31ef8b4226403957dcadefbacc41c8 GIT binary patch literal 3067 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 Date: Sun, 1 Oct 2023 09:18:50 +0200 Subject: [PATCH 13/26] Buckettool fill bugs (#1772) * Remove Fill to layer feature * Fix floodfill didn't take camera transform into account #1735 * UX: No matter what, allow a fill the very first time It's been a growing annoyance of mine that sometimes a fill won't happen, sometimes caused by a bug, other times because of the rules with are to be applied when filling while dragging the mouse. * Fix case where undo commands would fill up with no visible changes * Add explanation * Remove dead code * Fix more cases where continuous fill would flood the undo stack * Fix typos and such --------- Co-authored-by: Jakob Gahde --- app/src/bucketoptionswidget.cpp | 28 ------ app/src/bucketoptionswidget.h | 2 - app/src/tooloptionwidget.cpp | 1 - app/ui/bucketoptionswidget.ui | 22 +---- core_lib/src/graphics/bitmap/bitmapbucket.cpp | 97 +++++++++---------- core_lib/src/graphics/bitmap/bitmapbucket.h | 11 ++- core_lib/src/managers/toolmanager.cpp | 11 --- core_lib/src/managers/toolmanager.h | 1 - core_lib/src/tool/basetool.cpp | 5 - core_lib/src/tool/basetool.h | 2 - core_lib/src/tool/buckettool.cpp | 21 +--- core_lib/src/tool/buckettool.h | 1 - core_lib/src/util/pencildef.h | 2 - tests/src/test_bitmapbucket.cpp | 72 -------------- 14 files changed, 55 insertions(+), 221 deletions(-) diff --git a/app/src/bucketoptionswidget.cpp b/app/src/bucketoptionswidget.cpp index 9f3feedef..2e9e89e3e 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 a53644f33..c8985e253 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/tooloptionwidget.cpp b/app/src/tooloptionwidget.cpp index f6261336b..5c51c4206 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/ui/bucketoptionswidget.ui b/app/ui/bucketoptionswidget.ui index b5b20c4d7..49ffe7ee2 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/core_lib/src/graphics/bitmap/bitmapbucket.cpp b/core_lib/src/graphics/bitmap/bitmapbucket.cpp index 88ba46b22..60e506dda 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 88464994e..edd71d615 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/managers/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index f319c59de..14233ec79 100644 --- a/core_lib/src/managers/toolmanager.cpp +++ b/core_lib/src/managers/toolmanager.cpp @@ -254,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); } diff --git a/core_lib/src/managers/toolmanager.h b/core_lib/src/managers/toolmanager.h index 02373d2d4..6ba1cd332 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/tool/basetool.cpp b/core_lib/src/tool/basetool.cpp index 759d49c8e..d02496cb4 100644 --- a/core_lib/src/tool/basetool.cpp +++ b/core_lib/src/tool/basetool.cpp @@ -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 02f33ddec..533b59491 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/buckettool.cpp b/core_lib/src/tool/buckettool.cpp index 69d4d1776..911e881d6 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); } @@ -163,16 +161,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 +183,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,13 +259,6 @@ 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(); - } } }); } diff --git a/core_lib/src/tool/buckettool.h b/core_lib/src/tool/buckettool.h index 631c19938..4f9660a61 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/util/pencildef.h b/core_lib/src/util/pencildef.h index 040a2793d..e1b9b11ac 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, }; @@ -306,7 +305,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/tests/src/test_bitmapbucket.cpp b/tests/src/test_bitmapbucket.cpp index 3fa0624e3..cf27e52c1 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()); - } - } } From 86e55399959a4005a93937d780cd89d3b47fc948 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sun, 1 Oct 2023 12:56:31 +0200 Subject: [PATCH 14/26] Use checkboxes instead of tool buttons in onion skin widget (#1753) * Use checkboxes instead of tool buttons in onion skin widget * Improve styling of group box on Mac OS --------- Co-authored-by: MrStevns --- app/src/onionskinwidget.cpp | 32 +++++++++++------- app/src/onionskinwidget.h | 4 +-- app/ui/onionskin.ui | 67 ++++--------------------------------- 3 files changed, 28 insertions(+), 75 deletions(-) diff --git a/app/src/onionskinwidget.cpp b/app/src/onionskinwidget.cpp index f4872aa3d..8b7b8cac1 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 0bfcf64ef..711f24970 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/ui/onionskin.ui b/app/ui/onionskin.ui index bc9b8967c..9623c2cd5 100644 --- a/app/ui/onionskin.ui +++ b/app/ui/onionskin.ui @@ -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 - - - @@ -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 - - - From d3be33998650cc103d9491516f9c791de8b6bc4e Mon Sep 17 00:00:00 2001 From: Oliver Stevns <1045397+MrStevns@users.noreply.github.com> Date: Sun, 1 Oct 2023 14:37:16 +0200 Subject: [PATCH 15/26] Improve CameraPainter and BackgroundWidget performance (#1789) * Canvaspainter improvement WIP * Implement tiled buffer for faster painting WIP * CanvasPainter painting improvements - Split painting for current frame and onion skinning - Introduce blitRect for updating only the dirty portion of the frame - Optimization: Only do an expensive fill when the size changes, otherwise rely on the blitRect to clear the dirty portion. * Only update the dirty potion Aside from cleaning up various extra update methods that are no longer necessary, I've made a minor change in vectorImage, in order to get it's correct dirty region. * Fix vector selection being slightly wider than the overall bounds This was a necessary change in order to calculate the correct bounds that wouldn't draw artifacts. * Fix stroke placement for vector polyline This also fixes some inconsistency in the CanvasPainter that I couldn't make sense of.. * Implement faster hashing * Do some cleanup * Fix next onion skin being drawn on current frame when it's the last * Re-add loadFile if the bitmap image hasn't been loaded yet Although i don't like that this exists in our painter, I believe we added it because for some reason the image hasn't been loaded yet, so let's leave it for now... ideally though i've really like to get rid of such weird mutable things that has little to do with painting. * Update CanvasPainter interface to better match its usage * Cleanup TiledBuffer * Make polyline work * Fix blur logic of smudge tool Additionally this will fix some artifacts when smudging * Replace use of BitmapImage buffer with TiledBuffer - Note that VectorImage hasn't been implemented yet. * Make TiledBuffer work for VectorImage * Cleanup dead code in TiledBuffer * Fix not painting bitmap layers when not on current frame * Add missing license * Remove dead code * Implement blitting in CameraPainter * Fix drawImage would not get proper update bounds * Cleanup TiledBuffer and fix warnings * Fix Eraser should use destinationOut composition * Fix anti aliasing was causing unwanted floating precision error This was causing the gaps to be drawn between the tiled buffer tiles in some cases... particularly when erasing and you had zoomed in or out. * Fix Polyline can't be removed after applying on vector * Fix transformed selection was being painted in wrong order * Cleanup after testing * Fix tabletRestorePrevTool would always ask for a full canvas update * Remove unnecessary clear of cache when setting tool * Fix update artifacts when using dotted cursor - In addition this fix makes it possible to ignore an additional update event which would previously be done by updateCanvasCursor, but because the TiledBuffer will handle this now, we only update the cursor when a tool is not active. * Refactor TiledBuffer - Also add missing onClearTile callback, which is unused but implementation wise is correct now. * Some cleanup * Remove redundant clear of tile before deleting it * Update signal/slot naming convention * Apply some refactoring to tile * Fix blitRect::extend would make tiles odd numbered * Remove unused methods * Avoid invalidating cache till we're done drawing * Remove various dead code from ScribbleArea * Fix ScribbleArea repaint when tiled buffer does not cover update rect * Fix canvas pixmap being too big when using devicePixelRatio > 1 * Apply clip rect logic to BackgroundWidget * Try to fix seams appearing in Qt 6 * Fix canvas not being updated when using camera tool * Cleanup CanvasPainter * Adjust blitRect explanation * Remove the need to allocate an empty QPointF to draw pixmaps * TiledBuffer: Remove unused signals * Use clipRect instead of paint(target, pix, source) in order to support HDPI properly --- core_lib/src/camerapainter.cpp | 98 +++++++++++++-------- core_lib/src/camerapainter.h | 24 ++--- core_lib/src/interface/backgroundwidget.cpp | 7 +- core_lib/src/interface/backgroundwidget.h | 2 +- core_lib/src/interface/scribblearea.cpp | 8 +- 5 files changed, 83 insertions(+), 56 deletions(-) diff --git a/core_lib/src/camerapainter.cpp b/core_lib/src/camerapainter.cpp index e33c0ebbc..ca4a40993 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 1bfa1a9c2..45dcac50f 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/interface/backgroundwidget.cpp b/core_lib/src/interface/backgroundwidget.cpp index ebee13a2b..b51aad412 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.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 881a41a39..9c58fc11f 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/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 028068eda..7e6d58ede 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -45,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"); @@ -825,6 +825,7 @@ void ScribbleArea::resizeEvent(QResizeEvent* event) invalidateCacheForFrame(mEditor->currentFrame()); invalidatePainterCaches(); mCanvasPainter.reset(); + mCameraPainter.reset(); } void ScribbleArea::showLayerNotVisibleWarning() @@ -1010,7 +1011,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) prepOverlays(currentFrame); mCanvasPainter.paintCached(event->rect()); - mCameraPainter.paintCached(); + mCameraPainter.paintCached(event->rect()); } if (currentTool()->type() == MOVE) @@ -1193,7 +1194,6 @@ 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) @@ -1240,7 +1240,7 @@ void ScribbleArea::drawCanvas(int frame, QRect rect) prepCameraPainter(frame); prepOverlays(frame); mCanvasPainter.paint(rect); - mCameraPainter.paint(); + mCameraPainter.paint(rect); } void ScribbleArea::setGaussianGradient(QGradient &gradient, QColor color, qreal opacity, qreal offset) From 58eb218117f023d118336de0e073de738f9f5013 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sun, 1 Oct 2023 15:54:41 +0200 Subject: [PATCH 16/26] Fix perspective overlays not always covering the entire view (#1790) * Fix perspective overlays not always covering the entire view * Fix perspective overlays with active canvas rotation --- core_lib/src/canvaspainter.cpp | 3 +- core_lib/src/canvaspainter.h | 2 +- core_lib/src/interface/scribblearea.cpp | 10 +- core_lib/src/interface/scribblearea.h | 2 +- core_lib/src/overlaypainter.cpp | 134 ++++++++---------------- core_lib/src/overlaypainter.h | 10 +- core_lib/src/util/util.cpp | 58 +++++----- core_lib/src/util/util.h | 12 ++- tests/tests.pro | 2 +- 9 files changed, 101 insertions(+), 132 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index ef1324065..f03ae616f 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -159,9 +159,8 @@ void CanvasPainter::renderPostLayers(QPainter& painter, const QRect& blitRect) } } -void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, TiledBuffer* tiledBuffer) +void CanvasPainter::setPaintSettings(const Object* object, int currentLayer, int frame, TiledBuffer* tiledBuffer) { - Q_UNUSED(rect) Q_ASSERT(object); mObject = object; diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index 66c6e1d98..99b90c6be 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -67,7 +67,7 @@ class CanvasPainter void setTransformedSelection(QRect selection, QTransform transform); void ignoreTransformedSelection(); - void setPaintSettings(const Object* object, int currentLayer, int frame, QRect rect, TiledBuffer* tilledBuffer); + void setPaintSettings(const Object* object, int currentLayer, int frame, TiledBuffer* tilledBuffer); void paint(const QRect& blitRect); void paintCached(const QRect& blitRect); void resetLayerCache(); diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 7e6d58ede..4e8d5e319 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -1006,7 +1006,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) } else { - prepCanvas(currentFrame, event->rect()); + prepCanvas(currentFrame); prepCameraPainter(currentFrame); prepOverlays(currentFrame); @@ -1118,7 +1118,7 @@ void ScribbleArea::paintEvent(QPaintEvent* event) paintCanvasCursor(painter); - mOverlayPainter.paint(painter); + mOverlayPainter.paint(painter, rect()); // paints the selection outline if (mEditor->select()->somethingSelected()) @@ -1196,7 +1196,7 @@ void ScribbleArea::prepCameraPainter(int frame) mCameraPainter.setOnionSkinPainterOptions(onionSkinOptions); } -void ScribbleArea::prepCanvas(int frame, QRect rect) +void ScribbleArea::prepCanvas(int frame) { Object* object = mEditor->object(); @@ -1231,12 +1231,12 @@ 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, &mTiledBuffer); + mCanvasPainter.setPaintSettings(object, mEditor->layers()->currentLayerIndex(), frame, &mTiledBuffer); } void ScribbleArea::drawCanvas(int frame, QRect rect) { - prepCanvas(frame, rect); + prepCanvas(frame); prepCameraPainter(frame); prepOverlays(frame); mCanvasPainter.paint(rect); diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h index 41c62ca6a..c31ffca13 100644 --- a/core_lib/src/interface/scribblearea.h +++ b/core_lib/src/interface/scribblearea.h @@ -215,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); diff --git a/core_lib/src/overlaypainter.cpp b/core_lib/src/overlaypainter.cpp index a969cb96b..b1cd48010 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 b66762513..ce96f8f48 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/util/util.cpp b/core_lib/src/util/util.cpp index bf6721142..39468c04e 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 clipInfiniteLineToEdge(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) || + !clipInfiniteLineToEdge(t0, t1, -dx, x1 - left) || + !clipInfiniteLineToEdge(t0, t1, dx, right - x1) || + !clipInfiniteLineToEdge(t0, t1, -dy, y1 - top) || + !clipInfiniteLineToEdge(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 4f19feecd..6a172502d 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/tests/tests.pro b/tests/tests.pro index e229371c8..b18992808 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -9,7 +9,7 @@ TEMPLATE = app CONFIG += console CONFIG -= app_bundle -QT += core widgets gui xml multimedia svg testlib +QT += core widgets gui xml multimedia svg TARGET = tests From 6a4fe12ded32efb8e7b714dc8b88fbe8f82b2397 Mon Sep 17 00:00:00 2001 From: "Nararyans R.I" <51940383+Fatih20@users.noreply.github.com> Date: Mon, 16 Oct 2023 13:10:42 +0700 Subject: [PATCH 17/26] UI icons re-design (#1361) * Initial Commit * Selection, Pen, Trash, and Move * Action-Icons-Redesign * Fix app.qrc * Resizes controls to proper sizes * Recolor save-as * Redesign show lines, layer and keys addition for timeline update * Recolor hand tool, rework smudge tool and timelines * Updated app.qrc * Magnify rework * Add icons of upcoming shape, text, and camera * Change the icons for cursors of each individual tool, redesign the arrow-x icons, reorganize the tools icons * Hand cursor for closed and open, show invisible and outline icons * Hand tool now have a hand cursor that open and close accordingly * Uniforming perspective in the tools, tweaks to pencil, hand, onion skin, and soundscrub, increasing size of controls icons. * Tweaks to layers icon * Vector layer back to before * Tweak toolbar icon sizes * Optimize svg icons - We now have classic, legacy and playful - Playful icons has been optimized and tweaked. - Move icons into theme folders I should have done the move separately but well... I forgot! and now it's done P: * Fix paths after move * Fix svg rendering * Replace onion skin icon * Fix more broken icon paths * Replace smudge icon - Fix move icons to app - Cross image has been moved to icons -> general * Make and use alternative onion skin color icons * Update toolbox icons * Apply icon suggestions from review * Update icons in main window * Update onion skin icons * Add new grid icon * Fix crash after merge * Use new general cursor icons * Update zoom icons and fix grid background * Align add and remove colors and depth * Remove disabled icon for sound for consistency because why would this contain a disabled state when no other icon does... * Update control playback icons * Update more control icons - Loop, sound, sound scrub - Removed hideous arrow behind list icon * Tweak color of view icons * Add new layer cell icons * Update toolbar icons * Implement new clear canvas icon * Optimize and tweak preferences icons * Add new tool cursor icons * Adjust pixel alignment on overlay icons * Resize overlay icons to 22x22 * Pixel align top toolbar icons * Fix pixel alignment for clear-canvas icon * Fix pixel alignment for timeline layer type icons * Timeline layers: increase size of icons * Fix pixel alignment for timeline layer add/remove buttons * Fix pixel alignment for time controls and frames - Icons have been resized also, making sure they're approx. the same size inside the container boundary. - Introduced a new more options icon - Tweaked icon sizes in UI * Fix minor pixel alignment in view-reset icon * Remove backdrops from menubar icons * Fix pixel alignment for color palette icons * Onion skin icons: pixel and styling alignment Onion skin icons are now 22x22 pixels like the rest * Fix path to UI references in main window * Toolbox icons: 22px and pixel aligned * Statusbar: fix wrong path * Camera transform icons: pixel align * Fix missing icon in status bar * Add missing layer duplicate icon * Try fix icons looking fuzzy on windows * Try fix some icons looking misaligned on windows * Try fix icons looking fuzzy on windows * View reset: remove shadow * Fix border thickness for copy/cut/paste icons * Make sure preference dialog expects icons of 64x64 * Fix cursors in CameraTool * Fix onion skin icons had been reversed * Simplify palette on menubar and overlay icons * Update preference icons * Update cut icon * Update layer and frame icons * Update playback buttons * Update sound, scrub, more-options and layer cell icons - Tweaked styling, no more soft shading. Adjusted all colors to match defined palette. * Update magnifier icons and tweak Menu, Overlay and perspective icons * Clean up some leftovers * Fix zoom select icon using old style * Show drop down arrow on perspective button * Update smudge and hand icons * Update smudge cursor icons * Adjust overlay on zoom in/out icons Adjusted from 7x7px to 10x10 since that is what other overlays are * Brush tool icon: Fix pixel alignment at tip * Pen tool icon: Fix pixel alignment at tip * Remove old and unused icons * Update error dialog icon path --------- Co-authored-by: MrStevns Co-authored-by: Jakob Gahde --- app/data/app.qrc | 169 +++++++++--------- app/data/icons/add.png | Bin 771 -> 0 bytes app/data/icons/controls/duplicate.png | Bin 1441 -> 0 bytes app/data/icons/controls/endplay.png | Bin 1219 -> 0 bytes app/data/icons/controls/loop.png | Bin 150 -> 0 bytes app/data/icons/controls/play.png | Bin 304 -> 0 bytes app/data/icons/controls/sound-disabled.png | Bin 350 -> 0 bytes app/data/icons/controls/sound.png | Bin 263 -> 0 bytes .../icons/controls/soundscrub-disabled.png | Bin 7202 -> 0 bytes app/data/icons/controls/soundscrub.png | Bin 7652 -> 0 bytes app/data/icons/controls/startplay.png | Bin 1223 -> 0 bytes app/data/icons/controls/stop.png | Bin 528 -> 0 bytes app/data/icons/copy.png | Bin 909 -> 0 bytes app/data/icons/cut.png | Bin 1016 -> 0 bytes app/data/icons/dialog-error.svg | 96 ---------- app/data/icons/exit.png | Bin 1320 -> 0 bytes .../{new => general}/checkerboard_smaller.png | Bin .../data/icons/general}/cross.png | Bin app/data/icons/general/cursor-brush.svg | 1 + app/data/icons/general/cursor-bucket.svg | 1 + .../svg => general}/cursor-diagonal-left.svg | 0 .../svg => general}/cursor-diagonal-right.svg | 0 app/data/icons/general/cursor-eyedropper.svg | 1 + .../svg => general}/cursor-horizontal.svg | 0 .../{new/svg => general}/cursor-move.svg | 0 app/data/icons/general/cursor-pen.svg | 1 + app/data/icons/general/cursor-pencil.svg | 1 + .../{new/svg => general}/cursor-rotate.svg | 0 .../icons/general/cursor-smudge-liquify.svg | 1 + app/data/icons/general/cursor-smudge.svg | 1 + .../{new/svg => general}/cursor-vertical.svg | 0 app/data/icons/grid.png | Bin 140 -> 0 bytes app/data/icons/layer-bitmap.png | Bin 904 -> 0 bytes app/data/icons/layer-camera.png | Bin 1137 -> 0 bytes app/data/icons/layer-sound.png | Bin 882 -> 0 bytes app/data/icons/layer-vector.png | Bin 855 -> 0 bytes app/data/icons/magnify.png | Bin 760 -> 0 bytes app/data/icons/mirror.png | Bin 303 -> 0 bytes app/data/icons/mirrorV.png | Bin 1277 -> 0 bytes app/data/icons/new.png | Bin 666 -> 0 bytes app/data/icons/new/arrow.png | Bin 892 -> 0 bytes app/data/icons/new/brush_detailed.png | Bin 1315 -> 0 bytes app/data/icons/new/brush_flat_outlined.png | Bin 1290 -> 0 bytes app/data/icons/new/bucket_detailed.png | Bin 1430 -> 0 bytes app/data/icons/new/bucket_flat_outlined.png | Bin 1298 -> 0 bytes app/data/icons/new/eraser_detailed.png | Bin 1163 -> 0 bytes app/data/icons/new/eraser_flat_outlined.png | Bin 1206 -> 0 bytes app/data/icons/new/eyedropper_detailed.png | Bin 1559 -> 0 bytes .../icons/new/eyedropper_flat_outlined.png | Bin 1331 -> 0 bytes app/data/icons/new/hand_detailed.png | Bin 1270 -> 0 bytes app/data/icons/new/hand_flat_outlined.png | Bin 1241 -> 0 bytes app/data/icons/new/line.png | Bin 1229 -> 0 bytes app/data/icons/new/pen_detailed.png | Bin 1333 -> 0 bytes app/data/icons/new/pen_flat_outlined.png | Bin 1320 -> 0 bytes app/data/icons/new/pencil_detailed.png | Bin 1308 -> 0 bytes app/data/icons/new/pencil_flat_outlined.png | Bin 1373 -> 0 bytes app/data/icons/new/selection.png | Bin 745 -> 0 bytes app/data/icons/new/smudge_detailed.png | Bin 1250 -> 0 bytes app/data/icons/new/smudge_flat_outlined.png | Bin 1134 -> 0 bytes app/data/icons/new/svg/arrow.svg | 1 - app/data/icons/new/svg/brush_detailed.svg | 138 -------------- .../icons/new/svg/brush_flat_outlined.svg | 1 - app/data/icons/new/svg/bucket_detailed.svg | 1 - .../icons/new/svg/bucket_flat_outlined.svg | 1 - app/data/icons/new/svg/camera-move.svg | 1 - app/data/icons/new/svg/camera-rotate.svg | 1 - app/data/icons/new/svg/camera-scale.svg | 1 - app/data/icons/new/svg/color-dialog.svg | 38 ---- app/data/icons/new/svg/eraser_detailed.svg | 1 - .../icons/new/svg/eraser_flat_outlined.svg | 1 - .../icons/new/svg/eyedropper_detailed.svg | 1 - .../new/svg/eyedropper_flat_outlined.svg | 1 - app/data/icons/new/svg/hand_detailed.svg | 84 --------- app/data/icons/new/svg/hand_flat_outlined.svg | 1 - app/data/icons/new/svg/line.svg | 21 --- app/data/icons/new/svg/more_options.svg | 21 --- app/data/icons/new/svg/pen_detailed.svg | 122 ------------- app/data/icons/new/svg/pen_flat_outlined.svg | 1 - .../icons/new/svg/pen_pattern_detailed.svg | 1 - app/data/icons/new/svg/pencil_detailed.svg | 82 --------- .../icons/new/svg/pencil_flat_outlined.svg | 1 - app/data/icons/new/svg/selection.svg | 1 - app/data/icons/new/svg/smudge_detailed.svg | 47 ----- .../icons/new/svg/smudge_flat_outlined.svg | 34 ---- app/data/icons/new/svg/trash_detailed.svg | 1 - .../icons/new/svg/trash_flat_outlined.svg | 1 - app/data/icons/new/trash_detailed.png | Bin 1318 -> 0 bytes app/data/icons/new/trash_flat_outlined.png | Bin 1214 -> 0 bytes app/data/icons/next.png | Bin 534 -> 0 bytes app/data/icons/onion-blue.png | Bin 155 -> 0 bytes app/data/icons/onion-red.png | Bin 155 -> 0 bytes app/data/icons/onionNext.png | Bin 665 -> 0 bytes app/data/icons/onionPrev.png | Bin 670 -> 0 bytes app/data/icons/open.png | Bin 1356 -> 0 bytes app/data/icons/outlines5.png | Bin 470 -> 0 bytes app/data/icons/overlayAngle.png | Bin 798 -> 0 bytes app/data/icons/overlayCenter.png | Bin 699 -> 0 bytes app/data/icons/overlayGoldenRatio.png | Bin 712 -> 0 bytes app/data/icons/overlayPerspective1.png | Bin 329 -> 0 bytes app/data/icons/overlayPerspective2.png | Bin 366 -> 0 bytes app/data/icons/overlayPerspective3.png | Bin 376 -> 0 bytes app/data/icons/overlaySafe.png | Bin 715 -> 0 bytes app/data/icons/overlayThirds.png | Bin 709 -> 0 bytes app/data/icons/paste.png | Bin 1100 -> 0 bytes app/data/icons/prefs-files.png | Bin 4320 -> 0 bytes app/data/icons/prefs-shortcuts.png | Bin 3101 -> 0 bytes app/data/icons/prefspencil.png | Bin 6417 -> 0 bytes app/data/icons/prefstimeline.png | Bin 3384 -> 0 bytes app/data/icons/prev.png | Bin 534 -> 0 bytes app/data/icons/redo.png | Bin 739 -> 0 bytes app/data/icons/remove.png | Bin 668 -> 0 bytes app/data/icons/save.png | Bin 1166 -> 0 bytes app/data/icons/saveas.png | Bin 1439 -> 0 bytes .../themes/playful/controls/control-loop.svg | 1 + .../playful/controls/control-play-end.svg | 1 + .../playful/controls/control-play-start.svg | 1 + .../themes/playful/controls/control-play.svg | 1 + .../playful/controls/control-sound-enable.svg | 1 + .../playful/controls/control-sound-scrub.svg | 1 + .../themes/playful/controls/control-stop.svg | 1 + .../icons/themes/playful/dialog-error.svg | 1 + .../playful/display/lines-invisible.svg | 1 + .../themes/playful/display/lines-outline.svg | 1 + .../playful/display/mirror-horizontal.svg | 1 + .../playful/display/mirror-vertical.svg | 1 + .../themes/playful/display/overlay-center.svg | 1 + .../playful/display/overlay-golden-ratio.svg | 1 + .../themes/playful/display/overlay-grid.svg | 1 + .../themes/playful/display/overlay-safe.svg | 1 + .../themes/playful/display/overlay-thirds.svg | 1 + .../playful/display/perspective-angle.svg | 1 + .../playful/display/perspective-onepoint.svg | 1 + .../display/perspective-threepoints.svg | 1 + .../playful/display/perspective-twopoints.svg | 1 + .../themes/playful/menubar/clear-canvas.svg | 1 + .../icons/themes/playful/menubar/copy.svg | 1 + app/data/icons/themes/playful/menubar/cut.svg | 1 + app/data/icons/themes/playful/menubar/new.svg | 1 + .../icons/themes/playful/menubar/open.svg | 1 + .../icons/themes/playful/menubar/paste.svg | 1 + .../icons/themes/playful/menubar/redo.svg | 1 + .../icons/themes/playful/menubar/save.svg | 1 + .../icons/themes/playful/menubar/undo.svg | 1 + .../themes/playful/menubar/view-reset.svg | 1 + .../icons/themes/playful/menubar/zoom-in.svg | 1 + .../icons/themes/playful/menubar/zoom-out.svg | 1 + .../themes/playful/menubar/zoom-select.svg | 1 + .../icons/themes/playful/misc/add-color.svg | 1 + .../themes/playful/misc/color-dialog.svg | 1 + .../themes/playful/misc/more-options.svg | 1 + .../themes/playful/misc/remove-color.svg | 1 + .../themes/playful/onion/onionskin-blue.svg | 1 + .../themes/playful/onion/onionskin-enable.svg | 1 + .../themes/playful/onion/onionskin-red.svg | 1 + .../playful/preferences/preferences-files.svg | 1 + .../preferences/preferences-general.svg | 1 + .../preferences/preferences-shortcuts.svg | 1 + .../preferences/preferences-timeline.svg | 1 + .../playful/preferences/preferences-tools.svg | 1 + .../themes/playful/timeline/cell-bitmap.svg | 1 + .../themes/playful/timeline/cell-camera.svg | 1 + .../themes/playful/timeline/cell-sound.svg | 1 + .../themes/playful/timeline/cell-vector.svg | 1 + .../themes/playful/timeline/frame-add.svg | 1 + .../playful/timeline/frame-duplicate.svg | 1 + .../themes/playful/timeline/frame-remove.svg | 1 + .../themes/playful/timeline/layer-add.svg | 1 + .../playful/timeline/layer-duplicate.svg | 1 + .../themes/playful/timeline/layer-remove.svg | 1 + .../icons/themes/playful/tools/tool-brush.svg | 1 + .../themes/playful/tools/tool-bucket.svg | 1 + .../themes/playful/tools/tool-camera-move.svg | 1 + .../playful/tools/tool-camera-rotate.svg | 1 + .../playful/tools/tool-camera-scale.svg | 1 + .../themes/playful/tools/tool-eraser.svg | 1 + .../themes/playful/tools/tool-eyedropper.svg | 1 + .../icons/themes/playful/tools/tool-hand.svg | 1 + .../icons/themes/playful/tools/tool-move.svg | 1 + .../icons/themes/playful/tools/tool-pen.svg | 1 + .../themes/playful/tools/tool-pencil.svg | 1 + .../themes/playful/tools/tool-polyline.svg | 1 + .../themes/playful/tools/tool-select.svg | 1 + .../themes/playful/tools/tool-smudge.svg | 1 + app/data/icons/thinlines5.png | Bin 341 -> 0 bytes app/data/icons/undo.png | Bin 681 -> 0 bytes app/data/icons/unused/TitleBarCloseButton.png | Bin 300 -> 0 bytes .../icons/unused/TitleBarNormalButton.png | Bin 223 -> 0 bytes .../icons/unused/TitleBarNormalButton1.png | Bin 318 -> 0 bytes .../icons/unused/TitleBarNormalButton2.png | Bin 286 -> 0 bytes app/data/icons/unused/aqua.png | Bin 286 -> 0 bytes app/data/icons/unused/arrow.png | Bin 483 -> 0 bytes app/data/icons/unused/brush0.png | Bin 626 -> 0 bytes app/data/icons/unused/bucket.png | Bin 1908 -> 0 bytes app/data/icons/unused/bucket0.png | Bin 806 -> 0 bytes app/data/icons/unused/bucket1.png | Bin 1616 -> 0 bytes app/data/icons/unused/bucket2.png | Bin 2018 -> 0 bytes app/data/icons/unused/cameraBorder.png | Bin 1103 -> 0 bytes app/data/icons/unused/clear.png | Bin 3453 -> 0 bytes app/data/icons/unused/eraser.png | Bin 1160 -> 0 bytes app/data/icons/unused/eraser1.png | Bin 3574 -> 0 bytes app/data/icons/unused/eraser2.png | Bin 613 -> 0 bytes app/data/icons/unused/grid-b.png | Bin 213 -> 0 bytes app/data/icons/unused/hand.png | Bin 902 -> 0 bytes app/data/icons/unused/house.png | Bin 185 -> 0 bytes app/data/icons/unused/logo0.png | Bin 6415 -> 0 bytes app/data/icons/unused/move.png | Bin 294 -> 0 bytes app/data/icons/unused/onion_type.png | Bin 505 -> 0 bytes app/data/icons/unused/outlines3.png | Bin 204 -> 0 bytes app/data/icons/unused/outlines4.png | Bin 484 -> 0 bytes app/data/icons/unused/overlayCenter.png | Bin 286 -> 0 bytes app/data/icons/unused/overlayGoldenRatio.png | Bin 985 -> 0 bytes .../icons/unused/overlayGoldenRatio_org.png | Bin 377 -> 0 bytes app/data/icons/unused/overlaySafe.png | Bin 898 -> 0 bytes app/data/icons/unused/overlaySafe_org.png | Bin 295 -> 0 bytes app/data/icons/unused/overlayThirds.png | Bin 272 -> 0 bytes app/data/icons/unused/pen0.png | Bin 596 -> 0 bytes app/data/icons/unused/pencil.png | Bin 674 -> 0 bytes app/data/icons/unused/pencil3.png | Bin 750 -> 0 bytes app/data/icons/unused/polyline.png | Bin 430 -> 0 bytes app/data/icons/unused/prefstimeline2.png | Bin 467 -> 0 bytes app/data/icons/unused/printer3.png | Bin 1238 -> 0 bytes app/data/icons/unused/select.png | Bin 508 -> 0 bytes app/data/icons/unused/separator.png | Bin 1313 -> 0 bytes app/data/icons/unused/smudgeblur.png | Bin 408 -> 0 bytes app/data/icons/unused/thinlines.png | Bin 362 -> 0 bytes app/data/icons/unused/thinlines2.png | Bin 361 -> 0 bytes app/data/icons/unused/thinlines3.png | Bin 354 -> 0 bytes app/data/icons/unused/thinlines4.png | Bin 342 -> 0 bytes app/data/icons/zoom-in.png | Bin 651 -> 0 bytes app/data/icons/zoom-out.png | Bin 655 -> 0 bytes app/data/icons/zoom-reset.png | Bin 676 -> 0 bytes app/src/colorpalettewidget.cpp | 3 +- app/src/colorslider.cpp | 2 +- app/src/mainwindow2.cpp | 3 + app/src/statusbar.cpp | 26 +-- app/src/timecontrols.cpp | 41 +++-- app/src/timeline.cpp | 31 ++-- app/src/timelinecells.cpp | 8 +- app/ui/cameraoptionswidget.ui | 18 +- app/ui/colorpalette.ui | 32 ++-- app/ui/errordialog.ui | 2 +- app/ui/mainwindow2.ui | 118 ++++++------ app/ui/onionskin.ui | 16 +- app/ui/preferencesdialog.ui | 23 +-- app/ui/toolboxwidget.ui | 71 ++++---- core_lib/data/core_lib.qrc | 8 - core_lib/src/tool/brushtool.cpp | 4 +- core_lib/src/tool/buckettool.cpp | 8 +- core_lib/src/tool/cameratool.cpp | 8 +- core_lib/src/tool/erasertool.cpp | 2 +- core_lib/src/tool/eyedroppertool.cpp | 6 +- core_lib/src/tool/handtool.cpp | 2 +- core_lib/src/tool/movetool.cpp | 10 +- core_lib/src/tool/penciltool.cpp | 4 +- core_lib/src/tool/pentool.cpp | 4 +- core_lib/src/tool/polylinetool.cpp | 2 +- core_lib/src/tool/selecttool.cpp | 11 +- core_lib/src/tool/smudgetool.cpp | 6 +- 258 files changed, 403 insertions(+), 1013 deletions(-) delete mode 100644 app/data/icons/add.png delete mode 100644 app/data/icons/controls/duplicate.png delete mode 100644 app/data/icons/controls/endplay.png delete mode 100644 app/data/icons/controls/loop.png delete mode 100644 app/data/icons/controls/play.png delete mode 100644 app/data/icons/controls/sound-disabled.png delete mode 100644 app/data/icons/controls/sound.png delete mode 100644 app/data/icons/controls/soundscrub-disabled.png delete mode 100644 app/data/icons/controls/soundscrub.png delete mode 100644 app/data/icons/controls/startplay.png delete mode 100644 app/data/icons/controls/stop.png delete mode 100644 app/data/icons/copy.png delete mode 100644 app/data/icons/cut.png delete mode 100644 app/data/icons/dialog-error.svg delete mode 100644 app/data/icons/exit.png rename app/data/icons/{new => general}/checkerboard_smaller.png (100%) rename {core_lib/data/icons => app/data/icons/general}/cross.png (100%) create mode 100644 app/data/icons/general/cursor-brush.svg create mode 100644 app/data/icons/general/cursor-bucket.svg rename app/data/icons/{new/svg => general}/cursor-diagonal-left.svg (100%) rename app/data/icons/{new/svg => general}/cursor-diagonal-right.svg (100%) create mode 100644 app/data/icons/general/cursor-eyedropper.svg rename app/data/icons/{new/svg => general}/cursor-horizontal.svg (100%) rename app/data/icons/{new/svg => general}/cursor-move.svg (100%) create mode 100644 app/data/icons/general/cursor-pen.svg create mode 100644 app/data/icons/general/cursor-pencil.svg rename app/data/icons/{new/svg => general}/cursor-rotate.svg (100%) create mode 100644 app/data/icons/general/cursor-smudge-liquify.svg create mode 100644 app/data/icons/general/cursor-smudge.svg rename app/data/icons/{new/svg => general}/cursor-vertical.svg (100%) delete mode 100644 app/data/icons/grid.png delete mode 100644 app/data/icons/layer-bitmap.png delete mode 100644 app/data/icons/layer-camera.png delete mode 100644 app/data/icons/layer-sound.png delete mode 100644 app/data/icons/layer-vector.png delete mode 100644 app/data/icons/magnify.png delete mode 100644 app/data/icons/mirror.png delete mode 100644 app/data/icons/mirrorV.png delete mode 100644 app/data/icons/new.png delete mode 100644 app/data/icons/new/arrow.png delete mode 100644 app/data/icons/new/brush_detailed.png delete mode 100644 app/data/icons/new/brush_flat_outlined.png delete mode 100644 app/data/icons/new/bucket_detailed.png delete mode 100644 app/data/icons/new/bucket_flat_outlined.png delete mode 100644 app/data/icons/new/eraser_detailed.png delete mode 100644 app/data/icons/new/eraser_flat_outlined.png delete mode 100644 app/data/icons/new/eyedropper_detailed.png delete mode 100644 app/data/icons/new/eyedropper_flat_outlined.png delete mode 100644 app/data/icons/new/hand_detailed.png delete mode 100644 app/data/icons/new/hand_flat_outlined.png delete mode 100644 app/data/icons/new/line.png delete mode 100644 app/data/icons/new/pen_detailed.png delete mode 100644 app/data/icons/new/pen_flat_outlined.png delete mode 100644 app/data/icons/new/pencil_detailed.png delete mode 100644 app/data/icons/new/pencil_flat_outlined.png delete mode 100644 app/data/icons/new/selection.png delete mode 100644 app/data/icons/new/smudge_detailed.png delete mode 100644 app/data/icons/new/smudge_flat_outlined.png delete mode 100644 app/data/icons/new/svg/arrow.svg delete mode 100644 app/data/icons/new/svg/brush_detailed.svg delete mode 100644 app/data/icons/new/svg/brush_flat_outlined.svg delete mode 100644 app/data/icons/new/svg/bucket_detailed.svg delete mode 100644 app/data/icons/new/svg/bucket_flat_outlined.svg delete mode 100644 app/data/icons/new/svg/camera-move.svg delete mode 100644 app/data/icons/new/svg/camera-rotate.svg delete mode 100644 app/data/icons/new/svg/camera-scale.svg delete mode 100644 app/data/icons/new/svg/color-dialog.svg delete mode 100644 app/data/icons/new/svg/eraser_detailed.svg delete mode 100644 app/data/icons/new/svg/eraser_flat_outlined.svg delete mode 100644 app/data/icons/new/svg/eyedropper_detailed.svg delete mode 100644 app/data/icons/new/svg/eyedropper_flat_outlined.svg delete mode 100644 app/data/icons/new/svg/hand_detailed.svg delete mode 100644 app/data/icons/new/svg/hand_flat_outlined.svg delete mode 100644 app/data/icons/new/svg/line.svg delete mode 100644 app/data/icons/new/svg/more_options.svg delete mode 100644 app/data/icons/new/svg/pen_detailed.svg delete mode 100644 app/data/icons/new/svg/pen_flat_outlined.svg delete mode 100644 app/data/icons/new/svg/pen_pattern_detailed.svg delete mode 100644 app/data/icons/new/svg/pencil_detailed.svg delete mode 100644 app/data/icons/new/svg/pencil_flat_outlined.svg delete mode 100644 app/data/icons/new/svg/selection.svg delete mode 100644 app/data/icons/new/svg/smudge_detailed.svg delete mode 100644 app/data/icons/new/svg/smudge_flat_outlined.svg delete mode 100644 app/data/icons/new/svg/trash_detailed.svg delete mode 100644 app/data/icons/new/svg/trash_flat_outlined.svg delete mode 100644 app/data/icons/new/trash_detailed.png delete mode 100644 app/data/icons/new/trash_flat_outlined.png delete mode 100644 app/data/icons/next.png delete mode 100644 app/data/icons/onion-blue.png delete mode 100644 app/data/icons/onion-red.png delete mode 100644 app/data/icons/onionNext.png delete mode 100644 app/data/icons/onionPrev.png delete mode 100644 app/data/icons/open.png delete mode 100644 app/data/icons/outlines5.png delete mode 100644 app/data/icons/overlayAngle.png delete mode 100644 app/data/icons/overlayCenter.png delete mode 100644 app/data/icons/overlayGoldenRatio.png delete mode 100644 app/data/icons/overlayPerspective1.png delete mode 100644 app/data/icons/overlayPerspective2.png delete mode 100644 app/data/icons/overlayPerspective3.png delete mode 100644 app/data/icons/overlaySafe.png delete mode 100644 app/data/icons/overlayThirds.png delete mode 100644 app/data/icons/paste.png delete mode 100644 app/data/icons/prefs-files.png delete mode 100644 app/data/icons/prefs-shortcuts.png delete mode 100644 app/data/icons/prefspencil.png delete mode 100644 app/data/icons/prefstimeline.png delete mode 100644 app/data/icons/prev.png delete mode 100644 app/data/icons/redo.png delete mode 100644 app/data/icons/remove.png delete mode 100644 app/data/icons/save.png delete mode 100644 app/data/icons/saveas.png create mode 100644 app/data/icons/themes/playful/controls/control-loop.svg create mode 100644 app/data/icons/themes/playful/controls/control-play-end.svg create mode 100644 app/data/icons/themes/playful/controls/control-play-start.svg create mode 100644 app/data/icons/themes/playful/controls/control-play.svg create mode 100644 app/data/icons/themes/playful/controls/control-sound-enable.svg create mode 100644 app/data/icons/themes/playful/controls/control-sound-scrub.svg create mode 100644 app/data/icons/themes/playful/controls/control-stop.svg create mode 100644 app/data/icons/themes/playful/dialog-error.svg create mode 100644 app/data/icons/themes/playful/display/lines-invisible.svg create mode 100644 app/data/icons/themes/playful/display/lines-outline.svg create mode 100644 app/data/icons/themes/playful/display/mirror-horizontal.svg create mode 100644 app/data/icons/themes/playful/display/mirror-vertical.svg create mode 100644 app/data/icons/themes/playful/display/overlay-center.svg create mode 100644 app/data/icons/themes/playful/display/overlay-golden-ratio.svg create mode 100644 app/data/icons/themes/playful/display/overlay-grid.svg create mode 100644 app/data/icons/themes/playful/display/overlay-safe.svg create mode 100644 app/data/icons/themes/playful/display/overlay-thirds.svg create mode 100644 app/data/icons/themes/playful/display/perspective-angle.svg create mode 100644 app/data/icons/themes/playful/display/perspective-onepoint.svg create mode 100644 app/data/icons/themes/playful/display/perspective-threepoints.svg create mode 100644 app/data/icons/themes/playful/display/perspective-twopoints.svg create mode 100644 app/data/icons/themes/playful/menubar/clear-canvas.svg create mode 100644 app/data/icons/themes/playful/menubar/copy.svg create mode 100644 app/data/icons/themes/playful/menubar/cut.svg create mode 100644 app/data/icons/themes/playful/menubar/new.svg create mode 100644 app/data/icons/themes/playful/menubar/open.svg create mode 100644 app/data/icons/themes/playful/menubar/paste.svg create mode 100644 app/data/icons/themes/playful/menubar/redo.svg create mode 100644 app/data/icons/themes/playful/menubar/save.svg create mode 100644 app/data/icons/themes/playful/menubar/undo.svg create mode 100644 app/data/icons/themes/playful/menubar/view-reset.svg create mode 100644 app/data/icons/themes/playful/menubar/zoom-in.svg create mode 100644 app/data/icons/themes/playful/menubar/zoom-out.svg create mode 100644 app/data/icons/themes/playful/menubar/zoom-select.svg create mode 100644 app/data/icons/themes/playful/misc/add-color.svg create mode 100644 app/data/icons/themes/playful/misc/color-dialog.svg create mode 100644 app/data/icons/themes/playful/misc/more-options.svg create mode 100644 app/data/icons/themes/playful/misc/remove-color.svg create mode 100644 app/data/icons/themes/playful/onion/onionskin-blue.svg create mode 100644 app/data/icons/themes/playful/onion/onionskin-enable.svg create mode 100644 app/data/icons/themes/playful/onion/onionskin-red.svg create mode 100644 app/data/icons/themes/playful/preferences/preferences-files.svg create mode 100644 app/data/icons/themes/playful/preferences/preferences-general.svg create mode 100644 app/data/icons/themes/playful/preferences/preferences-shortcuts.svg create mode 100644 app/data/icons/themes/playful/preferences/preferences-timeline.svg create mode 100644 app/data/icons/themes/playful/preferences/preferences-tools.svg create mode 100644 app/data/icons/themes/playful/timeline/cell-bitmap.svg create mode 100644 app/data/icons/themes/playful/timeline/cell-camera.svg create mode 100644 app/data/icons/themes/playful/timeline/cell-sound.svg create mode 100644 app/data/icons/themes/playful/timeline/cell-vector.svg create mode 100644 app/data/icons/themes/playful/timeline/frame-add.svg create mode 100644 app/data/icons/themes/playful/timeline/frame-duplicate.svg create mode 100644 app/data/icons/themes/playful/timeline/frame-remove.svg create mode 100644 app/data/icons/themes/playful/timeline/layer-add.svg create mode 100644 app/data/icons/themes/playful/timeline/layer-duplicate.svg create mode 100644 app/data/icons/themes/playful/timeline/layer-remove.svg create mode 100644 app/data/icons/themes/playful/tools/tool-brush.svg create mode 100644 app/data/icons/themes/playful/tools/tool-bucket.svg create mode 100644 app/data/icons/themes/playful/tools/tool-camera-move.svg create mode 100644 app/data/icons/themes/playful/tools/tool-camera-rotate.svg create mode 100644 app/data/icons/themes/playful/tools/tool-camera-scale.svg create mode 100644 app/data/icons/themes/playful/tools/tool-eraser.svg create mode 100644 app/data/icons/themes/playful/tools/tool-eyedropper.svg create mode 100644 app/data/icons/themes/playful/tools/tool-hand.svg create mode 100644 app/data/icons/themes/playful/tools/tool-move.svg create mode 100644 app/data/icons/themes/playful/tools/tool-pen.svg create mode 100644 app/data/icons/themes/playful/tools/tool-pencil.svg create mode 100644 app/data/icons/themes/playful/tools/tool-polyline.svg create mode 100644 app/data/icons/themes/playful/tools/tool-select.svg create mode 100644 app/data/icons/themes/playful/tools/tool-smudge.svg delete mode 100644 app/data/icons/thinlines5.png delete mode 100644 app/data/icons/undo.png delete mode 100644 app/data/icons/unused/TitleBarCloseButton.png delete mode 100644 app/data/icons/unused/TitleBarNormalButton.png delete mode 100644 app/data/icons/unused/TitleBarNormalButton1.png delete mode 100644 app/data/icons/unused/TitleBarNormalButton2.png delete mode 100644 app/data/icons/unused/aqua.png delete mode 100644 app/data/icons/unused/arrow.png delete mode 100644 app/data/icons/unused/brush0.png delete mode 100644 app/data/icons/unused/bucket.png delete mode 100644 app/data/icons/unused/bucket0.png delete mode 100644 app/data/icons/unused/bucket1.png delete mode 100644 app/data/icons/unused/bucket2.png delete mode 100644 app/data/icons/unused/cameraBorder.png delete mode 100644 app/data/icons/unused/clear.png delete mode 100644 app/data/icons/unused/eraser.png delete mode 100644 app/data/icons/unused/eraser1.png delete mode 100644 app/data/icons/unused/eraser2.png delete mode 100644 app/data/icons/unused/grid-b.png delete mode 100644 app/data/icons/unused/hand.png delete mode 100644 app/data/icons/unused/house.png delete mode 100644 app/data/icons/unused/logo0.png delete mode 100644 app/data/icons/unused/move.png delete mode 100644 app/data/icons/unused/onion_type.png delete mode 100644 app/data/icons/unused/outlines3.png delete mode 100644 app/data/icons/unused/outlines4.png delete mode 100644 app/data/icons/unused/overlayCenter.png delete mode 100644 app/data/icons/unused/overlayGoldenRatio.png delete mode 100644 app/data/icons/unused/overlayGoldenRatio_org.png delete mode 100644 app/data/icons/unused/overlaySafe.png delete mode 100644 app/data/icons/unused/overlaySafe_org.png delete mode 100644 app/data/icons/unused/overlayThirds.png delete mode 100644 app/data/icons/unused/pen0.png delete mode 100644 app/data/icons/unused/pencil.png delete mode 100644 app/data/icons/unused/pencil3.png delete mode 100644 app/data/icons/unused/polyline.png delete mode 100644 app/data/icons/unused/prefstimeline2.png delete mode 100644 app/data/icons/unused/printer3.png delete mode 100644 app/data/icons/unused/select.png delete mode 100644 app/data/icons/unused/separator.png delete mode 100644 app/data/icons/unused/smudgeblur.png delete mode 100644 app/data/icons/unused/thinlines.png delete mode 100644 app/data/icons/unused/thinlines2.png delete mode 100644 app/data/icons/unused/thinlines3.png delete mode 100644 app/data/icons/unused/thinlines4.png delete mode 100644 app/data/icons/zoom-in.png delete mode 100644 app/data/icons/zoom-out.png delete mode 100644 app/data/icons/zoom-reset.png diff --git a/app/data/app.qrc b/app/data/app.qrc index 1c78e052b..00c7fb59a 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 9ab683295817a9b90bb2c6b682ebdb2419212d32..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 771 zcmV+e1N{7nP)`jWp!s^VX*>YO(rHLknr_?|Nb5O^XJdb8#iw3LRJqDK#VX0WMpI% z*x1-|I5;@cxwyD^fHVWpAO>b;X0ZInL$EAg5lk}cMNaezGVPv0vW*Y^XE^7FJHba0pi+=7cV{l z2p|@aDIl9cn)&(p8Tk147&tjO8F+bl8IqEc7`VB)8M?c>86H1=%mDNT1JELn%VK~o zdkNH90uVqfAU^{YrGsn*X$H|Cr-Qs8E-uah3ImWgKr{?Py_IflZM_R1fLK5Q2J$mV zBgkfuW|%^te?hKe00tgB&_LoKH9)mIKrgui1P}`-v_ZCkoUfy!1C|5@7|1mrKYnBY z$}@uGnkO7(?O(2|?mk0JcC}5vIe-37YKzMjKI6xU0 z8NmTsR8$0Wi6%e*F~R%nMq$?UjdjFa)9xl4oW1T0E2}-2tY&s3&>`W<}F*cfHM<7 z05PIxXJGPl0|uTuD3L(p8J?X%eg-)kSv^320RR|GF6kM>p<)04002ovPDHLkV1iVQ BEldCa diff --git a/app/data/icons/controls/duplicate.png b/app/data/icons/controls/duplicate.png deleted file mode 100644 index dc2285a0c7e2cb8d0c9b8be4ff320e4f71c62cde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fHRz`)E9 z;1l8sRG_D)XJBApY;0_9Zf39UUDL6B8R78y6Rsl$4a2nVFT9m6MZ`pPye;R8(AC zTvAd}R#sL~QBhf0Syfe4Q&Ur0TU%FGS6^S>(9qD>*x1z6)ZE)6-#>BU#3@sz%$zxM{`~n17cPAG@ZqCJj~+jM{PgM5=g*(Nc=6)p%a?E8zJ2%Z z-TU|NKYaM`>C>k#U%q_(`t`?;AHRP6`u+R&pFe;8{{8#!-#?&FN5N5zedtMjRAmzTy(y#1fE zU0(d^Ix_7v!~631@D-f1C;F|GwYA-oa{Bqn#~KfqgSP!ludBJ8w^=!sn<1$1iPru7 z@A%(a#?JLrIVAY-gHhyrHSsO%MLt5iSv$(vqf-1r7M~Q%udDm*A;`QpIVOUAq7UNfCH*=j`i%++#qe~X!(ghQBodg5re0zpUXO@geCxk`!sC; diff --git a/app/data/icons/controls/play.png b/app/data/icons/controls/play.png deleted file mode 100644 index 4377798c48dbec63c6a318151006054db8aa4e9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>?0v z(btiI;l}H)G9s&he1&9>AYTTC+!+iE#eED6uRItS+JM64?->{_d|+TO`p&>0Q?l(+ z4mVJ6nWu|mNX4zBKmY&RGpjNvG)QQg0!a-sAYs7c;qTAReP)j3dKm|uzopr09K4$!~g&Q diff --git a/app/data/icons/controls/sound-disabled.png b/app/data/icons/controls/sound-disabled.png deleted file mode 100644 index 90d536ce6a6e117984559dc319be6934fb7ea67f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 350 zcmeAS@N?(olHy`uVBq!ia0vp^d>}Rl8<3oNC%zs?u_bxCyDFdh=kd;r2pW9HV!4W9*+SA1`MC1J2OSXPajv_}t${#tD zk=diFy2i%g-`3Hh|qbrnukVr!eH9+WXXK{v+IiPfOX^e+ur3cxYqV z=A31!b^D=S^X5whdIgijdfA!}{#v({?_(&#bc@!LEoqZ4c2w1_x$pnMFpaf;?-Ny} zB)k3xm5<*lZ_@c$qhsUxy<9+0u-nyT^;G^946mL9`+V@vm)bMcF3)YxKidS+GfjmT r?)^5->s)Snh3{UoU%}LG`x)bZ&zN-VpvGCCw;4QL{an^LB{Ts5&+COB diff --git a/app/data/icons/controls/sound.png b/app/data/icons/controls/sound.png deleted file mode 100644 index ba6a4521658dd77b38048d5b7eccd8f7d9389680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^d>}Rl8<3oNC%zs?aTa()7Bet#3xhBt!>l1&1>l|w-+CD?;g1E}&)txD*;@a2yriWU(m?D5RveX=5fEhXF%Dtcl{UH_oSl4q@bP0l+XkKAn;8O diff --git a/app/data/icons/controls/soundscrub-disabled.png b/app/data/icons/controls/soundscrub-disabled.png deleted file mode 100644 index 01aad7c7b649a9b5c0ffe93fa70018c9ecb578b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7202 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 diff --git a/app/data/icons/cut.png b/app/data/icons/cut.png deleted file mode 100644 index 4222da9688952a20d213d36791546935be9ae76d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1016 zcmV$0!J+vsS+hP%9Fl5ky%l+ei=l7lOobM6{|0~+s+U^L2LataWPVi|BCX?x^ z&1M6gPUpaJJlkiu!C=^LwOYXy4Gj%H_PjI8r7~Rp);f6;?M*+jIGM~U44reN= z+UAx~u-R;_Phc0KR;vvjn8)Mw_zXt3qQY*syIWCJRb?lW$xYk-4;gHT;v4Kx~!=oMU7S2x_**$G;$_Pb0b`ydjD3Z+u%UWr5!>;kx4Zc=-D zJM{PWLv3v>l$4aL^9n96FUN+4hCFR_AKA4L8k!SrWJUwS6$%CP_Vxn$QJ^3{e~$-V zR@50EdER(EEM4_9_^9;3f&+L8VSo9rRU2AWMstMf|{Bd z=fp zuv{*O*49?&>gs}lfdMy+ZUZnF3_xpucCQ#xSYbY4TAYbO_A?^7`NTFce+iptgNEKgTKB;_&)F^5M_74hnpg1B zT)k>>rW6*D1ks*^&{U~{n4^M#Bf_0tA>%C~o|0?A&dOm)jw;UcJ@Q;59&lC#3rYCN z3BxG)QP2e~th%PrY-44X^M m5SZNveu-E;fdG@ngMR`)q7TJ30Si$80000 - - - - - 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 0e21816019c046462e87b924e7585bd565b35a2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1320 zcmV+@1=sqCP)WdKBJAT}UMb!;FqFfcSAF)}(cGCDOdAS*C2Fffw?u&)3B00(qQO+^RP z2L=iw0AUV=;s5{u32;bRa{vGf5&!@T5&_cPe*6Fc00d`2O+f$vv5yPok>?WIalb!vWnR{nEclIao!6brSxO->M z-Z|en-}%m2;D26hrwfThVuW+v=ledVubfWM)}$0X2!imLHdD`1dWP=@(sA635OQ>I zaPXYI6F@$1ztq=vw0UW13BHslJ0*A?r_CS)$Z3Ee(4lDt-PQ<@A(Po?DHf++CBx1D z6cN_e()eX|4rR%}G=|rsqtunC5Lo6N6)q@Fo9NT zw=F=66=SEL>wEakZ%^UtgO~8@{4KOJ073!K?}UL=DudC{s}%}ELV~(5$fnf+qUVj} z#+cPgae-?@H@)4+8uUXQ53%A=7cc+NkD;fBLwblU@vE<&z*$dxzf_9{7(VFw>L5J!K1H#$3wFzV_U zsVQ$$ro^c4Dj|CmjzejdB;1?`i{=*uewzFp-`}~3>n~o!_4x~Myb?8^#J+tTv6zWH zdngbJMisXPAcWYg!OcHYV&w9MkSqY>j6T64QG>m8{24YmwSdXgJ$$_P0=hqVo@4}k z@xmMG9>&L)F*S9A3d^6ms=+%CN0i1#N!P_ zrc#aKn+Obx%Bqu;Rcuvtd1~ZtH!6zETN7ve{mEp~Q4w(>E`Fbdcq_HDMo!3)2zjq4 zNu9)dU7j>$wQVyX=MPjQ7r><vF@is(*7^zMVsZvE%729ks)cZmznPC^xlv2&Q zuG1XD{WzZeh=v)59%({`0F-i+!`i01^xu)8z32pid45*_l=jG-yNmC0u5FB6xwKj+ z`M|(H*rq2>CVGw? zJ3MdOMUv^y$mMc^XisXI92y&Iy8@i?x4ye8fUWDuk@q6)?M>g&D0q-E7y+!|Wb*9Y ef9vNzTYmvQE6TC}^o1<|0000 \ 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 000000000..4b9c9cf3a --- /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 000000000..522ac4e42 --- /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 000000000..59753b35c --- /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 000000000..002b17d46 --- /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 000000000..24e96799a --- /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 000000000..d8f4c56af --- /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 f44227b9be164794da7d53000d675d581a04bb7d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140 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 IM6N<$f+8)0vj6}9 diff --git a/app/data/icons/layer-vector.png b/app/data/icons/layer-vector.png deleted file mode 100644 index d264d8c53fb3d0646d6c723251597a1ff3216b56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 855 zcmV-d1E~CoP)c63`|W;WvHvG0~-hsKrA2vq!{D^7^rV-W~i&F0*iyduV23y7S5Z)aA@}y z2B23Mctm9xVq#*z-WC@ZXW--GV_30b#eaYR0vpE4$_h8==+R>g7oQ6d;xUk!3*yhWL%9H4zKZH_$+#xVgHvm4E*2O7^40&G5BlzgBu1)j!!=R2gfYP z5&!=E11BGV0AhhDsIF}UsuEy$v9EyP`?o6$pVvGDi+}sWz_8ewfx-1I1B06q1H)Hf znAIHSX0UP#0mm%JAXwx91Q6IT5NF}MS@4AGlXRJ3AHzcin2J|h85ojXI2okQh%j(+ z^DqQQr!d4PCNsc%1Ey1w??@R)}F;`7s6|AtA6@WDJTx z5C8}uxXWN^3St%`)@%jKDUfhO7zP6X0mOo?2n6_;_!x|%j8P2w_T(D_7Z(@UQkVg7 z!=QYC0Ac|JAjlwO8&)k?h1F%aN)=G90GR_2Kw!gMTwECL-Mh!Y&d$z2D){;HCxeob h5`(w5Hv>R`0RT}Z3jo#Vc&Y#Z002ovPDHLkV1kT6WA6X} diff --git a/app/data/icons/magnify.png b/app/data/icons/magnify.png deleted file mode 100644 index 3ee87bc0dcdc91391a50c1cc40d8edf5e9908553..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 760 zcmVz^Q>-8KIssI20AY({UO#lFGm;eBCjsO7aNB{ta0001Y z?*IVM@BjcZ_W%GKbhgrL3jhEDZb?KzRCwBA{Qv(y0|+c!xbXj@M~@huJbA+K{{4Ff zUS3`XBO@b*?Cfkt20Q>DfEYmr%$zy%zo@7vgMz#~gP6ED!?$nW7_MKx&ah#_1_o|! zZiXpSrrlOxBp@KbaPQtdoCW{{5YyYYZyCM;Z3Za@Y5e)?7sD?g zW@Tk%P*G9>yXf!VzYKTo-1(2q0Du5u;^N|BxC#uguV24{F-QYDJ3E7xmKFm86B7d{ zW~8O18UFqIht&lD0mNixW(E$Z&p=mz{QU9bM+PY=DTdRhPcv-azMWy!s#Oe8QBgp% zn6MfE5I~I2o;~~D+SeXPcfxNVG#R>-hfB=l>1PCAo zP&xqNi4!OOx3jhV548Qin3&jqU?TcoTU+}d7=8bNUIODCJ9hkssfS^J0AhrtL}YO5 z)-ABYKY#u(tXsR5Vb|{6U@w8d)~#DXX$I~RfB<5_<}YC4LMUKif!lEA%ozr6Z*PWe z+qV68a&iKz0SF)lbSI(X=g*)22Ss6XbMt>28ymQH4jecDb|pXn5i|gVU%q_#ADEc_ zH#aqb4dLM60EgA%$B+L51P}`Y5davdjKCcFKR!O5fsv6BoF+g?9F$%F0tjm$;sU@l q@*k8|fPu`QqN2h8bO{4MfB^u64QGc)u8TDQ0000p4-h8YqX zEDVe;8yTTQOJ7UFj9w1Y0tNwx7eeU`Gv+risy9Y39b@EB5K}q3yWxgJhj7CWYdPkU z#vcp<)(juH8yK9n{j~4=!FEEGfsw^1!CG=w;tvT2hDM$M_D9cCGU~WL896W*I{jVB z_f=e@K#bv|zD*(n*J6E#H*81J8JRdDnVtzbZ7krIx{&~~l_P~ioss#Sl$47hQZU-&t;ucLK6TIaa;NT diff --git a/app/data/icons/mirrorV.png b/app/data/icons/mirrorV.png deleted file mode 100644 index dfe0c01bd9f55fdab5f15f74d40843aeabeca857..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1277 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 Z=5Gs+!ij(D%5(q#002ovPDHLkV1g~bP0;`V 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 f8ad7c56a3e8b1bf0aceff74393fda4b35f0e72b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1290 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 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 b18abef14a3eb61d3e0d946ac4f8e1db55929eb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1298 zcmV+t1?~EYP)TKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600WLmL_t(YiM5l>ZyQAzho5)W-dQJczCXYr)M*olC_(}W<${Dt8{kxq zNV&rwKoAlKERh=r=fH&{haxy3!J!mkA#n_$6cq?xO%uoYPTV98b{wyFcR0AQ8oX)J z3PyT0vvYW!d4D@2oG`{FCcl2;#phmW)ls^;x|q)`vytD%7=tl}AP5@PK@il}ass?_ zB+;`)_BT~16iA*u$E8ab+01XVw7T8^4HKyo8i=&(s45f+#QXaAae0{!uY5v#Tb#Cd z3%>6+l;?Rz=92_lyA`k0*4s~o zjcAQHnyq4q%=9#)&puO+;@{xIJ?lRC3veacB%_tU0}(_7Q3??ux3i{YzBLyV-*YPfIK7>S0;3dx2q=Y83ZsOgAJn?>ee!{2 zwvGL1Ao5;jDzyL*Cg_i}#@YMjPcVk~ND`|_W2#jUK`FtwRg5SCrD$)BlUv_n-nXy0 zVohYGQor8|3lrSjp1JvgUn<91hR>jTyD`-&#+o$Ro$|uq#|Z|3;pVdg z^$m$e!LmRU#`P%9uVn+bdNGqu%^wFo9AUTUg5A|YtiKNcZ@)}sBabKr%3|-2h1tNZ zj-Cd7te|=5@%qQ^%+IxV0PM})+6&5+S2O9<=4s#!g0YE7L8+Ib@n!&uw{u9uLicu2 zx}C$#?~G^CspZqc!#X$uMtJaIX@Ld#H+YN1GJekq+vr>;H-JWTL}6>|V^^E&|O zN4hC5t6HJlg8zN|13hF9i4vxu^k^lez diff --git a/app/data/icons/new/eraser_detailed.png b/app/data/icons/new/eraser_detailed.png deleted file mode 100644 index acb6e5f69289491d6d5ace80739e25d1ad908163..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1163 zcmV;61a$j}P)TKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600Rd}L_t(YiQSb=Z`4*4hF_08o`fM8GBgk|L5*5KeEbR)Y$_ok1XNWk zwW|69`XgfRyFkhgO%MoB3xW|W300-4+pLf>i9O!5l|t~b9mOAGpVLL+k!RveBFc&@ zUD?;Rp67gAT`Ty2VOiGfAM458S8vms0Mv2cj(vFm{FI2iZZ&YVuBA@*9cBRK;|Ofq zJ}n|E%zOm^&NM1B4ZV-GtqSD-3T)e+%d%_*z-0hnW{3z*PpfEDV|2IFQpu47wr!tb z<`ogSC?Y7%EXy!kS8zg4(0#8$M#^=lz_P3sfH%y1A&*)_z|1JFXOAnGsU_&X*M=lx z1-5O^@4?RlfXHqUa%l0MnQ`(MaI%)5>sAMn0|k!boMq-W0M3m@TU_T?V7D*LN#L`I zT}k)vZB)wOyOm`kFNnyBh%6K% zF2Y7pG^Vr%|uhbNGEC`oS;^9>*9f*poaC!|;x(s+gFV zKp2J~qBarzco_Uhf<4K@Fbuy@RTZx5-g8~|C(|?!g&$b}082|t%TW|HH#ax08-{Vv dAMz6%{{dqAmiYTiDSrR}002ovPDHLkV1jeK1nU3* 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 c80869e8f5533e2242c90bad675554f17c936686..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1206 zcmV;n1WEgeP)TKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600S~fL_t(YiLI1hPZL=f#-Eu^|IyNxT1qR5h#NLW)ND3cA!MT=SrJdN ziDs|#*6ePtC-W&L^Ck9b@7y(+y+G1-A*p{bkwo2Ki;`^wq3ujt3&o+GbM^wMQz)h2 zK9}db=l47BJ2}JQh#`^^bUB3h-HFl558ryBS6RFAn(c`2klSHT{CwlerLRv0aUsIv zb)I3XcHj}(v4BWQxXWoz{xWrCWUwy?0B{N%E<|{wa}3qilFkG~Qu;hj$Lz0Dqvr?0 zJxzPNl1!dqbNd(qA}LX?%P|`xqr;~{ftEE6fx}4DhD?F(m9!TSN$Kx)JLh6#?5qA0 z{N&1;i{(Qlj4>qQPf*Hh$QY>SO#G+lbY6t$0r}GRI0b^Jns*C zK1=jc1tE(hk$ej6eI988)q;r$RTfjNy4?4YKXvlw>PGdpjpzJ*9(O}hEN|dMD1c;g z9@=USX#)Bjyq*Bk3)y@*vNlrTP)TKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600fgsL_t(YiRIK=Y!p=(!0~Tp=eo1Av)xO(-QAXgDXmH*(jYV?zzYNj z0n{Rvm_Qma1+|7o3`#)JgdjZlKtybj0ES?~6J8P;AO@l$F$5$_D@75uTP)k|c4l|y zwln7(9|R+mOZVCDaVGQsWlqjH6qGTaY+PNJ*YaBc;FtFgeA|8ZdGDw1(g3iV0@=7xKXYYv<*GIOaMEt=QOVj*$k)|jLOM9{KXxZHOwkp3%k z{EYU(nXO!DlRGZ*rj<`6c9&T$+HAr!zEiNRpSB*iaJzIt__Pm-jM z4;?v@xxcgP@HaOAVEj3#O(ewkwr%V1`~6jVv3Oor)gOpuMZK7`g(UCtN<~>AG2ATI zux^bO@^5@`@Zjh}cL@ND`vaciZsb*Uh;rQ=xtX5CyLdl1E^V6zIC>GXnM@{|RWJOm zj;z|((^Gg<{DFW^^twZ*&U!quvO?em))M zAFkcHe&3x}`^yR5KmVQsB^Wz#Ten93P>0*rZhdRtqdxk7&EH`T4SbYA7uo;-002ov JPDHLkV1o8^zMB95 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 11c398feb434f6b1b3db5ed4b55e8582bb13cdf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1331 zcmV-31TKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600XZ{L_t(YiM5o=Yg|n$Bnp9#El?6kW$d44=|XNVj;e&p}jYz(=?OO`#R5iT_h7G z$)wGI{@`-Y;lk&8zTfZR5;QUAU!$MRK6U)r{NugIWE^~W@u$_*b$`C?X!f0Frg~4e zwOG(fL1_i06tq&Xnh~%q@~BNx{IRkMAtdJK9=rd+6YbwVH{Ex-z17}=D+whfv{KNz zNdaZj=YRO6vdN{!%JnrEt#IzlL%65af|7Czt`xM^&{{z&4Z9hEg@uKU{Lsjy4PU%? zyt6eIbA_WV4hR4#1*B5Y#%zmh#nhC8?fJ9ftKXc?lb^O8>r33S52-XVnG6zRcH*gZ z(3As8=@9^zW@l#?2L^|(Ym>T4n^XU6R!A-b*tU&0jv`X*r+I zZwMiv6KhkObX99J7>5DLS_vG-MjVHbLPE$x3Q$UO06w3dp1w9bJbasT4j}|M=TL@J zgfeSV=_i#G#IZCS+kx+gxbjzK?XCip(hPu4CMPG|k&%&G_3)iXB^yeaveNnqf`nM9 z1hKPNSx>z?3Q$TN03VN!kKY;_8@pM9$8ikKIi!?3B9YLyrPS-j=z*+b6Jj{-YfJAv z@D@=@Z2%vQj*eC)CMMQ|5IgYtb-k1FX8h|lccYS|7D%=YIU9{T*oJ>lC=~eA)Rb3$ z^1bicH{bVd&+{($k^Va1df-Io5m=eregR6U1>n7*p&`u7%-pEYydGZLODW;|zU8{^ zn`xT%0eIh!wfs9&rBEcqX<*>x{{H@B#bWVFqeA!YgCMXx&wC?H(>})7>NewTO6hmy zs<0XfTqcCf)pFhou6Gdxf#tgHg(OKHVT|3VhcL#z0nqSQu~aUXCwE;#&Jjh?zLUGI zdp=2$Q;e~dgD%TH0mfJw1i^Z>TD3UmyNayA!!Wd5*L^ifk}<~E?+3vFU|&R(()pgA zo@D?@qhiA_v^>vyB}tMqjIp2Z1m7n>DJ^w(cfU|56fSSWNEAgd#-wo^BZ?y8dEQIL zn6r$rt9OF~0I7rLIyySOFBXdzg%D{F1Y~h>u@pto1CHasahzqP)Sxlu9AoUq!@z;v z9h_`$Z@gwvcS*=#{ob$IB pV_!4_JJe80dnu*a{~Y{(&A+o*-ENE8P?rDz002ovPDHLkV1nGDV~GF& diff --git a/app/data/icons/new/hand_detailed.png b/app/data/icons/new/hand_detailed.png deleted file mode 100644 index f1bb467c1c2e3fa704c612c05f409788044c1e15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1270 zcmVTKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600VMKL_t(YiPe=~Xj^p@$G`Vaa&Ht<>!3SUZR>Zwda1N<-;GA2OP1?x@A~Gc|-4dFn?bz^wdt?y*!#Gzn9CtQpREIku%+kzZIxoN&8sSh!E8)ToOylr@;O%n zZh*pX6F;tpF)_Auham|I3sdR!`MsSX+{rD1iBzxG^JTSMPEL=19oP)u>`W^4d*<4t zqofXL4|T%h^L6W3(g|%v}f7hWm({763Y;w`8R*#H-^DWF$u^vPO z5(K@?zaB)k4H7maNMPYSHz5E}{KmOcT1$WfIaxA{4=2u?g)ps+P$I}44}@ibY_SpS zgfkITNp3T4-~OScq9$F(yL)@yc`6dAs{$I{vb7ej*A>(1f8vez(ykjc)VtZ6^J7;p zU1|syP3;adE0r%=Dr!(g0EVWMsqv|6375cT;Kp22l|6R_IFK5E1GA~*9To%@1af?D zoNF(Fs5c2HimY55`KSv3)K=kB7seDO3RMlmmrf@0OcbP+TQtuIm2&wIfKOaE`1SOO zW42|PEW|CZ&o@1=^U(o!=dHDwwc1YC4Nkp(Y|!ubf6634#7!Za+kl9WTgWcB0#@?* zkKIGqRYh|HKs=uKqf{(?<_f5+t}aerpX~1pcf%+Yu$*7Ss!>EB7)0NL4?vcsI#4(t z#kG?NmSrK4xM8g+-04@W#0O0fJ gXw3R2ng9Lz6ApFuB-_X51poj507*qoM6N<$f~A}{6951J 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 c9702db012d6d48250a34eb7cc295497a34f6e30..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1241 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 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 fac8320e349992a67569f14c52fec2f1969d7dbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1320 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 SnJEka0000TKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600Y@cL_t(YiM^BCYg|Us_&%wmc*(aMnSbfStfuhOmH%;VdIQC48DWe0#PB zcMNs{VGnj4Xdg}tcBc?%HENU0;r4W;xU$lnoJqi45hm_}JKw|!#0jFugikPs+f(J@ z>gv@>+?$eF4&Y9}>>=C%_x_*gpw+02Gl#XQ^32nhu2tg0({eZW-j(j;y@-SbLnpvT znZvEAxx$szYx8lk?_qfg@x&6M@nx9Zdth??+?l!peHSM<2qC;aTDthm{M8p?HFF;0 z1;Bd`&kk(T zpw+0A5D#B1FRvg3j{qQe0SiOef!dqhSbF()r#|FH+#>`+2(OM5R}_>&@B(26?*;4# z>?~++vk!OV`fJ&F69)@0P=GnSRk(0D4+MDOK3tf1DOnDHB8oq(J3U^ha-k1pj4jJaU8vJOrX`MEvWqXVzE+&a~8}F z>^iY98IaL^xY6w{ybYZth!PEm!nyKf7U!autM%sEv5346wOnCoh?yYViN=uy84YVS z9BCMx!kQFTr_kws=eBnd7&up%OyXR0y;^Us0XSZ=`0&Q`{8KSA!;t|y)6Lz$>Nc!S zK_-Px6PPT4P7~<8HpZgZ%vHv997iu!>&>5fydwgwMr~CMPs~k~N^oT0NQ243nyjC_ zH5rUfvA^{d^EGfPa5&8$aH?hxgun6N`&;*mTKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600Cu5L_t(YiQQGpN&`UW`*%q8?xzU$ZzO0A7I}~!i#6pHP80g2Jx zI*W%70#2stXm^dJqernKFWajuojjQdr!cIqaJlm=$tvS5veS(7e|I{;j0kCbGevxw bc>e+)S9VWc=j6^T00000NkvXXu0mjf;>j}D diff --git a/app/data/icons/new/smudge_detailed.png b/app/data/icons/new/smudge_detailed.png deleted file mode 100644 index c5fe1ba47a824fc761ad9c2aba6d08f7c0c5b342..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1250 zcmV<81ReW{P)TKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600Ul0L_t(YiJg>9j8jz<$N%@-``*lSzFHOp6$Z?xn1KxmAd;BT8e^h~ zabw(I*tu{sAqF&=gha5>n7C+wXjqU)svFB%kIq7?n! z(b~L{!EhN!r*mll-v9^&`}(K;H^2&qh={C?R-@M~7VilN0cn~@+PCunwg>zA_uijJ z4H($j+1~VkH~=s+iiHCF?M=8f2yYHEBWc_I%D3_gw!)#+ zot?fN%v=&@1`AO-%M~;8fAW%cYOF2*fDcX65ZZOYM1a=N%s^#VU}Xq1<90r;leT@n zCcp}ZmbbV0-l1|^SZin^NUgyDSZlD>fDjJ=r*2M#XRG7L}|R@qW1 zoEaHQq!t#wstaJJ(m(%o<(gjgb3kv+n7$BQHf_ajpxn z^|O7KCnu&3b#<)(DTT{duHvbuyGs=lL6kut2?&A6cLQ%lV)5$_>v+z=)U8|lbNK>N zsWbwC0F+_et)3x}n79dL8n90Nc4GgDb3Z&vLwW(Cf8LnXGk+)M=l*>o`0|T_h(JQ1 z=(>ph@+;PD+JN!#$^784XB!zZJ7qN@ut|IE5y zfBXHLkyt!cEx*>Fp{M8Bt(!JK2OI6);EOL0Y&&@Nk8rKR>H=(7zwVQzk3UvwBQq|HUP$M1xxHRF53j0kuw~;K zqc_<5t|(8bTP$K=;K&D&Sln#{P}RZOwLR}ITd`aL00S5s8O=J5bD$A%z{5cx;NM)S z-kzP~pME_0b|e;GY!pz{!O+m@ZCY!;VVcINlhNG=hc6v%R2%^A05COWCa?)J7XSbN M07*qoM6N<$fTKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600QbsL_t(YiJg^QNK;`L$Ny&^Zk|oubdZbkBR>!A8rIp%b8Q~O|Fy5rf7o^??hX`bz@ z=k9sm=l%W8`@H8phd`cTH5oMk`T$4)yaezc8CZkL!5<1Jv%jyII!Javaq7e?oz_RQU0M~VTLoz)l*bm|gg1}>y zO7RaI07y~_^-5gsFq44 zJ$&E*P)JG;jm4VNc}@XA5D%24#WG&>KM{^ZB)kMxlks#>v8+=h67q&`Zf+qIiA?bl z05I@vO*#T=>+wi5I-gTuH5r>oQra#M3XsY};PVG2@4gz!EXlTmt1>x>*|`PK+cYou zl6M8}C9m=CF-}1=7IU*H78JFO!a^wkAel^Zm4l`eunb!*eYVddyaeG$wBPP<#vAHu z006Z*#4Q`(c6+&Vh3Wpg;6!Qp%d;I-NA%6d(* z3c9q7N7`DOThqDEAAoIo=Azl|vgCTObYyK&T}}19`r2ce_SmLpT=~GW3c9q7C7Pz= zm(sb \ 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 343986314..000000000 --- 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 2cc465649..000000000 --- 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 2451c4f20..000000000 --- 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 28ac13c64..000000000 --- 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 3de522724..000000000 --- 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 a99b2392a..000000000 --- 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 fdf95dff9..000000000 --- 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 4a79d4bc7..000000000 --- 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 f981f4d05..000000000 --- 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 24735cd0a..000000000 --- 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 ae223140c..000000000 --- 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 16a19438a..000000000 --- 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 b1dc80d36..000000000 --- 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 3c320cfc4..000000000 --- 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 c727549f2..000000000 --- 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 07f79b30f..000000000 --- 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 be8238920..000000000 --- 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 a2588d50d..000000000 --- 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 931fcb1ab..000000000 --- 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 3f611b6ac..000000000 --- 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 2e3281686..000000000 --- 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 d93ec2865..000000000 --- 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 172a92c75..000000000 --- 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 4ef010bbb..000000000 --- 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 6fb77c629..000000000 --- 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 070e36d85..000000000 --- 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 f3ef881348d14906439a2f427e8ffc54575dd256..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1318 zcmV+>1=;$EP)TKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600W{)L_t(YiM5u`YaC@5$3O4<+{tdz)Q$PkkhZC|H5f^$J(w85gNO?C z)`}o_@X)iTJ$RQa{sSKLte{>(Pd%vQAOvYBRb!x~v_uk`Xlk>wyV;rdeIF0W4AV{8 zrUzem8Rli?`-*i|SdP-}i8>R`X_NX1*LA9{vyzrIc}9SA}8dmP#cr z2m&{bW3#=zownO;+v#*vnx=u)`oYG=#%EWqT=}}F=j^nvE zdfm*;%_Tr^v^Qd;+D9o2`1aa$wi`Qqba9%i3pYtpO@CjR!RPvL_nOTZvwau`SZiCb zyTMbZkD;yQVWZ81MvMD@KjhxpCP}IhE1aJ=k@tbMc3+7O>H~~1Eu~byh*0S%arVRz z+6bN0aQ5Ud(^IGEEk|5)ITz>lk<(??- zfozD#;T&Xu4B|LWesvrN&vWwb1@gcXkwfo6qnJh3+G3q^!Ax94FvdVv4m}Z&1zB+x z*D~PVm(y%Ep({xr3lNb;QKT3fMNlRV&~CR6@4y&yb75gYvd}BWa#1`3uB@yuKR+J> zx4N|+`-f1g)m{QV@I3Ei7>38AC>pI)DkEVShRtSkv(;*?w_2^$BuQ4SwM)SF^?H3@ cn*Q(OKeRC9e_-W~8vpTKTzreu3z|A1cV8URXm|R@o7!csY z00epYd8rH-;M+9@Mi6~j7i1QuJYK++Tu@X3r2hc1QA%oYG6Msn0gzo%UQz%QXIuhg zizH=(**ie&qL3hGAo~oE&6f^g-vP0cAnZ3Fb`j7MKsLxsk#sN{ zvX{X*zo4=xGd-h3!N|bCK*2F3KPgoqxU#q;HMdy7GcP&6s35;6u_QGGs21WrFyL_Z zbX5R`7t9|V49*Om46Y0c3_%R;3~*t}0VqDe3`RH>fr}x5QfM3y-AMod010qNS#tmYE+YT{ zE+YYWr9XB600TNnL_t(YiM^H2ZyQAv$3MF}yI#jQX?`e8-70dEk_M@?29X0IrwFJx zmji#n9{3ZGkhoX<2aME1uN;a{3m0y*Rj-YRP=Z<-349=_UEA?`y*ocR#NIj^J83RF z>1k(nX5RO`@0<6=VvJO))mh*@;2bbDqy}habSnsg#wa^h#;6N?>v`UnzVEN*ayiR& zU0m12aU6sY7-Nu9qLd;@5~P%LyImbc(Uz3*9&jfJ0yD5FGJxMzDwS^`*ZCMyVN{N&* zb);6S0sJ~_FbmL*^C;q~HKr>sI#6$Oy6$MlpAP ze8S>|X?}kE2L<2dgOw7Wtt}tfr?q~aJ!@wp7zOa;*$bMTh^N2r^6AeWm#~+x`DYEs zIo$3zHohnNR5j53>IP1Y0Aox?YYl)a91P$IhmEzXY0>>#Z?k^AOnG6N<gxDL=nY`dMl*f3;~OrE4W;m1{h1xxCqioKId_5`2rReoo<(v#d&I1n@kmQT)8xj zaBP$^XQt@LkD18=>y@RnJ$&);Dh7?}w+v&(1Ds0HKn4jm#vp`!tZknKU}UjnfwJSi zQ>R~3#XN`?|VgaaDtJi^>z~v!b9NX1VM1j c`~OA$1M-RIBoF=WI{*Lx07*qoM6N<$f`(ZjMgRZ+ diff --git a/app/data/icons/next.png b/app/data/icons/next.png deleted file mode 100644 index 625adbb2c5a1f5ba913202ac4ec405af40c53178..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 534 zcmV+x0_pvUP)Px$AW%$HMc=c7|NrU!$c+F0+{~wb|Nq)}Uqb)??f?J#fMrMf(4yM1hq8`m{>O~} z|I*5(eYlfr|Mb6@c~_xXz_^lW#-DWGw~PP(+v>uXdSFBU z_r=nvd;kCSp@ChEZ%_aE$BJ!D+O2$vZB50YdH?_6*s+NJ%8>utruxW|v5#v1|IeO% zS=X_M=D3FZ(4hb9xr1j(w2)~3|JaUjP`{aPmX?+P00960|22*9%K!iX0d!JMQvg8b z*k%9#00(qQO+^RU0Ur`F3#tEe*#H0lgh@m}R2b7^V1R%)1~d?dE)pjnhmZpU2Q`>T z93wk3s}Bd42n$psj$Mq=o>@Z0mjx;k$IQs5>Ey1%&F`%T7Kvkx3io4TGSSkH6y^bm z#Hq?z*b2m`2b*$oLR6TCh#N)=*+fXOLCvspRq${#Fwldb&7C%s_GDPmsB*+% zQr3iGH&X$@Bh7|W^h*Dza~e)qBDG@1Hz|f0w`?w-32O}nYGm+q^>bP0l+XkK`C2Yx diff --git a/app/data/icons/onion-red.png b/app/data/icons/onion-red.png deleted file mode 100644 index c05086a945755638761924327926c9b5a48b99ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>ldb&7gLC6U0WXFVpY$@NIbLZ4H8Oa*`njxgN@xNAKFcow diff --git a/app/data/icons/onionNext.png b/app/data/icons/onionNext.png deleted file mode 100644 index 45bb9d0136438f503207ad1173a0b8c1bbb11d25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 665 zcmV;K0%rY*P)@*gQNuntXixhd@k#0Aj|bn4O(pK~_y`idRIg-Jf5-elz{~uIK3J z9IK$9`0K`vYpVeQhzU(StCWITt&Ml+%&ew)yuW{Z<@@*bjnTAevnAWwdLA(`G5rH6 z0tg@$m}((0Syu^p<=lXzQr7?f|1oTv*J|Wo@4)O)Qu$p&L+dND$p8Ta)Wj(*uddc; z?H(p?VC(hm(E8c(eC&)u)wNAOIM_M;eWARrT-q0tg_0n7H`_-vKp%-O4X0@*Ak(53+*)4F4I?^e_Sh5YX!XaO)Wv@B=0$ za9}e61P~L@D1=%+GO4-h~sz{r6I&p&8rJ$rPAg_ZH|V+&K0%PUtd z7nwhQ7W=(>cN0ODfz$y65DNnnLeuvzA6XvUxbRCuP4)PnKfit?CB@3#x^;8et5+|U zAiDw}fLMSb&h+=sAI2?<`Z%84x%$Y`-2BR-MRP@$ELrgD{{8#0Ks9gCoemH{4AP34 z%aycEmU41($|x!-F0iz;*(M<&5sEML00ImExvr>9uMtZX00000NkvXXu0mjfK7}an diff --git a/app/data/icons/onionPrev.png b/app/data/icons/onionPrev.png deleted file mode 100644 index 09f529e55c447cdc7a2b244d97d4ec3f427dcd2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 670 zcmV;P0%84$P)jH8eCj6B3eBpFe-5_5J(zN8+-ovpINpGXVmK35#Mz85#LRFE8Ku3m2~BjgL=# z`~CNSCj&d5d3l|S*#v~dKL7*}3$kh{K0x$l6!Ut;MG zbtAhgFm(U{1ha`tO--XEA|keB<;rzm<=e5&o6AA1qdLJCQc_OmybXVKS2UN|1t># zr4{|c6F>|A0mOvrN(KfNW)=oQ03d+Csu>yoGr|P^VO7S6!UG5(CI&J9Kmaje7()aA z2q2IqCLs8aU)g^Yg8>2v>^vqWW`qD%2cwz^5I`78{{18DJb(Zq%TE9S1ojgnBmO}A z{~y&)009J!NoHmwF-V+&J@M=3Pv%FD9x=0V^1J}??_55~_4ehHZvX+rg5kXXAd?vw zUcGq6{`u>-&q9(4H~;+k#jtErwZMaGXLr1N`LqilfLI_w&H@jf-#>pb-??>@iAzA_ zmavrghwEn!@oZl@`P-Z4_Y;5q{COXw1|Wb~U?KSb|38Ka6M94+Ji7nYSz77V@1Nfp zm-bf(Jh**f!@K8CCnASCKmdU>F~5KRjx#DMROrr~n~hQ``fXQE9pKuva@yx-4<5!b zF#LFg9xwm_1VVbEqGBMEz%k4tAaq7pTq+x1Q~(4R0C6m|ip&yMQ~&?~07*qoM6N<$ Ef~q4DNB{r; diff --git a/app/data/icons/open.png b/app/data/icons/open.png deleted file mode 100644 index a79982ea1f3d1b99654c2a73238f21d716cfa0e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1356 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 diff --git a/app/data/icons/overlayGoldenRatio.png b/app/data/icons/overlayGoldenRatio.png deleted file mode 100644 index a207587b29d5cf22882cbcf0b968178ea0f3566f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 712 zcmV;(0yq7MP)+h*4srjD(t`G0I@`=9wRA z-gvz?lO|hbt0=MYYr$W@R#_`Wq-14bO>CH+^JdgY=G^YNpZmS%obEjr9O0CznN_`@ zr87ooJm8*-%)4uzsh|xP9keK_sgF!g2PtbGcM>gjVV9azmV{4*y@p&+_}12=MSih4QRFY1jc{lb98JRR#4nEiVl`=K!t!!F zS}Lpl+{b>2GnSs6k+oB1J0>B<3E*auF-94nk0J2``0&#sJIdtu7V@W9kz7^0P7Z@* z5+ups6*Vi>W3t9YkI6`pv-|(IuV%d8U##v7K=qSly>&s&7UVZA>+{gE@<&kl2p8#6 z{>lLizKgzD(meanxDIERB`vxG=Nr)apeu$_kSb+5;&FIAfrbb)UqjtOv1dQ$9N$BD zO}^?1cK4uL;u{yg0ahJn-dcH-SO5S332;bRa{vHDZ~y>AZ~+Og7Uci{0PRUcK~z|U z?Uk_&!Y~j;Um`jrdM026T1H@q6d8hwAt)&6SRySI5+!1Qw271+s-O@ObB^tE!*5UW zdpe)bNnT`6ggI=E0OUnBsoqah)nzJ$`%Y8U<>PvrM-gb@z(QT13kOgY=)+Y7hTv)f zV{jD#6Fj)UFL+P^2|Sp96b_I8;#C|X(N=Mrne^iT^Jmjkb$QyJmjLVCN_?i54s`C^ zI?e(%1Ls}9749V913p>67aab3z+w+b-uY#J$5bDRr~@wV+*!cqz*!4~h8qPm;G+Us u@Ie7h_?Lh-{6ioH{4Ed*4v@x#1H1rMuEEWUvi7C`00001QKO=&=a>_gd!t@6XTA>Wy+|*;F`mTePoeEA0`#zl&vkW%=vmNqiq> zFuAWNE6goof9{ZesYB+@g1F_C4^u2YD1Y#lh?ijSSNtb3;XrQUx0W*g2Mkj!3U&rM z@3g!8M(C`R(gOK)R)SA;v^O>xX@C85YDQM7y0(Ml!D;m>uYay7-r3Cm?yOC7^Es}$ PARl?U`njxgN@xNA04H;! diff --git a/app/data/icons/overlayPerspective2.png b/app/data/icons/overlayPerspective2.png deleted file mode 100644 index 1ee0bb396ebb1ec44b1b0fae52929c841d37272f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP-k1U@Im-%jsHlUDfW=KSdbAE1aYF-JD%fR4Vl$uzQ znxasiS(2gP?&%wlqL<1J6u;!@;uvCadg&!YKW9S$mJ826h$>!ETw%ea;Bs8?#e|?@ zHJ2)lj2AoHZ*gS35D4uOu|Kk`K|xaHIs39rWohNdvOo9!l++XyKf?KTB2V{e{W`Zj z$#)#|#g_`6x2{;Q^Tf`z(oPR@<*Vn+c~&#;!NhM7f{)uO&c9w@pb)qEQR-}qMYb_v zRaVKnP9N0$to>`UJ&yzrr}HoW`Qf$p?F~=lIZl5S&ssep^Dx(v~<;3v!D8z!ta>*x;#6%)_ETWdE3*~&t;ucLK6TY CG>1C? diff --git a/app/data/icons/overlayPerspective3.png b/app/data/icons/overlayPerspective3.png deleted file mode 100644 index e06848d05a6c70d6a97ac2c4bbd2389a24901513..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP-k1U_O5dW{wKsg54%#er@=ltB<)VvZPmw~~#C^fMp zHASI3vm`^o-P1Q9MK6^dD1OJ&#WBR<^wdck`4}Aq+V)$jC}~ZtU|Y2JkatJW;tw8Q z7AXg6>}?bmuw|VaR8(Z$ReXHcUEcbw*8lB4pFg7~QgLJfml|tv!;mazPac_zr>jyg-a(-r%V>r>V43% zuYW3QjMM>9tH<%&Pu^WVv$vM{*Mv6=N6LQ42jyMvdOl}|yVDVS{e^tpdo&*WnW-b^ zThJ*badGy7zTI`A8%y??UER#g`R26ek6ju8Uw5?4ySwu8{e4B2|5#*K1&8=vt~Un- NfTydU%Q~loCIH{EkaGY4 diff --git a/app/data/icons/overlaySafe.png b/app/data/icons/overlaySafe.png deleted file mode 100644 index d0563e97cbc75b110dec892a5b0211ee25982d11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 715 zcmV;+0yO=JP)+h*4srjD(t`G0I@`=9wRA z-gvz?lO|hbt0=MYYr$W@R#_`Wq-14bO>CH+^JdgY=G^YNpZmS%obEjr9O0CznN_`@ zr87ooJm8*-%)4uzsh|xP9keK_sgF!g2PtbGcM>gjVV9azmV{4*y@p&+_}12=MSih4QRFY1jc{lb98JRR#4nEiVl`=K!t!!F zS}Lpl+{b>2GnSs6k+oB1J0>B<3E*auF-94nk0J2``0&#sJIdtu7V@W9kz7^0P7Z@* z5+ups6*Vi>W3t9YkI6`pv-|(IuV%d8U##v7K=qSly>&s&7UVZA>+{gE@<&kl2p8#6 z{>lLizKgzD(meanxDIERB`vxG=Nr)apeu$_kSb+5;&FIAfrbb)UqjtOv1dQ$9N$BD zO}^?1cK4uL;u{yg0ahJn-dcH-SO5S332;bRa{vHDZ~y>AZ~+Og7Uci{0PsmfK~z|U z#g@Gd!Y~kpU!ru8(nHG{j6lNz%t6aU8G^L5j9|BxT(}ICCCg5Nf9~v8Uh{o=caq(S zWnR%JMrql%gp1F29PHqHy;l`@zn&w2saXR6CIA@xsaR$Enq{h1BC#C=Z002ovPDHLkV1o1RLRA0& diff --git a/app/data/icons/overlayThirds.png b/app/data/icons/overlayThirds.png deleted file mode 100644 index ccf9d026de5c88c487fb19bef62d2c93b65c90cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 709 zcmV;$0y_PPP)Z8l!w|?p*Vs zX2$K_E6G;bPElgxYr$W@R#_`Wq-14bO>CI1_nN7hWX|cl@6+#`_wAhD0VSN&Qf9di zQfbQwjRxJbbMx-X7Zlp4riX4-GxedVsR>Gs&wI+8eCu_3l|J|Hs9lPh8kAKD4{Ew$ z36BX+tXaClJHpLcLR}I*6ZRN#LE$?`kL38(;dqX}9X7(DVNe={-SHxh6tR{tQo{0b zI#S71Eq@>9C3VsC%(UcoS*@6a7$u0CaYh)X4?hDE`|uJFhy3E*|MkA#`bk!#R+Ff+ zj=?f<5?FXdU6t-p$uZHRGLx)3{{QvWjP(X`-Rpu-@od}golv5JewMI%n_*o`u$qLwF1;j-hw3qxBRXfJS;MI<4X;N+8LyY5aAP_a@o- z$*QJ(;K*ga1E6YJ*Rr5}rXRZ8>r}y*e(1Kh`zDEig=bKx3%tVtR0YOxRe@h{HGwI( zihv8QBH)4pWPr1X)=sh_vN3ZN>s!V4&ChjN03422=X%i_m>alN1w!Cr0-k3iwMP6&xT>2?uxr!ab9CJaA?T00000NkvXXu0mjfLLxv? diff --git a/app/data/icons/paste.png b/app/data/icons/paste.png deleted file mode 100644 index 5e60ba794cf56c68f125298ef871e7d8768164e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1100 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 diff --git a/app/data/icons/prefs-shortcuts.png b/app/data/icons/prefs-shortcuts.png deleted file mode 100644 index 1fd7bd013530c25f25bbe3f28ec1925682cdf46d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3101 zcmV+&4C3>NP)001-y1^@s6!aBwN000Z*Nkl7E&-8z}>5 zR`LAH*6vuSqI#b>hU2df&%6XBKtqR+a6hZOW~|QEpT! z`Ks>y?s@(0@BF?9;$w7l)Y-LbmpwBxBeU78Tv}QZg+f6XW9mOaYmJH&Q52zc?K=uX zLak9@M5ID6;0Jzm_V;H)Kmj_w+-!A*0H{{0+da?wHSikiIHEA5>ihV$GJ2X+zYnNRI_`c8No(c!O@t7`-QC^K1Gl7l-+lL8!mw={$8qTG?d7@Wp4(K$#KZ)> zy}gW#jF3*JDV0i0OiXa#zyZ?f^oALBHk*|IFEJX)2bIOF~Dep<9H128YFw;CWS&F4GdJP)#~>#JkJB*^y$-_J9myS3^s+a zyqxE~_uk``S6<=h(W8_~CGz<^FTL~2=t=V6TD z^5x42A-Htu5~)-Q-}ecl2;DTpYBw+mOsR+<42Ysg0#^J<7$%CSlq-0i$Nl^FnVp@b zR4TD|?_RpPx(LG%%eLEMpcJlCi$^cN{4yVZ{4s|P9U_%V;dx%X1O=g5Kf|u)AN5O& z(NwAx02US&xO(*}Uw-)|uInPDL<)g0221Ovd0S_Qc>No~AVjH1G>=!EA%vl`s}q2? z-+r6v>1pQX<^cHQlTQ$awi%$2vUYl8WQ0#Y{gfaG*tv5jLWtyt8)4Y36Hj9Vbad7l z^442#F*`fUz`y{HN{rgm_Rg5w5MFQ7#THjWtbpa@?@xuVES2v{2 zuw^a8%+Jp=Iy%bj+qXG*@E`*N1K73`H={j9dRrpr%rMD^l4su?KM)VR3rCkld3-Bg5_9v zogSx7ovLjxRkSh-Y3VR4In_FS!!Sfgdc}x07(JameDJ{s3=a<{om0Pdettfg`t2~9 z+hL5s^E?h8KFr|YAWA889NgE-u<5(GlA+}|03S(tE%H=Y-Tn@)|(Mr|c@atqG zos~d!E#yT}L_VL#c5GbXvet`01GcD5-{u)-v?27vWaN?JAL!YIOZ9Be7q&5&B_l{r{^GI@@4unz)5Mk*qR0;FZp)7OVmrk1E6;$qmJt265QET^_XsQs%} zs*F!ep|xrm^#_3=RivnteY7$34-8=2cJeA0Le@q^$d<~0j4KnCu&`|#+p@82i$bZu z*w`4O-~1=G?O<7UJsLa^3_D-QN2PM{Vt#RH#AuyV7L?LxQ$?wON~MC7t)TigLWtTN zH^A)w%<#uQj#4U?Nu^U2v|f(4U%}$i!eq5n{@GS$*rz@?6&`xw(62I?pZpuh;YzuT zHnmr77zRX2;Rhiq3<-jOp#JUq_?k-dHU8c+d> zXFvS?Z+`KM=l^9ZGen%cZaZ@1$j?)$)blrP+}vI&71KcwxJoIhwHEbkS1E-y8m$dV zMQBx1O0|blD6Nn}Xsz{fHk-W$Ng38hK52ZWigk_-Hq$n9WB+zjU53{1eSqCpacX%LqlRKGi<=b za`M-8mDH}Bj8ruFhkyUWZ+`o`-)7^{4yLB2`k#90sejnY3~O|B)Y-dt@1{!DN~O9p zlga$dah(4iA0NLvG&E!#J9g}!nj5hd8LK4g^z<}yfBF*=od{6JaazP@y8^VCVcTxP zSc~(cV$96UB>C&)D#9%VnY{Y9w)M$w}Rilj)wC&}GljpmAx$7|3KYJoeaQiQ!3ZDEQGaR!LT+LMqiVV`F2SIB|kK zd-hN+m+^g{{rmSPnn|=NK!vQG@jNeyA<08;l4RX%hP{@YR6waz;)y4opj0X)0>O1% zIyyVR2rNssB|z0$^S=Jfl1&l0o6T6Y4R9=ruFg(e*TuGN78e&O7K>=D)Npt z*th^~@o%hMWHP!IM}DYe{cADgN;&B{9)GiaCj$cm+`M^{d_K?I+#FY~T%opmFdLSW zD;emx?)|;nvdv-C8>*BdpU<$$!{gd6i_~MJ4Jb9AY*;(%1z030Q zGRKb}$8}wV5N!!ikXVjII-UOB@!t$aJx(nxE^%pknhO^$P%4#v5Mz~O6_%jG?O^}@ z{b;Q@fBrldFJ1)T_19mgzrQcZTYjhjMM5f-;+bcj;iHc}S}W6OTjbUm2M-?P^Upu$ z_`e?K(xpq}ayd?(JjrLDeYRndyQXBdWbOYl^!NAk>Z`AkN~N$Yi@MyXzd_p(pqAy3 zPNz9@=1gq9)uxMF%W~-J>*LIsGwj{FH&OON2(;G8l5AVD8e=Gz%Vck5Np+=gy?FT< zgA@YCvf37)0Qr2L>;HS5&<~N8+;ov!XI#5}4bSng?Ap>4Qlg@0Lz2~ATe3RIG^|#u zs7T|wjeV8tx@ZYbTLRQcTwgw)$FW`9dS^8W(9L2jE-uy>E8Ahqbu%7H)`ybyp=5n1 zSszN)hm!T7WPK=EA4=B04x`>#GA_%efZn*{yf)-y9a4w|WeErYXDc&AQ$MEN#bL@s)vvOx00000NkvXXu0mjfPUi__ diff --git a/app/data/icons/prefspencil.png b/app/data/icons/prefspencil.png deleted file mode 100644 index 84d69a7041c178bc7cce8fefe3da8fa60ecf0180..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6417 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 diff --git a/app/data/icons/prev.png b/app/data/icons/prev.png deleted file mode 100644 index e3a64d5276694ab0f26a06b7ce55e0f9efba4858..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 534 zcmV+x0_pvUP)Px$AW%$HMc=c7|NrU!$c+F0+{~wb|Nq)}Uqb)??f?J#fMrMf(4yM1hq8`m{>O~} z|I*5(eYlfr|Mb6@c~_xXz_^lW#-DWGw~PP(+v>uXdSFBU z_r=nvd;kCSp@ChEZ%_aE$BJ!D+O2$vZB50YdH?_6*s+NJ%8>utruxW|v5#v1|IeO% zS=X_M=D3FZ(4hb9xr1j(w2)~3|JaUjP`{aPmX?+P00960|22*9%K!iX0d!JMQvg8b z*k%9#00(qQO+^RU0Ur`E3iZ_xi~s-tgh@m}R2b7^V1R-+22>D-CJ-l&DiEjU5C;KJ zK8pw!hYu?=J7XM_&*G~h!EDbc#ts&U)AQ!%)^T^zUTN9t>tFfsXsN3lXw zaB`XktH%h~TF9wF&0v#?un~$j6b~_n*}|r4X5i+b;A#hRO`M|_iaUTJl7#$%>03Ae Y0BWongxvb}p#T5?07*qoM6N<$f-_McB>(^b diff --git a/app/data/icons/redo.png b/app/data/icons/redo.png deleted file mode 100644 index 8a4c817d010ad9f43df6bb64300422895ae3ab5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 739 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 \ 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 000000000..4d9464ee4 --- /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 000000000..06b7b58ca --- /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 000000000..1bc0aa42d --- /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 000000000..6bf0efd27 --- /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 000000000..af430cbaa --- /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 000000000..8407d60b4 --- /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 000000000..865a31e31 --- /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 000000000..196085534 --- /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 000000000..06203759a --- /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 000000000..f8af0aa11 --- /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 000000000..cebd5b7a7 --- /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 000000000..322a746ad --- /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 000000000..c1d1c2e1a --- /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 000000000..cf629212c --- /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 000000000..34ece24a7 --- /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 000000000..b03b5a074 --- /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 000000000..730edb4db --- /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 000000000..181dfed2e --- /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 000000000..4e5ee7f05 --- /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 000000000..478c627a2 --- /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 000000000..622a54fba --- /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 000000000..ee48daa36 --- /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 000000000..d97b9e481 --- /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 000000000..f55476791 --- /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 000000000..a344bc221 --- /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 000000000..77635a80f --- /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 000000000..e0d0b29ba --- /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 000000000..5c0e59003 --- /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 000000000..93a7b3129 --- /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 000000000..4744b3e86 --- /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 000000000..b0b9338c9 --- /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 000000000..9251aa6d2 --- /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 000000000..fc5599153 --- /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 000000000..b4e82b5bc --- /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 000000000..e48c0e7ab --- /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 000000000..8edf749a6 --- /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 000000000..59987335b --- /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 000000000..232780807 --- /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 000000000..2af800b05 --- /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 000000000..2dc1faf2a --- /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 000000000..f93af3709 --- /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 000000000..d1a238695 --- /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 000000000..2957338e7 --- /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 000000000..fd7c7d924 --- /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 000000000..122f4a3a6 --- /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 000000000..d6c1ea233 --- /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 000000000..c49338945 --- /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 000000000..3503240e2 --- /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 000000000..a00f27599 --- /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 000000000..d898e6ac5 --- /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 000000000..fcbe1a8ca --- /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 000000000..56977b6d1 --- /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 000000000..6aa9a06f5 --- /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 000000000..f6d0a9ebf --- /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 000000000..b0af39098 --- /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 000000000..9627b4b63 --- /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 000000000..054a684f4 --- /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 000000000..ac74f3c65 --- /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 000000000..853cc3823 --- /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 000000000..bc8f95477 --- /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 000000000..e9f2ab3f0 --- /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 000000000..808c1e782 --- /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 000000000..2071aa06b --- /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 000000000..fbb384c16 --- /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 000000000..b593c30d5 --- /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 000000000..c4945557a --- /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 000000000..59da89625 --- /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 000000000..f25e2d0c1 --- /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 000000000..4527e8fe5 --- /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 6d8655d1a2bea1a948b1fedcb1e2853ac739dbf2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i zkbPrI-P<{wcmHqnWMmN#V#N2Yt>v)|$8RYgKRu*ffn5Idgj}&@%Q$tu8`sY@>w&!J^#O^^>Ubzp|SAfbwH8KgGbiwum2~o d5+u*Vu=;O&jG^KCRX{H@c)I$ztaD0e0s!P%e|`V} diff --git a/app/data/icons/undo.png b/app/data/icons/undo.png deleted file mode 100644 index a63c3ef998819894d4bcccde518278a164d13d13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 681 zcmV;a0#^NrP)pO#-j2y#_8#e;}y8L~}u!G?Ou?7PK5F?oE02<1~ zuwU6rSwL1>7DLg;*B==!t+>SS_uJn@h6;uS#25?^K#UBH5JOq~SOhrqIB+Wb{qi@% z&&59(7``ziGSm|`93X%g8LAoHGjK2nFi1c}*nv0%NyQ}~z6@o5068Cs|1%gewBipI zfB<4)_`?v$@CC@a0~Ptp(9d8D^o2N-{||_70*zz>x|9)YGy_8?9z%;DApj6Sj2IHR zzy#(9Oa;bBV%vfE3ed2BK$&S!!?A&Eh613W{Xhq700a77b;0>syV z_#P1dgo^P2u?RGM-314kBhcCl00D&E;AmiEGBDg{&;!zz1fu^J5T6A{GAM`~8J0rA zA0U7jacYPLduu&|5HK<{fh-vyW#3Ea{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>p^K@|xskoK&=Jo4j1wIBAfydw9 z-|r0J5^&I&Z(pw#r0Kx0@z3w?{T@ph7+soz8W@s({{F7tqN=UUts-w<$1~kon2|$a zS-ruB4-cI?Oj@`D*+p8k88{S<{r>)5`Szj}D>%{@dor>JT#gSZcz#Z{gJnXa8moW< z2fITcU}R&swRMk8>sznKK%X#py85}Sb4q9e0E?n$+W-In diff --git a/app/data/icons/unused/TitleBarNormalButton.png b/app/data/icons/unused/TitleBarNormalButton.png deleted file mode 100644 index d379c70a60a5f40dfd15a78d9783a904d9f67550..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#IhC#5QQ<|d}62BjvZR2H60wE-$B^mK6yskoI?^Z6MsvqVQnhrt4- z1tJa%hK3JV85oZ+u@szZ=a+ww!^p@85?;u$a3%93pv0Hw=jDAeI}|3ca43lF(0)*) zl)#Y4%p!0lFGXCznn|I7V~31FLxjRLhP$1a<^l{X0^GBXW-urmV2IeK>B4Td{1(u1 N22WQ%mvv4FO#p5YK`j6P diff --git a/app/data/icons/unused/TitleBarNormalButton1.png b/app/data/icons/unused/TitleBarNormalButton1.png deleted file mode 100644 index 7b73f6585e54c8204105b216cf5544c3cffc5dd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 318 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1SD^+kpz+qEa{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i<@@N{tuskoK&=Jo4j1z83bfydw9 z-#>YQO~65CzJ0x#v$O-l#y`Km^9P4GIWhS@Iod6LBBp`i&^lh>1)9tp3e)!2|4$12 z{^Mh^MRZu0n6IP*!@}EY`uhCgT!}U{KLjqDH83c>@#tVONo8PcIXbcY-JQnGp&=n6 zqN0sF5HT~8iP78h61kXI1QK&2Y8eF_BDQ2^WC&aVy5U9L?wX%Qv$)n6vIsaFaWj`< z;7~By%E%%h%pA#}&=B$M{eAy7pcP4fK0IWWyv4x8!_erH=5_8~>U>a$c)I$ztaD0e F0s!-aY+3*S diff --git a/app/data/icons/unused/TitleBarNormalButton2.png b/app/data/icons/unused/TitleBarNormalButton2.png deleted file mode 100644 index dbba6c93efc01478c579c2d4511727d326029416..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1SBVD?P>#3Ea{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8@N{tuskoK&=Jo4j1wIBAfydw9 z-|r0J5^&I&Z(pw#r0Kx0@z3w?{4@0x6&sg5I@&ERz~;cPQ2QuL0V@OJkx$>>$D8at z-Yc6?dc27?H9rJyhBPo7dd|=x62l18WbxqKTQaWft`Vo ajlt_roJDNe5_zE889ZJ6T-G@yGywqK7h)j* diff --git a/app/data/icons/unused/aqua.png b/app/data/icons/unused/aqua.png deleted file mode 100644 index 303e80f24ee7eab9b689d8d5fa7a8a63ddfcd963..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^EI=&G!2~2N9IQbSEa{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i=8@N{tuskoK2=lh4#`~Ux!l$8Ac z|3ANggU)yRziBBc4GawoNtHjp@-y==un3%;D@Y8aY}p zFdjK{;K&&UV+MtWh=2dne{o-C1)5=gf75}lKz-cS>`lx-zRa691$+(+8w)lWAG8GN zIoi8{9jK$iQV?Wn;zNf52BwbI$-D;{ITS>IDh>1g^c0wa=>Pxa^Ryir7#TFQl=N8k S?Jx(rox#)9&t;ucLK6U5Ghk}~ diff --git a/app/data/icons/unused/arrow.png b/app/data/icons/unused/arrow.png deleted file mode 100644 index ee926e95f435000c2a208b1da77a043c598ecb23..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 483 zcmV<90UZ8`P)-^a)2zk-6o{{sgO z5Y!A1Kuio!z{khO0MrC>GDCcPeEN|iM>a6v0RRC6*T})a!N9}A!|>(H7lv=&zJXle zf9&Yd<+wBg1Q1*!Gcz*-D=RC*`}gk|K79DV@cZ{~25TFeh~vkPW3?F|fKY5?XJ=>l z_3IbI-@kw13aqTG{7;@dxe#3=Kmfrtf;@cf+BJrM|Nen7GO)0)h(p&15I`(21|uUQ z!|T_t8CX~#LTqeo3|lsDe#Ohj_nwJ~=@q(0fB<4b=6w3}iDAy1Ij_yl&B0FIvUTe( zZf@>tW@ct~=o$e62p%sXAtC>d9Xoax#GX2J>MD@yK?&mQnKSpW#Unrf;f!E)b@l%s z&9$|)|DQj9z6V_%Ab@acoHAv~MxcNH%gD(5fARcz6S_P=0O3!MKpR0CpFDYT2we^! ZzyRoYRKUiQI1&H=002ovPDHLkV1gS%(HsB( diff --git a/app/data/icons/unused/brush0.png b/app/data/icons/unused/brush0.png deleted file mode 100644 index 03d1ecae212fb1c5d787c48db0b97711d04766f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 626 zcmV-&0*(ENP){!PTYG=wYVf_vUPF5BMfB<45)sV%D7o!+*^VU6vsH#>5fB<3u zxrkW2Wy_ZTxqhzyFHLXxUl8E>-&#TH|DHX2{)6NI0*Giswr}75Kf}ujY)ET-@P8Xc z>Hiznum2B|2M8dd4B4~q(Eox!H;5r|5JP6on)M%94L|^~5DJsY^EUtA^!ho&U(-N_ zZKwA$Y&&$EAvmvu!PwXsMIAr@G2)7~Gsh17Kd@&X!!}k4hJ~@d43(SqGd%nFg&~;# zAA`B6DT9frDI>CafB?ekqKyk@{y%&CFasAS2ZQF1mkb>{g}{b{i?A~o8|pHcn3yo4 z>j4NLjPTgBeE$EQlC=NtU%&kS{>`iZ9PDiWbv4xg`vyk*#}|$O0ffRixp&+DKxb>P z0S|6o`@em|>i?akng4|a`2Me3y9Td800D&JwbhH~Gt@M;Fq}Phm|?;!ZlDM+!=>Y^ z7$l`-8RX>T@kRz{7;_JKf|Di*mm}3WGSs?z)!2C6h;r(wG24M*ihMMeb1}SN2f>{S3 zfWR*D@mUH^1Fv2kXL$GSIs?PM`wad*z6`;kp$t~GHjKpNL4W`Q0QX&eGIPlYa{vGU M07*qoM6N<$f><*l=Kufz diff --git a/app/data/icons/unused/bucket.png b/app/data/icons/unused/bucket.png deleted file mode 100644 index b87ff2bf4704305650bb964aee3cda0b1c175253..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1908 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 diff --git a/app/data/icons/unused/bucket1.png b/app/data/icons/unused/bucket1.png deleted file mode 100644 index 8823ce8cb175dc3b8274ebdd518274b7048990e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1616 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ʳ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 R*{=UYz}3mavEH5|_&*DbyLA8n diff --git a/app/data/icons/unused/eraser.png b/app/data/icons/unused/eraser.png deleted file mode 100644 index c86abe0c3cf62d0822b6e24ca32f11e368bf4dc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1160 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 diff --git a/app/data/icons/unused/eraser2.png b/app/data/icons/unused/eraser2.png deleted file mode 100644 index fc7ec9a48da011c8f38cc7c2be56bcd1a4a8de13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 613 zcmeAS@N?(olHy`uVBq!ia0vp^@<1%X!3HFmR;x7vDb50q$YKTtZeb8+WSBKa0w~Cm z?&#~tz;NUBR~eC2K)ynKzw^oAW8hHe(mkH6$*joEz#?#SnsBCO9Ef#t+T=t{KM?EUw8J{J z-@i@$@WfdEevm+L0Z7pC^h90Op#1y$>-ojvi@u99F9n&TRx_>1;6#u@8bcJ$&@jVetBB-;M+_^L@~rNUPu)@V^_7l*v_eBblWdx1(FY;s53YNBXRKdP1?`?D7+0 z%(ZjbPG>PNw!~~}J{%tqAaF+UDx;T|7Y|Q)enV{+^MVK-CI-eMCk~uo;?J9L)Zlby zA;XDFw$6Z+e6JV+cJ>{;G<8Z87cy%LJR^449yQq!~)*DGy%pvgQu&X%Q~loCIE~j)p!5^ diff --git a/app/data/icons/unused/grid-b.png b/app/data/icons/unused/grid-b.png deleted file mode 100644 index f956c7a8ec58be944ee0a3eda534bcc21ab34569..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|oCO|{#S9GG!XV7ZFl&wkP>{XE z)7O>#E*G;ngJq4Z{$rp}iKmNWh{pNkHS5=>E3j^G(Aa*Yrpc@P?EDi44jfz^Q>-8KIssI20AY({UO#lFGm;eBCjsO7aNB{ta0001Y z?*IVM@BjcZ_W%GKbhgrL3jhED{7FPXRCwBAWIzB8AQptOe*m#mb#?U>78VwU#>Pey zApboOe}~dPkn}MC1Q4lwv;X}0!vNIJ0Q43E&_FO7Xb{7P4~xJU?_xw zou!HmSPIKd?7${yG>VX*-ft`FFPF<*BJ!nnH;4$T`VW0T>@Fe#GlR^8yQ8XLW?<&m zavuT21WE@WlexLM8MbZP#sG{Ruopm12hkubE-nrhXJ%$*P*zrExN_wR!z&a<{d$RfmIv<4RFc5!kPwKm@q}#0DD30Q3?VgZQ8*zIN>zm=6S?X#N2Z zK+K>B0OHU4_wV1p$H$ij3>OBV|G}C;^w+On!CnDj5c~f9`wVN>t_67qlocL<^aBJC z3sPQr26Qq5Fc2Apg@wVc0oe=+8(8>&3i_@%07*qoM6N<$f{FWuy8r+H diff --git a/app/data/icons/unused/house.png b/app/data/icons/unused/house.png deleted file mode 100644 index 00750a7621b092de5e8c702263b6847f9a496ef2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 185 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 Cr}JF^ diff --git a/app/data/icons/unused/move.png b/app/data/icons/unused/move.png deleted file mode 100644 index 474bdf21eeca804dd013081fc9a8cdf049bff476..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngd!3HEhbh*6;QY`6?zK#qG*KS<#k1zuAB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH1%?eKJQ45_%4lyHDw>w_l~i-6q| zt|R{&Z%9mMYG63j&gX2Je{d;-LIcOmU-I+cGBCFAZ*?|(^u^qP!BBCdXk&$%K=dQS z0#iZ4;uvCa`tGE?-cE@k$LhaV8NWGvXop0iqMFOhrK!Av zVk}#?vj62>E9mGvx$(-STgL@hZbtBl209D2d|_?PHDkGXJt69L=FV`vpnr_>-d5g! zUU}~Kyyp%_r!ueDo?!l<=rwPcpR;AN$^*lL2}kBjzWBb}+d}<+r?3OV2PPA9^SI^b zk|*tpoK|q+d&0x7jI);ExM1e@43iI+Za=nV6A&zB t-*)P4BbQ0!20jZ}zTKUxds(zr)-jsa#@to9uwDZc1D>vaF6*2UngE#n%G&?{ diff --git a/app/data/icons/unused/outlines3.png b/app/data/icons/unused/outlines3.png deleted file mode 100644 index 025bcd3094fe0789d31543f1a7cc35631727d7f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>BdAc};RNP8RQINmEuu;;1A#l-8 z%MLaUg)UQO7J;L)7@0bf&N48%Y)orlSQKFllwTvM(4cXRN5DZO8;>%>fK`$T2N)ik Wh@6bo*VG2u#Ng@b=d#Wzp$Pz@qd8Ro diff --git a/app/data/icons/unused/outlines4.png b/app/data/icons/unused/outlines4.png deleted file mode 100644 index 6b2bffb26c0d01ba00d1d1ef5eef0b91a53a5dc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 484 zcmV!HtzV(#>~Q$?QF@QE+K{zu-FU%2q3rtKYsmUh_7z_$?*R_W1N)% zgO8yu*4RNe1R#Lm23SRBy!`i{fj!;Hk|EI40GF3B3;_rrumRz@yD#ze& zq(dk>pcn!WK#Wm#&WkTR`w*ikDaOzf>B~R@_zud&00G3Pt)%;3MnsfhMnVV!8Q?oW afB^tLc98)Lj$nTP0000xcViv?f>_+SPb&=A6U2GAShvs@`hmW$4Tp}>bt(%*2#so#Xsyjy`53o=fWQ0BR&Tf+p99(WsqlD zb|ZO?fqubJ&BS@hman{TC4EzR5%9RfOt9dv<@cq`*HmRw1nnOrpPax{=Is27*@kid WWKrhWl#BczXL`E&xvXWFU8GbZ8()Nlj2>E@cM*00TWqL_t(|+U=Q5Y)wHF z$A7nkR?#StN|81pKJp?&NlB%x_)4)tSd;jO#DcjGC+#uNF25D576KQ5FMyw6djXsTs+4!_4(N9T_yqI=oxnBV z5Ks}u#EXGT@)AF!))g{ z1Xhmw1Lpuuz$3-(BQRMT>@LO0Z<$P{Y67l@2w<$H2waoxGHrlQ6bEN>5U!a5063!9 zHkkH+<+66uX#hAK@eS{l(m%-rV25ISMe>0{&TQYA0MshRcMIUiTVm)N0(wjUOvb~K z6h&E6frEzrTfi@qgF(aQ(VN4GWO58{2RaNkpVK8mV`a+spIQvOJ3xBcAN5pty!--9 zgIncfdtd|Lo59hn0-N}EU_Y=4cxigDXxurUP89*7B?>B$y1;ET$v{#}36WAQV2uP_#v2C&$c)JCJV;04Qd;nOZ z*d}cNR7v$j%dmQZQ;dEem?s5^Qb{?t09W$>z$OZ|+CZq2#K_N5%O-$Y#lF)f748rS zeFb>-CxQEh{&kWeTB!`UDaS}y|Nby=Ig1gHieQtE_MlV~Qp_Crhe_W94x^Xb!vKI} zfF?hUHYKEqB4PtjFFWpBMn8_0Gg>holI!Pyq}DxhmF@(71gHlzE16)Ig`axhp`pKR zq9Uo2Q~2`pY;Tm8!^wMjunz>lk6C1TV*&UvU7ns7XPmAz18n3#pV7tWX^e9 z;PRg5>hH%qa)#4)wPwG0AeZ~)V@qz#!anOe3&I>5wb(LkoW$d2-Z^`d^WMfuzqd!U zOMWS6(2Pki^`CKEv+Uv#Rk4lJpZ&@&3+v%JtD>=?<>`lqtlNZl%dc%Wo^eF`nt|Zv z_76hGD~^eYEi(NOD`oPbQ_Adv*u6t(%lHhs`&BA6d5Y=04%d9{l N;OXk;vd$@?2><~-i%|do diff --git a/app/data/icons/unused/overlaySafe.png b/app/data/icons/unused/overlaySafe.png deleted file mode 100644 index 3ed1c7facb2fa907ebd9e42d619793ca8e12a38d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 898 zcmV-|1AY97P)WFU8GbZ8()Nlj2>E@cM*00QPoL_t(|+U=M-XjDNI zhCkz~goue71RsqSK2{b*&_;}v>jPg1L9kI9m6%2ZQNbdGNJJO4QA{hw#Wa$_BEcpS zB^DYi)Jm4b#3B(Npb;gk%@pf0vzfU&cf6SM!!|qrIcMgdb7#&O5C{Z}|2y!rvG$H|k>QBJl44&4I)|Zoyd&79ibzO}=3%C(~A#YH>2%J%;whx>%#M!9E+YIBGxCO{r97vNuzq+3f z0O<&EbUXaIY&;G8(r~VZ$btdKd;d4k2kf)-Z~Dg1Rq!79t_f_hR9$;filE09r}Y($ z-88VGg4b$@u_r_p)B~4PUGFMF-wb#EsK%yW!(E{IjY{srkY(|FU_WpRc&XCeMuZ&( zf4dC)qXvGLhWis({I9ypdSDxH5qMQafV{!}K@B}d`R{a6+U*;L3HE5XjjE#aA;62I z2w*zVyEWWXo)+)acDr_iXMwxw0mSy*K!>r2ouBVc(it6P3XcGHfMdWWUvH|^idk^^UAOg&G0aBj*eFqlCFweOB zN(P7kB7g|s6%Cff%FTrMl?)I8q!5xqNIn4(Km-s0M1Z8jwh}A>mn8#401-eS5D2XB Y2ds1+D(Y$+82|tP07*qoM6N<$g6xub-2eap diff --git a/app/data/icons/unused/overlaySafe_org.png b/app/data/icons/unused/overlaySafe_org.png deleted file mode 100644 index 34e9abe00967fc92d045831f54aaee9424ee053e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 295 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP-4~u}2lFqGy!NrEi9vkREb0@=1i>Et=4 zDZg{LkYwJ$y&=nJ2jlG)PA;|6(iJ`%vSwU9_jKO>q8(R$?Tr%M@7Sf?l~Ly)Jw1Pe zUXf07{KF2zxEYUIbeE;BkYt=EkzrgZPSM+6Gi`s$<{NV^vaXrglzbp%iQ7rr1kJ?e=hq*^o?gBtI{Ap)?k@hA z!b5NSc_lvI$vu6{1- HoD!M diff --git a/app/data/icons/unused/pen0.png b/app/data/icons/unused/pen0.png deleted file mode 100644 index 326af06e2f86493b7a42053584e5ad3df4b6bd21..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~OrP)S?yeGDttZbFx0 z00_RaeAb?O5@3{Mt!BbC(fr*KQAuJ+_VZyZ8 zC?*305M1%8+YkPK{qvu}Kvo3I_cc*qc>05nVeR@&aMJ+-2(Eb3t)~p$+6pKpWn||u zbWNBB=K}-~Sn;L{H~tH7b28{i3WEh*txUkQsk%4=hq4DlNB0D<=>P!$EdbL00NUi| z|8B0o|MdC#|MBYf}C3-*PsA^G$z(`BE z{@OO&3NJ&0aJF`U#~7*Tp1T)LlqZVHgMS|9j5P@dyhCu~cx3SY%ojF-Aflm>)2N zkkTazYl_~8;e~YJO(G%$NjH)tR#0A4Ab|uHK@1VIi7ECmrAYCFAJcs(dD?cq-Y#Op z{7Rwc_Idd6f8O`y1^!c!!wfGJ6i@)5IXUa8ENe;c>Df&5n8P6HU0q14u7)58CWYf3 zZS5Zj>g(&HH#0I2o16sjXN@}x3zc54*LYxPArFBxRdL}LSPnt$pnp!`H>+AHX~ zj@j8+06Qz;BjZ#W> zU2Qu80O6&NsAvtMys7DvBuNoL5MFQ`_qnjJP$PuU9gz4qk6QLXianljNs>akt`G7& z|1LK-cf)SCtADLV0W$owidyW~foO0CQd(E{URBjOLdc-OVE7OS1lFBSr<_QWOmw>l z$1R~BmQ_!jLS>%K(B7hvU~)1OS-IO0cSlxlmsH z1ani*F=sf9&4`GK)*uRNYDRTkAJsH%NS5UT0CDy*<9$EdzidyQJX8rMmm6V=ql;tnWvW z+x?a>%wjkk9;cN0jYgvwi^T%n-Q9a406=Uu&?^_MFlN=fCV~*sH0>+HFjD}6DJdz7 z9LKG-w6sL`LjZtKO4-iN&SQ$A?0v z(btiI;l}H)G9s&he1&9>AYTTC+!+iE#eED6uRItS+JM64?->{_d|+TO`p&>0Q?l(+ z4mSe>ql%}CV@SoVq=bY754I$2Hj@wk|JU!4;BadAq3giVC?VbWgIQobbCPZn4?~lN zLLJAE`3y`PM=r8Ha(9T~GA-m2IQ{VA9EPiFI5tTrG$e@SHCDCCcidjc_LXU)z^rE} zGtxBvGdnOGCFt+3| zoZ$BUD9OMTp>U4zk+(w)pF+b0-h?(*6|vrL0f+4!4j&a&;S+G+*})JJ#3aSNz43>Z zfcQdppa%s%J+4%k$KRC}lf}T)A-LYS0~jVQ8yS%aHiigKvF4MvuHOd+B7>)^pUXO@ GgeCyhhlF|n diff --git a/app/data/icons/unused/prefstimeline2.png b/app/data/icons/unused/prefstimeline2.png deleted file mode 100644 index c906a1452170a90099eb45d33eb099ca6538e5ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 467 zcmeAS@N?(olHy`uVBq!ia0vp^x*U{KPEd%?iKl(C_O-GRaI-p=B5gB{EY z4IA2y-_PIw@6t_K2F8|mef#VG+ugYwzWbBf+`Ehd4muip;{CVl-Ya~6vz9Se%xX7K zN5I?U$=`ha{#A!>_;*7dEVN-s|NQ#;i}rJwSS}=-+|Iyg5^)Ep`^Mdae&yv1KrzkZ zAe)bHJ1}gtsr-}zwmCx1MndEr1Bb#iWp_RvHlR9z6*gZ#YRVOWm|J4jui!Yce%<0^ zTVIe%I)16@ujib;$aC_m3|Tpl4WD!!7G1NiX03~ j9430E5|3CF4lw*(<21K!>4F8oSYhyV^>bP0l+XkKE3%=y diff --git a/app/data/icons/unused/printer3.png b/app/data/icons/unused/printer3.png deleted file mode 100644 index 84eac292b11e78a5b3a00f4be9add855be5867b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1238 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 XMDFmH{XR`Z00000NkvXXu0mjftIShQ diff --git a/app/data/icons/unused/smudgeblur.png b/app/data/icons/unused/smudgeblur.png deleted file mode 100644 index 5a11cf9560000b9f9b8697bd80a51f72a481cbcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 408 zcmV;J0cZY+P)K3|K1m0!ybwRanKh40000(@pN$vskoI?Q>EF`$njym%is6b70UI44Qo07?|*8+melYmTMoDGFBZ}A05S$4N2L?}9KbLh*2~7Z# CWQywm diff --git a/app/data/icons/unused/thinlines2.png b/app/data/icons/unused/thinlines2.png deleted file mode 100644 index eb1c17204e1cc17296baf3a08bff02286630604b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>(_H=O!skoK&=l_3uX3GZ$zr5sh z$g*=_FbrH!$=AUv;NT;Yk;e9sk*T9o=&S+*kUu4Zjj@3tX_1RD2Lp?Mu<9&F28D(X zQWIW1GOE0FZf>7J zBLgD~f3uVP{C5G~r=1kz;?k0G+CZ*pV!g5L^YiZebw8aZr3o-_C>&$Tf1ej#`&Vhf y}CxCy`;xOzy1P{0i5Y z9$j~+kzVm!--p+ML2#4gl9!AE>zR`@bFvtiIwo-MXq>_+V9&Cuy1^GH|8Ubx@s96I z8x0qJU&sL+j{o>7RMQ&&}8s*^>bP0l+XkKRQz*z diff --git a/app/data/icons/unused/thinlines4.png b/app/data/icons/unused/thinlines4.png deleted file mode 100644 index 2c6ab49d674a94b74fa479317f7407dc6fdd85b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 342 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|Ea{HEjtmUfZd~z?Faq)=OI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i>3@N{tuskoK&r~aoAbK!&2r>E!V z-DToXP!kHdzmGSCfki-=bJhR;&)-+u?GCo#N}KQPZJqyqDxWxf_~5B7 z`!oio4$eoXH+fFJSM$#&0IcTmv2%Xc{O4a$QJp!n!z-MD@yHd6;%D>s*LQ~SvM?~R ZF-V@z?5*N9Uk3CugQu&X%Q~loCIC=Cf3E-l diff --git a/app/data/icons/zoom-in.png b/app/data/icons/zoom-in.png deleted file mode 100644 index 0d63f15d2658bba971c6806685795d9a12dd3055..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 651 zcmV;60(AX}P)004{+008s*004(C004>d007mj001&P001PyN;-T;00009 za7bBm000XU000XU0RWnu7ytkQ0ZBwbR5(wKQ$1^9Q52l_Sn#3PY|x+2%Wl{XOeLI7YG2XuxPR z;_dAX)9F+e<(R*|VltV)X0ss@iEK`f;O6Fr*xb9$AIa>KPN!qdPB<72hnT(V{1H5l z)oO+3=VvZ>08yz_;uj0-XQ_ljp|CjySRN>s%iu3#Fc`32uQ!!_h!%?l$z&1}=7P_> zMX^{!I-N!|8pZYXHAGR|RQ4fsyIu18{b)9uurjgFXJ@i)9&Ria!+bu+VzEHv;Q`;S zt`47b8^+z;9eusLB)wiwTCJAY5|PK_A*To zkK@PVqdXEvDwTr6;rQ!ht8|LhYL&dVw`8~5Ns=VlNj95hr9bt94?eQj>k)rKpU)?Y zY{$vva;*Fx0-XJRpX&8G`Mlnp6P{JO-KI~QN&X2Is=)qGa=Ba(1VP>jzT(Wu@eog8 lJRTF@6&5l~CKDt{!f!k^ok(=0fS>>X002ovPDHLkV1m`37O4OL diff --git a/app/data/icons/zoom-out.png b/app/data/icons/zoom-out.png deleted file mode 100644 index 7ad245c474a3d392a0058bcfefaa885edb197eec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 655 zcmV;A0&x9_P)004{+008s*004(C004>d007mj001&P001PyN;-T;00009 za7bBm000XU000XU0RWnu7ytkQ1xZ9fR5(wKQ_E^vQ50SGHlR^NXwaYzQc#>ZaiAb3 z6F;CCJMja=PY|l&2MA6iBWKZ&B8Vsrg7c8hiXtMuP#ma;qVW-T&pFk!RxMqyC-6k&=r1oZo6S%z zmyyY2pjN99kH=3A`CzqLNvqYuE=cpEk>UV1_M({I9BptFu;s$ zJ_XL`?d=Vwzn|r{&aarAN7k77eFqur)Y&P4LeaH@nLl`w- zEV$|5uCIx0C%s-zEJPFx28lf%DHIA(g+hT{-8b<)iGQM6t+LOA z^?Hr3kB|8F^duy4k)s# zNF*YL?BtY6C3gHD19004{+008s*004(C004>d007mj001&P001PyN;-T;00009 za7bBm000XU000XU0RWnu7ytkQ8c9S!R5(wKQ(cRpVHm#NcURsa(GI3Km~F^`a^irJ z*%Lot=h%rK;5Vp6et>e~(AgNxrZmw`%DLl>B>8G^faIIu+2?sMyT;n~s;l(g_jP|g z7l9B0hD0Jkn$0HK?KXzPAyg_A>~=f+e!svI&VUz)$Kyn&)8X^=HS||kn9XJ=l}bpb z(@-jvh{a-Oi+r$HEJUqV!{hPruFK^T>-8FniwoFnHe@mx3^Cc<(P&Vs)%NM9 z5c~Z;X0$U)X2Ruag%zbtCWHCrhEKHFY*4LMch`OgQK3-aCq3A7I^|>nxm*sb)k>ox z*vlA=Mr^m+z2q38!{OkE8q+D*&ZE%?27>{`ViDPF77q^(@Or(pmbjN3LwI?4A(2P~ zjYb1bTI|#1C7*&FH+37NKR)8e^D|$G;pyp#dFJ;dhf3#IE|*Ct7$k1D zn@EzxC&}mYG_;GK2;Q^P=@9mW;c%E2IjmDC6lnNA3NZG1JwnYy!lBUHB2v7{QZdMKs`d3SLl&pHk%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); diff --git a/app/src/colorslider.cpp b/app/src/colorslider.cpp index f3b3c24c4..1f67e0585 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/mainwindow2.cpp b/app/src/mainwindow2.cpp index f59aaa32c..81ca1189b 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -1697,6 +1697,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/statusbar.cpp b/app/src/statusbar.cpp index a34e51d27..904c45506 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); @@ -142,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 8a224c3ac..2dc7748f1 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 af5bd22c1..dcb26b28f 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); @@ -96,12 +94,12 @@ void TimeLine::initUI() 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); @@ -119,23 +117,21 @@ void TimeLine::initUI() // --- 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(); diff --git a/app/src/timelinecells.cpp b/app/src/timelinecells.cpp index a25805aab..4524cf38c 100644 --- a/app/src/timelinecells.cpp +++ b/app/src/timelinecells.cpp @@ -605,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) { diff --git a/app/ui/cameraoptionswidget.ui b/app/ui/cameraoptionswidget.ui index ad3a20ac1..f2100f77f 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 a66c820f1..559d9a1e7 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 7e955dca5..315bf36b7 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/mainwindow2.ui b/app/ui/mainwindow2.ui index 50b108997..0d35271b7 100644 --- a/app/ui/mainwindow2.ui +++ b/app/ui/mainwindow2.ui @@ -49,7 +49,7 @@ 0 0 831 - 26 + 24 @@ -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 @@ -330,7 +334,7 @@ - :/icons/new.png:/icons/new.png + :/icons/themes/playful/menubar/new.svg:/icons/themes/playful/menubar/new.svg New @@ -339,7 +343,7 @@ - :/icons/open.png:/icons/open.png + :/icons/themes/playful/menubar/open.svg:/icons/themes/playful/menubar/open.svg Open @@ -348,7 +352,7 @@ - :/icons/save.png:/icons/save.png + :/icons/themes/playful/menubar/save.svg:/icons/themes/playful/menubar/save.svg Save @@ -357,17 +361,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 @@ -423,7 +423,7 @@ - :/icons/undo.png:/icons/undo.png + :/icons/themes/playful/menubar/undo.svg:/icons/themes/playful/menubar/undo.svg Undo @@ -432,7 +432,7 @@ - :/icons/redo.png:/icons/redo.png + :/icons/themes/playful/menubar/redo.svg:/icons/themes/playful/menubar/redo.svg Redo @@ -444,7 +444,7 @@ - :/icons/cut.png:/icons/cut.png + :/icons/themes/playful/menubar/cut.svg:/icons/themes/playful/menubar/cut.svg Cut @@ -456,7 +456,7 @@ - :/icons/copy.png:/icons/copy.png + :/icons/themes/playful/menubar/copy.svg:/icons/themes/playful/menubar/copy.svg Copy @@ -468,7 +468,7 @@ - :/icons/paste.png:/icons/paste.png + :/icons/themes/playful/menubar/paste.svg:/icons/themes/playful/menubar/paste.svg Paste @@ -487,7 +487,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 @@ -509,7 +509,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 @@ -518,7 +518,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 @@ -537,7 +537,7 @@ - :/icons/zoom-reset.png:/icons/zoom-reset.png + :/icons/themes/playful/menubar/view-reset.svg:/icons/themes/playful/menubar/view-reset.svg Reset @@ -549,7 +549,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 @@ -561,7 +561,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 @@ -581,7 +581,7 @@ - :/icons/grid.png:/icons/grid.png + :/icons/themes/playful/display/overlay-grid.svg:/icons/themes/playful/display/overlay-grid.svg Grid @@ -593,7 +593,7 @@ - :/app/icons/onionPrev.png:/app/icons/onionPrev.png + :/icons/themes/playful/onion/onionskin-red.svg:/icons/themes/playful/onion/onionskin-red.svg Previous @@ -608,7 +608,7 @@ - :/app/icons/onionNext.png:/app/icons/onionNext.png + :/icons/themes/playful/onion/onionskin-blue.svg:/icons/themes/playful/onion/onionskin-blue.svg Next @@ -620,7 +620,7 @@ - :/icons/controls/play.png:/icons/controls/play.png + :/icons/themes/playful/controls/control-play.svg:/icons/themes/playful/controls/control-play.svg Play @@ -632,26 +632,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 @@ -659,7 +651,7 @@ - :/icons/add.png:/icons/add.png + :/icons/themes/playful/timeline/frame-add.svg:/icons/themes/playful/timeline/frame-add.svg Add Frame @@ -668,7 +660,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 @@ -677,7 +669,7 @@ - :/icons/remove.png:/icons/remove.png + :/icons/themes/playful/timeline/frame-remove.svg:/icons/themes/playful/timeline/frame-remove.svg Remove Frame @@ -686,7 +678,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 @@ -695,7 +687,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 @@ -704,7 +696,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 @@ -713,7 +705,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 @@ -722,7 +714,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 @@ -731,7 +723,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 @@ -740,7 +732,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 @@ -749,7 +741,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 @@ -758,7 +750,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 @@ -767,7 +759,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 @@ -776,7 +768,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 @@ -785,7 +777,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 @@ -794,7 +786,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 @@ -803,7 +795,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 @@ -812,13 +804,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 @@ -1123,7 +1119,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 @@ -1135,7 +1131,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 @@ -1147,7 +1143,7 @@ - :/icons/overlayCenter.png:/icons/overlayCenter.png + :/icons/themes/playful/display/overlay-center.svg:/icons/themes/playful/display/overlay-center.svg Center @@ -1159,7 +1155,7 @@ - :/icons/overlayThirds.png:/icons/overlayThirds.png + :/icons/themes/playful/display/overlay-thirds.svg:/icons/themes/playful/display/overlay-thirds.svg Thirds @@ -1171,7 +1167,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 @@ -1183,7 +1179,7 @@ - :/icons/overlaySafe.png:/icons/overlaySafe.png + :/icons/themes/playful/display/overlay-safe.svg:/icons/themes/playful/display/overlay-safe.svg Safe Areas @@ -1195,7 +1191,7 @@ - :/icons/overlayPerspective1.png:/icons/overlayPerspective1.png + :/icons/themes/playful/display/perspective-onepoint.svg:/icons/themes/playful/display/perspective-onepoint.svg One Point Perspective @@ -1207,7 +1203,7 @@ - :/icons/overlayPerspective2.png:/icons/overlayPerspective2.png + :/icons/themes/playful/display/perspective-twopoints.svg:/icons/themes/playful/display/perspective-twopoints.svg Two Point Perspective @@ -1219,7 +1215,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 9623c2cd5..20c6eec59 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 @@ -161,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 @@ -254,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 aabb79cba..2e9ddf3f0 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 80ead2921..b2c062096 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/core_lib/data/core_lib.qrc b/core_lib/data/core_lib.qrc index a420eac51..5bc67e882 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/tool/brushtool.cpp b/core_lib/src/tool/brushtool.cpp index 88b6b6e22..4bec803d5 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) diff --git a/core_lib/src/tool/buckettool.cpp b/core_lib/src/tool/buckettool.cpp index 911e881d6..6ef11c59c 100644 --- a/core_lib/src/tool/buckettool.cpp +++ b/core_lib/src/tool/buckettool.cpp @@ -81,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); } } diff --git a/core_lib/src/tool/cameratool.cpp b/core_lib/src/tool/cameratool.cpp index feb0e5462..9c74fe31a 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: diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index 0ae100520..405a6498c 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) diff --git a/core_lib/src/tool/eyedroppertool.cpp b/core_lib/src/tool/eyedroppertool.cpp index 0007c1d86..d72c3f71a 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 e28e20299..d741a2ed1 100644 --- a/core_lib/src/tool/handtool.cpp +++ b/core_lib/src/tool/handtool.cpp @@ -62,7 +62,7 @@ void HandTool::updateSettings(const SETTING setting) QCursor HandTool::cursor() { - return mIsHeld ? Qt::ClosedHandCursor : Qt::OpenHandCursor; + return mIsHeld ? QCursor(Qt::ClosedHandCursor) : QCursor(Qt::OpenHandCursor); } void HandTool::pointerPressEvent(PointerEvent*) diff --git a/core_lib/src/tool/movetool.cpp b/core_lib/src/tool/movetool.cpp index ec02bafc0..c8b99315e 100644 --- a/core_lib/src/tool/movetool.cpp +++ b/core_lib/src/tool/movetool.cpp @@ -376,32 +376,32 @@ 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: diff --git a/core_lib/src/tool/penciltool.cpp b/core_lib/src/tool/penciltool.cpp index 5e5e04636..cd2169870 100644 --- a/core_lib/src/tool/penciltool.cpp +++ b/core_lib/src/tool/penciltool.cpp @@ -140,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) diff --git a/core_lib/src/tool/pentool.cpp b/core_lib/src/tool/pentool.cpp index 482d5c277..64dbed970 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) diff --git a/core_lib/src/tool/polylinetool.cpp b/core_lib/src/tool/polylinetool.cpp index c404e343c..4d723b487 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() diff --git a/core_lib/src/tool/selecttool.cpp b/core_lib/src/tool/selecttool.cpp index 52d57fa68..cab118899 100644 --- a/core_lib/src/tool/selecttool.cpp +++ b/core_lib/src/tool/selecttool.cpp @@ -57,27 +57,26 @@ QCursor SelectTool::cursor() 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 QCursor(mCursorPixmap); } diff --git a/core_lib/src/tool/smudgetool.cpp b/core_lib/src/tool/smudgetool.cpp index f55538000..de94077e6 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); } } From 127cd62212311b6b72a731c7a96b74021801bec4 Mon Sep 17 00:00:00 2001 From: Jakob Date: Tue, 17 Oct 2023 21:30:25 +0200 Subject: [PATCH 18/26] Fix heap use after free in MoveTool (#1791) Also gets rid of a layer pointer in SelectTool --- core_lib/src/tool/movetool.cpp | 30 +++++++--------------- core_lib/src/tool/movetool.h | 2 -- core_lib/src/tool/penciltool.h | 2 -- core_lib/src/tool/selecttool.cpp | 44 ++++++++++++++------------------ core_lib/src/tool/selecttool.h | 7 +++-- 5 files changed, 31 insertions(+), 54 deletions(-) diff --git a/core_lib/src/tool/movetool.cpp b/core_lib/src/tool/movetool.cpp index c8b99315e..ed3757da7 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()) { @@ -124,8 +124,8 @@ void MoveTool::pointerPressEvent(PointerEvent* event) 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,9 +149,9 @@ 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->updateFrame(); @@ -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; } @@ -406,7 +395,6 @@ QCursor MoveTool::cursor(MoveMode mode) const } default: return Qt::ArrowCursor; - break; } cursorPainter.end(); diff --git a/core_lib/src/tool/movetool.h b/core_lib/src/tool/movetool.h index 7e5c210e8..38107d642 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.h b/core_lib/src/tool/penciltool.h index 0b15fb719..2cb823296 100644 --- a/core_lib/src/tool/penciltool.h +++ b/core_lib/src/tool/penciltool.h @@ -51,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/selecttool.cpp b/core_lib/src/tool/selecttool.cpp index cab118899..1fa4d639a 100644 --- a/core_lib/src/tool/selecttool.cpp +++ b/core_lib/src/tool/selecttool.cpp @@ -94,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(); } @@ -120,9 +120,9 @@ void SelectTool::beginSelection() 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(); @@ -130,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; } @@ -150,9 +150,9 @@ 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()); } @@ -164,8 +164,8 @@ void SelectTool::pointerMoveEvent(PointerEvent*) 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 @@ -181,7 +181,7 @@ void SelectTool::pointerReleaseEvent(PointerEvent* event) } else { - keepSelection(); + keepSelection(currentLayer); } mStartMoveMode = MoveMode::NONE; @@ -200,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); } @@ -213,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; @@ -302,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 5383e8193..16e732846 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); }; From 4c0dd6f25ac51a9d9f2d1b0f50a91cae643fad36 Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Sat, 4 Nov 2023 00:04:43 +0100 Subject: [PATCH 19/26] Run macdeployqtfix with Python 2 explicitly Changes to the macOS image seem to make this necessary --- .github/actions/create-package/create-package.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/create-package/create-package.sh b/.github/actions/create-package/create-package.sh index 71a5ddf9f..580c83539 100755 --- a/.github/actions/create-package/create-package.sh +++ b/.github/actions/create-package/create-package.sh @@ -75,7 +75,7 @@ create_package_macos() { 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::" From f7dbcfdeeb3fe08abe05886e3d7efc5adc21df26 Mon Sep 17 00:00:00 2001 From: Ben Beasley Date: Sun, 12 Nov 2023 08:01:33 -0500 Subject: [PATCH 20/26] Fix deprecated top-level developer_name in AppData XML (#1796) * Fix deprecated top-level developer_name in AppData XML Use the name element in a developer block instead, as recommended by appstreamcli 1.0.0. * Add id property to the AppStream developer element --------- Co-authored-by: Jakob Gahde --- app/data/org.pencil2d.Pencil2D.metainfo.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/data/org.pencil2d.Pencil2D.metainfo.xml b/app/data/org.pencil2d.Pencil2D.metainfo.xml index 4debb60d3..bf7a50781 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 From a7ab2a60adf4ec9e3f31aa35b9019b3544b5d506 Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Fri, 24 Nov 2023 21:46:51 +0100 Subject: [PATCH 21/26] Fix compiler warnings --- core_lib/src/external/macosx/macosx.cpp | 2 ++ core_lib/src/graphics/bitmap/tiledbuffer.h | 4 ++++ core_lib/src/managers/viewmanager.cpp | 7 ++++--- core_lib/src/tool/cameratool.cpp | 4 ++-- core_lib/src/tool/strokemanager.cpp | 5 ++--- core_lib/src/util/util.cpp | 18 +++++++++--------- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/core_lib/src/external/macosx/macosx.cpp b/core_lib/src/external/macosx/macosx.cpp index deb1316bc..a4f1f5435 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/graphics/bitmap/tiledbuffer.h b/core_lib/src/graphics/bitmap/tiledbuffer.h index 7439c628c..3e29cfed8 100644 --- a/core_lib/src/graphics/bitmap/tiledbuffer.h +++ b/core_lib/src/graphics/bitmap/tiledbuffer.h @@ -30,7 +30,11 @@ struct TileIndex { 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; } diff --git a/core_lib/src/managers/viewmanager.cpp b/core_lib/src/managers/viewmanager.cpp index 928d11d18..9b85848ea 100644 --- a/core_lib/src/managers/viewmanager.cpp +++ b/core_lib/src/managers/viewmanager.cpp @@ -205,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/tool/cameratool.cpp b/core_lib/src/tool/cameratool.cpp index 9c74fe31a..e82edb487 100644 --- a/core_lib/src/tool/cameratool.cpp +++ b/core_lib/src/tool/cameratool.cpp @@ -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/strokemanager.cpp b/core_lib/src/tool/strokemanager.cpp index b4e9a6b4f..d94ba58b1 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/util/util.cpp b/core_lib/src/util/util.cpp index 39468c04e..622fb4e44 100644 --- a/core_lib/src/util/util.cpp +++ b/core_lib/src/util/util.cpp @@ -19,7 +19,7 @@ GNU General Public License for more details. #include #include -static inline bool clipInfiniteLineToEdge(qreal& t0, qreal& t1, qreal p, qreal q) +static inline bool clipLineToEdge(qreal& t0, qreal& t1, qreal p, qreal q) { if (p < 0) { // Line entering the clipping window t0 = qMax(t0, q / p); @@ -37,14 +37,14 @@ 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) || - !clipInfiniteLineToEdge(t0, t1, -dx, x1 - left) || - !clipInfiniteLineToEdge(t0, t1, dx, right - x1) || - !clipInfiniteLineToEdge(t0, t1, -dy, y1 - top) || - !clipInfiniteLineToEdge(t0, t1, dy, bottom - y1)) { + 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 {}; } From 18c5494f61f228a0f7b8820420627d4ac76231a8 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sat, 25 Nov 2023 13:59:17 +0100 Subject: [PATCH 22/26] Fix vector layer color import (#1798) * Get rid of Object pointers in layer and vector classes * Import colors of imported vector layers * Fix tests --- app/src/importlayersdialog.cpp | 51 +++++++++++++------- core_lib/src/canvaspainter.cpp | 4 +- core_lib/src/graphics/vector/beziercurve.cpp | 22 ++++----- core_lib/src/graphics/vector/beziercurve.h | 2 +- core_lib/src/graphics/vector/vectorimage.cpp | 18 ++----- core_lib/src/graphics/vector/vectorimage.h | 6 +-- core_lib/src/managers/soundmanager.cpp | 51 -------------------- core_lib/src/managers/soundmanager.h | 1 - core_lib/src/structure/layer.cpp | 15 ++---- core_lib/src/structure/layer.h | 13 ++--- core_lib/src/structure/layerbitmap.cpp | 4 +- core_lib/src/structure/layerbitmap.h | 4 +- core_lib/src/structure/layercamera.cpp | 4 +- core_lib/src/structure/layercamera.h | 4 +- core_lib/src/structure/layersound.cpp | 5 +- core_lib/src/structure/layersound.h | 4 +- core_lib/src/structure/layervector.cpp | 6 +-- core_lib/src/structure/layervector.h | 4 +- core_lib/src/structure/object.cpp | 38 +++++++-------- core_lib/src/structure/object.h | 8 +-- tests/src/test_layer.cpp | 21 +++----- tests/src/test_layercamera.cpp | 38 +++------------ 22 files changed, 112 insertions(+), 211 deletions(-) diff --git a/app/src/importlayersdialog.cpp b/app/src/importlayersdialog.cpp index f303072f0..35ae3f71b 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/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index f03ae616f..24529ec38 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -251,7 +251,7 @@ void CanvasPainter::paintVectorOnionSkinFrame(QPainter& painter, const QRect& bl QPainter onionSkinPainter; initializePainter(onionSkinPainter, mOnionSkinPixmap, blitRect); - vectorImage->paintImage(onionSkinPainter, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); + vectorImage->paintImage(onionSkinPainter, *mObject, mOptions.bOutlines, mOptions.bThinLines, mOptions.bAntiAlias); paintOnionSkinFrame(painter, onionSkinPainter, nFrame, colorize, vectorImage->getOpacity()); } @@ -344,7 +344,7 @@ void CanvasPainter::paintCurrentVectorFrame(QPainter& painter, const QRect& blit 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) { diff --git a/core_lib/src/graphics/vector/beziercurve.cpp b/core_lib/src/graphics/vector/beziercurve.cpp index 97c2a79d6..ebfe27eea 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)); } @@ -869,7 +869,7 @@ bool BezierCurve::findIntersection(BezierCurve curve1, int i1, BezierCurve curve //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); diff --git a/core_lib/src/graphics/vector/beziercurve.h b/core_lib/src/graphics/vector/beziercurve.h index 0f1b67f30..47297f57f 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 9dfe0980c..061b36c2b 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,11 +1182,13 @@ 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) @@ -1220,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); @@ -1246,7 +1236,7 @@ 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 ec97458a5..8f9769deb 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/managers/soundmanager.cpp b/core_lib/src/managers/soundmanager.cpp index 655d51307..8bcc3843c 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 31abc12d5..4085ad3b5 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/structure/layer.cpp b/core_lib/src/structure/layer.cpp index 1e5f43553..f272e5912 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 bbb857cf8..0e82e2b1d 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 119464046..d5eb0cc62 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 4865dd64a..8ae10fe1a 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 c41507f5b..b072954e0 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")); @@ -539,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 0ef4cc151..54773ecb6 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 83b46bc50..c3c88751d 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 718c6fc38..ddcf8c6fa 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 57ea14443..3efd51e9f 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 563a1800b..163cdc0a4 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 30391fed5..17834e6f7 100644 --- a/core_lib/src/structure/object.cpp +++ b/core_lib/src/structure/object.cpp @@ -97,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(); @@ -119,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); @@ -129,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); @@ -139,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. @@ -149,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); @@ -378,7 +378,7 @@ bool Object::addLayer(Layer* layer) { return false; } - layer->setObject(this); + layer->setId(getUniqueLayerID()); mLayers.append(layer); return true; } @@ -456,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) @@ -507,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 @@ -542,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; @@ -553,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 @@ -606,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(); @@ -755,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); } } } @@ -815,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") { diff --git a/core_lib/src/structure/object.h b/core_lib/src/structure/object.h index 6ed45a318..c7701d06b 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/tests/src/test_layer.cpp b/tests/src/test_layer.cpp index 39eb0be60..73478d105 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 c37afdc26..f6899d73f 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") From 8c3728f94ba432856b6c6bf1319ef34f8361bc40 Mon Sep 17 00:00:00 2001 From: Jakob Gahde Date: Sat, 9 Dec 2023 22:55:28 +0100 Subject: [PATCH 23/26] Mention the Qt 6 Multimedia requirement in build guides --- docs/build_linux.md | 2 +- docs/build_mac.md | 1 + docs/build_win.md | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/build_linux.md b/docs/build_linux.md index c591ec298..81195359d 100644 --- a/docs/build_linux.md +++ b/docs/build_linux.md @@ -21,7 +21,7 @@ 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 diff --git a/docs/build_mac.md b/docs/build_mac.md index 7ce2127a9..0c3897af7 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. diff --git a/docs/build_win.md b/docs/build_win.md index 2364ac3f2..9b467b40c 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. From 363dcd79c6d772abb126b9df22a5369910c21ffb Mon Sep 17 00:00:00 2001 From: Oliver Stevns <1045397+MrStevns@users.noreply.github.com> Date: Sun, 10 Dec 2023 14:56:30 +0100 Subject: [PATCH 24/26] Regression - #1799: Fix pen dabs being added to each other (#1800) Actually the first check in canvas painter would always return true because the blitRect and mTilledBuffer rect are in different coordinate spaces. --- core_lib/src/canvaspainter.cpp | 11 ++------- core_lib/src/canvaspainter.h | 3 --- core_lib/src/interface/scribblearea.cpp | 33 +++++++++++++++---------- core_lib/src/tool/erasertool.cpp | 4 +-- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 24529ec38..3990e3920 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -300,16 +300,9 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit if (isCurrentLayer && isDrawing) { - // Certain tools require being painted continuously, for example, the Polyline tool. - // The tiled buffer does not update the area outside which it paints, - // so in that case, in order to see the previously laid-down polyline stroke, - // the surrounding area must be drawn again before - // applying the new tiled output on top - if (!blitRect.contains(mTiledBuffer->bounds()) || mOptions.bIgnoreCanvasBuffer) { - currentBitmapPainter.setCompositionMode(QPainter::CompositionMode_Source); - currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); - } + currentBitmapPainter.drawImage(paintedImage->topLeft(), *paintedImage->image()); + currentBitmapPainter.setCompositionMode(mOptions.cmBufferBlendMode); const auto tiles = mTiledBuffer->tiles(); for (const Tile* tile : tiles) { currentBitmapPainter.drawPixmap(tile->posF(), tile->pixmap()); diff --git a/core_lib/src/canvaspainter.h b/core_lib/src/canvaspainter.h index 99b90c6be..7f1d3c285 100644 --- a/core_lib/src/canvaspainter.h +++ b/core_lib/src/canvaspainter.h @@ -42,9 +42,6 @@ struct CanvasPainterOptions bool bThinLines = false; bool bOutlines = false; - /// When using a tool that can't rely on canvas buffer, - /// for example Polyline because we're continously clearing the surface - bool bIgnoreCanvasBuffer = false; LayerVisibility eLayerVisibility = LayerVisibility::RELATED; float fLayerVisibilityThreshold = 0.f; float scaling = 1.0f; diff --git a/core_lib/src/interface/scribblearea.cpp b/core_lib/src/interface/scribblearea.cpp index 4e8d5e319..d39359c02 100644 --- a/core_lib/src/interface/scribblearea.cpp +++ b/core_lib/src/interface/scribblearea.cpp @@ -202,16 +202,6 @@ void ScribbleArea::onTileUpdated(TiledBuffer* tiledBuffer, Tile* tile) void ScribbleArea::onTileCreated(TiledBuffer* tiledBuffer, Tile* tile) { Q_UNUSED(tiledBuffer) - Layer::LAYER_TYPE layerType = mEditor->layers()->currentLayer()->type(); - if (layerType == Layer::BITMAP) { - const auto& bitmapImage = currentBitmapImage(mEditor->layers()->currentLayer()); - const QImage& image = *bitmapImage->image(); - tile->load(image, bitmapImage->topLeft()); - } else if (layerType == Layer::VECTOR) { - - // Not used, we only use the buffer to paint the stroke before painting the real vector stroke - } - const QRectF& mappedRect = mEditor->view()->getView().mapRect(QRectF(tile->bounds())); update(mappedRect.toAlignedRect()); } @@ -855,7 +845,24 @@ void ScribbleArea::paintBitmapBuffer() BitmapImage* targetImage = currentBitmapImage(layer); if (targetImage != nullptr) { - targetImage->paste(&mTiledBuffer, QPainter::CompositionMode_Source); + QPainter::CompositionMode cm = QPainter::CompositionMode_SourceOver; + switch (currentTool()->type()) + { + case ERASER: + cm = QPainter::CompositionMode_DestinationOut; + break; + case BRUSH: + case PEN: + case PENCIL: + if (currentTool()->properties.preserveAlpha) + { + cm = QPainter::CompositionMode_SourceOver; + } + break; + default: //nothing + break; + } + targetImage->paste(&mTiledBuffer, cm); } QRect rect = mEditor->view()->mapCanvasToScreen(mTiledBuffer.bounds()).toRect(); @@ -1208,7 +1215,6 @@ void ScribbleArea::prepCanvas(int frame) o.fLayerVisibilityThreshold = mPrefs->getFloat(SETTING::LAYER_VISIBILITY_THRESHOLD); o.scaling = mEditor->view()->scaling(); o.cmBufferBlendMode = mEditor->tools()->currentTool()->type() == ToolType::ERASER ? QPainter::CompositionMode_DestinationOut : QPainter::CompositionMode_SourceOver; - o.bIgnoreCanvasBuffer = currentTool()->type() == POLYLINE; OnionSkinPainterOptions onionSkinOptions; onionSkinOptions.enabledWhilePlaying = mPrefs->getInt(SETTING::ONION_WHILE_PLAYBACK); @@ -1270,7 +1276,8 @@ void ScribbleArea::drawPath(QPainterPath path, QPen pen, QBrush brush, QPainter: void ScribbleArea::drawPen(QPointF thePoint, qreal brushWidth, QColor fillColor, bool useAA) { - mTiledBuffer.drawBrush(thePoint, brushWidth, mEditor->view()->mapScreenToCanvas(mCursorImg.rect()).width(), Qt::NoPen, QBrush(fillColor, Qt::SolidPattern), QPainter::CompositionMode_SourceOver, 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) diff --git a/core_lib/src/tool/erasertool.cpp b/core_lib/src/tool/erasertool.cpp index 405a6498c..7d57ade7e 100644 --- a/core_lib/src/tool/erasertool.cpp +++ b/core_lib/src/tool/erasertool.cpp @@ -200,7 +200,7 @@ void EraserTool::paintAt(QPointF point) brushWidth, properties.feather, QColor(255, 255, 255, 255), - QPainter::CompositionMode_DestinationOut, + QPainter::CompositionMode_SourceOver, opacity, properties.useFeather, properties.useAA == ON); @@ -240,7 +240,7 @@ void EraserTool::drawStroke() brushWidth, properties.feather, Qt::white, - QPainter::CompositionMode_DestinationOut, + QPainter::CompositionMode_SourceOver, opacity, properties.useFeather, properties.useAA == ON); From 3c9a7a4d4f7072ec577ff10e59b58bd882a5d209 Mon Sep 17 00:00:00 2001 From: scribblemaniac Date: Mon, 11 Dec 2023 23:41:36 -0700 Subject: [PATCH 25/26] Improve support for animated image importing (#1801) * Fix movie export not being a modal operation * Make DoubleProgressDialog inherit from QProgressDialog Probably not done initially because it adds a lot of member functions and properties that are not used by the double progress dialog. However it handles canceling much better than we do manually, including canceling with keyboard shortcuts or by closing the dialog. * Improve gif import progress dialog and refactor The progress dialog now updates as it imports the gif, and can be aborted during the import. Some refactoring was done to move some logic around. * Remove animated image handling from Editor::importBitmapImage The error checking has also been modified to hopefully be more robust as it no longer depends on reader.size() being invalid, which might not occur during certain types of image corruption, and may occur without corruption for certain types of image formats. * Add support for WebP import through a generic "Animated Image" import * Add non-animated WEBP import/export * Fix incorrect dialog titles --- app/src/actioncommands.cpp | 49 +++++++++++++- app/src/actioncommands.h | 1 + app/src/doubleprogressdialog.cpp | 4 +- app/src/doubleprogressdialog.h | 7 +- app/src/filedialog.cpp | 6 ++ app/src/importimageseqdialog.cpp | 10 ++- app/src/mainwindow2.cpp | 51 +-------------- app/src/mainwindow2.h | 2 +- app/ui/exportimageoptions.ui | 5 ++ app/ui/mainwindow2.ui | 6 +- core_lib/src/interface/editor.cpp | 105 +++++++++++++++++++++--------- core_lib/src/interface/editor.h | 4 +- core_lib/src/structure/object.cpp | 4 ++ core_lib/src/util/fileformat.cpp | 1 + core_lib/src/util/fileformat.h | 7 +- core_lib/src/util/filetype.h | 1 + 16 files changed, 166 insertions(+), 97 deletions(-) diff --git a/app/src/actioncommands.cpp b/app/src/actioncommands.cpp index 35727c69e..347779eeb 100644 --- a/app/src/actioncommands.cpp +++ b/app/src/actioncommands.cpp @@ -47,6 +47,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" @@ -65,6 +67,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); @@ -106,6 +152,7 @@ Status ActionCommands::importMovieVideo() { ErrorDialog errorDialog(st.title(), st.description(), st.details().html(), mParent); errorDialog.exec(); + return Status::SAFE; } mEditor->layers()->notifyAnimationLengthChanged(); @@ -293,7 +340,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; diff --git a/app/src/actioncommands.h b/app/src/actioncommands.h index f55e832c8..6d8ac3e34 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/doubleprogressdialog.cpp b/app/src/doubleprogressdialog.cpp index 7d171a01b..22c2dc390 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 8dda8301f..93bfb67ab 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 8b35d69ca..42ee65db2 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/importimageseqdialog.cpp b/app/src/importimageseqdialog.cpp index 896067a88..86c451591 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); diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp index 81ca1189b..da3d6ee2b 100644 --- a/app/src/mainwindow2.cpp +++ b/app/src/mainwindow2.cpp @@ -254,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); }); @@ -949,57 +949,12 @@ 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; } diff --git a/app/src/mainwindow2.h b/app/src/mainwindow2.h index a9f9f7a0c..9f1b683f6 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/ui/exportimageoptions.ui b/app/ui/exportimageoptions.ui index 23341ac93..98d1ae91c 100644 --- a/app/ui/exportimageoptions.ui +++ b/app/ui/exportimageoptions.ui @@ -98,6 +98,11 @@ TIFF
+ + + WEBP + + diff --git a/app/ui/mainwindow2.ui b/app/ui/mainwindow2.ui index 0d35271b7..d444be23e 100644 --- a/app/ui/mainwindow2.ui +++ b/app/ui/mainwindow2.ui @@ -65,7 +65,7 @@ - + @@ -912,9 +912,9 @@ F1 - + - Animated GIF... + Animated Image... diff --git a/core_lib/src/interface/editor.cpp b/core_lib/src/interface/editor.cpp index 7c4d5bc60..49af52acb 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 diff --git a/core_lib/src/interface/editor.h b/core_lib/src/interface/editor.h index eef05b266..a3ba0e179 100644 --- a/core_lib/src/interface/editor.h +++ b/core_lib/src/interface/editor.h @@ -174,7 +174,7 @@ class Editor : public QObject 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(); @@ -230,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/structure/object.cpp b/core_lib/src/structure/object.cpp index 17834e6f7..bb25cdb1e 100644 --- a/core_lib/src/structure/object.cpp +++ b/core_lib/src/structure/object.cpp @@ -828,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/util/fileformat.cpp b/core_lib/src/util/fileformat.cpp index 55707cfa5..3d4eb7707 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 2ce07e5fa..6935d0aa4 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 a0558fa5a..9a88fad1e 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 From 4eb164396476eff449a74ff80aa6a2aca6cb7a31 Mon Sep 17 00:00:00 2001 From: Oliver Stevns <1045397+MrStevns@users.noreply.github.com> Date: Fri, 19 Jan 2024 21:53:22 +0100 Subject: [PATCH 26/26] Fix stroke not being shown on a selection while drawing (#1807) * Fix stroke not being shown on a selection while drawing * Fix transform not being applied when leaving a temporary tool * Assert that selection using identity transform is implied while drawing --------- Co-authored-by: Jakob Gahde --- core_lib/src/canvaspainter.cpp | 3 ++- core_lib/src/managers/toolmanager.cpp | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core_lib/src/canvaspainter.cpp b/core_lib/src/canvaspainter.cpp index 3990e3920..ef6c21221 100644 --- a/core_lib/src/canvaspainter.cpp +++ b/core_lib/src/canvaspainter.cpp @@ -315,7 +315,8 @@ void CanvasPainter::paintCurrentBitmapFrame(QPainter& painter, const QRect& blit } // We do not wish to draw selection transformations on anything but the current layer - if (isCurrentLayer && mRenderTransform) { + Q_ASSERT(!isDrawing || mSelectionTransform.isIdentity()); + if (isCurrentLayer && mRenderTransform && !isDrawing) { paintTransformedSelection(currentBitmapPainter, paintedImage, mSelection); } diff --git a/core_lib/src/managers/toolmanager.cpp b/core_lib/src/managers/toolmanager.cpp index 14233ec79..25c6ef2b5 100644 --- a/core_lib/src/managers/toolmanager.cpp +++ b/core_lib/src/managers/toolmanager.cpp @@ -412,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;