From 0b44b83cf34fb8054d33ba2ded28576b6c25b2ad Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 5 Sep 2019 20:03:16 -0300 Subject: [PATCH] Add a timer to MovingPixelsState to avoid RotSprite on each mouse move We can use the fast algorithm for fast feedback, and use a timer to draw with RotSprite when the mouse doesn't move after 50ms. --- src/app/doc.cpp | 6 ++++ src/app/doc.h | 1 + src/app/ui/editor/moving_pixels_state.cpp | 15 ++++++++ src/app/ui/editor/moving_pixels_state.h | 4 +++ src/app/ui/editor/pixels_movement.cpp | 43 ++++++++++++++--------- src/app/ui/editor/pixels_movement.h | 8 +++++ 6 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/app/doc.cpp b/src/app/doc.cpp index 1a4944eec..65855c6f7 100644 --- a/src/app/doc.cpp +++ b/src/app/doc.cpp @@ -255,6 +255,12 @@ void Doc::setFormatOptions(const FormatOptionsPtr& format_options) ////////////////////////////////////////////////////////////////////// // Boundaries +void Doc::destroyMaskBoundaries() +{ + m_maskBoundaries.reset(); + notifySelectionBoundariesChanged(); +} + void Doc::generateMaskBoundaries(const Mask* mask) { m_maskBoundaries.reset(); diff --git a/src/app/doc.h b/src/app/doc.h index 5d73fa5b4..edf3aba8a 100644 --- a/src/app/doc.h +++ b/src/app/doc.h @@ -135,6 +135,7 @@ namespace app { ////////////////////////////////////////////////////////////////////// // Boundaries + void destroyMaskBoundaries(); void generateMaskBoundaries(const Mask* mask = nullptr); const MaskBoundaries* getMaskBoundaries() const { diff --git a/src/app/ui/editor/moving_pixels_state.cpp b/src/app/ui/editor/moving_pixels_state.cpp index 6c63e319f..fad75d4d1 100644 --- a/src/app/ui/editor/moving_pixels_state.cpp +++ b/src/app/ui/editor/moving_pixels_state.cpp @@ -58,6 +58,7 @@ MovingPixelsState::MovingPixelsState(Editor* editor, MouseMessage* msg, PixelsMo , m_editor(editor) , m_observingEditor(false) , m_discarded(false) + , m_renderTimer(50) { // MovingPixelsState needs a selection tool to avoid problems // sharing the extra cel between the drawing cursor preview and the @@ -80,6 +81,8 @@ MovingPixelsState::MovingPixelsState(Editor* editor, MouseMessage* msg, PixelsMo } onTransparentColorChange(); + m_renderTimer.Tick.connect([this]{ onRenderTimer(); }); + // Hook BeforeCommandExecution signal so we know if the user wants // to execute other command, so we can drop pixels. m_ctxConn = @@ -114,6 +117,7 @@ MovingPixelsState::~MovingPixelsState() removePixelsMovement(); removeAsEditorObserver(); + m_renderTimer.stop(); m_editor->manager()->removeMessageFilter(kKeyDownMessage, m_editor); m_editor->manager()->removeMessageFilter(kKeyUpMessage, m_editor); @@ -164,6 +168,8 @@ EditorState::LeaveAction MovingPixelsState::onLeaveState(Editor* editor, EditorS ASSERT(m_pixelsMovement); ASSERT(editor == m_editor); + onRenderTimer(); + // If we are changing to another state, we've to drop the image. if (m_pixelsMovement->isDragging()) m_pixelsMovement->dropImageTemporarily(); @@ -341,6 +347,9 @@ bool MovingPixelsState::onMouseMove(Editor* editor, MouseMessage* msg) // If there is a button pressed if (m_pixelsMovement->isDragging()) { + m_renderTimer.start(); + m_pixelsMovement->setFastMode(true); + // Auto-scroll gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir); @@ -657,6 +666,12 @@ void MovingPixelsState::onTransparentColorChange() Preferences::instance().selection.transparentColor()); } +void MovingPixelsState::onRenderTimer() +{ + m_pixelsMovement->setFastMode(false); + m_renderTimer.stop(); +} + void MovingPixelsState::onDropPixels(ContextBarObserver::DropAction action) { if (!isActiveEditor()) diff --git a/src/app/ui/editor/moving_pixels_state.h b/src/app/ui/editor/moving_pixels_state.h index 2cf479fb5..dcdd683dd 100644 --- a/src/app/ui/editor/moving_pixels_state.h +++ b/src/app/ui/editor/moving_pixels_state.h @@ -16,6 +16,7 @@ #include "app/ui/editor/standby_state.h" #include "app/ui/status_bar.h" #include "obs/connection.h" +#include "ui/timer.h" namespace doc { class Image; @@ -68,6 +69,7 @@ namespace app { private: void onTransparentColorChange(); + void onRenderTimer(); // ContextObserver void onBeforeCommandExecution(CommandExecutionEvent& ev); @@ -90,6 +92,8 @@ namespace app { // used to remove the dragged image). bool m_discarded; + ui::Timer m_renderTimer; + obs::connection m_ctxConn; obs::connection m_opaqueConn; obs::connection m_transparentConn; diff --git a/src/app/ui/editor/pixels_movement.cpp b/src/app/ui/editor/pixels_movement.cpp index 75790eeb9..6333f9875 100644 --- a/src/app/ui/editor/pixels_movement.cpp +++ b/src/app/ui/editor/pixels_movement.cpp @@ -131,6 +131,8 @@ PixelsMovement::PixelsMovement( , m_opaque(false) , m_maskColor(m_site.sprite()->transparentColor()) , m_canHandleFrameChange(false) + , m_fastMode(false) + , m_needsRotSpriteRedraw(false) { Transformation transform(mask->bounds()); set_pivot_from_preferences(transform); @@ -170,6 +172,17 @@ PixelsMovement::PixelsMovement( } } +void PixelsMovement::setFastMode(const bool fastMode) +{ + bool redraw = (m_fastMode && !fastMode); + m_fastMode = fastMode; + if (m_needsRotSpriteRedraw && redraw) { + redrawExtraImage(); + update_screen_for_document(m_document); + m_needsRotSpriteRedraw = false; + } +} + void PixelsMovement::flipImage(doc::algorithm::FlipType flipType) { m_innerCmds.push_back(InnerCmd::MakeFlip(flipType)); @@ -265,14 +278,7 @@ void PixelsMovement::cutMask() void PixelsMovement::copyMask() { - // Hide the mask (do not deselect it, it will be moved them using - // m_transaction.setMaskPosition) - Mask emptyMask; - { - ContextWriter writer(m_reader, 1000); - m_document->generateMaskBoundaries(&emptyMask); - update_screen_for_document(m_document); - } + hideDocumentMask(); } void PixelsMovement::catchImage(const gfx::Point& pos, HandleType handle) @@ -292,14 +298,7 @@ void PixelsMovement::catchImageAgain(const gfx::Point& pos, HandleType handle) m_catchPos = pos; m_handle = handle; - // Hide the mask (do not deselect it, it will be moved them using - // m_transaction.setMaskPosition) - Mask emptyMask; - { - ContextWriter writer(m_reader, 1000); - m_document->generateMaskBoundaries(&emptyMask); - update_screen_for_document(m_document); - } + hideDocumentMask(); } void PixelsMovement::moveImage(const gfx::Point& pos, MoveModifier moveModifier) @@ -862,6 +861,12 @@ void PixelsMovement::drawParallelogram( rotAlgo = tools::RotationAlgorithm::FAST; } + // Don't use RotSprite if we are in "fast mode" + if (rotAlgo == tools::RotationAlgorithm::ROTSPRITE && m_fastMode) { + m_needsRotSpriteRedraw = true; + rotAlgo = tools::RotationAlgorithm::FAST; + } + retry:; // In case that we don't have enough memory for RotSprite // we can try with the fast algorithm anyway. @@ -931,6 +936,12 @@ void PixelsMovement::updateDocumentMask() m_document->generateMaskBoundaries(m_currentMask.get()); } +void PixelsMovement::hideDocumentMask() +{ + m_document->destroyMaskBoundaries(); + update_screen_for_document(m_document); +} + void PixelsMovement::flipOriginalImage(const doc::algorithm::FlipType flipType) { // Flip the image. diff --git a/src/app/ui/editor/pixels_movement.h b/src/app/ui/editor/pixels_movement.h index 1eb02bfc1..60e486f1f 100644 --- a/src/app/ui/editor/pixels_movement.h +++ b/src/app/ui/editor/pixels_movement.h @@ -71,6 +71,8 @@ namespace app { HandleType handle() const { return m_handle; } bool canHandleFrameChange() const { return m_canHandleFrameChange; } + void setFastMode(const bool fastMode); + void trim(); void cutMask(); void copyMask(); @@ -135,6 +137,7 @@ namespace app { const Transformation::Corners& corners, const gfx::Point& leftTop); void updateDocumentMask(); + void hideDocumentMask(); void flipOriginalImage(const doc::algorithm::FlipType flipType); void shiftOriginalImage(const int dx, const int dy, @@ -166,6 +169,11 @@ namespace app { ExtraCelRef m_extraCel; bool m_canHandleFrameChange; + // Fast mode is used to give a faster feedback to the user + // avoiding RotSprite on each mouse movement. + bool m_fastMode; + bool m_needsRotSpriteRedraw; + // Commands used in the interaction with the transformed pixels. // This is used to re-create the whole interaction on each // modified cel when we are modifying multiples cels at the same