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.
This commit is contained in:
David Capello 2019-09-05 20:03:16 -03:00
parent d03f5e9145
commit 0b44b83cf3
6 changed files with 61 additions and 16 deletions

View File

@ -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();

View File

@ -135,6 +135,7 @@ namespace app {
//////////////////////////////////////////////////////////////////////
// Boundaries
void destroyMaskBoundaries();
void generateMaskBoundaries(const Mask* mask = nullptr);
const MaskBoundaries* getMaskBoundaries() const {

View File

@ -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())

View File

@ -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;

View File

@ -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.

View File

@ -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