diff --git a/src/app/commands/filters/filter_manager_impl.cpp b/src/app/commands/filters/filter_manager_impl.cpp index 37c509109..5db826cc7 100644 --- a/src/app/commands/filters/filter_manager_impl.cpp +++ b/src/app/commands/filters/filter_manager_impl.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -110,7 +110,7 @@ void FilterManagerImpl::beginForPreview() m_previewMask->replace(m_site.sprite()->bounds()); } - m_row = 0; + m_row = m_nextRowToFlush = 0; m_mask = m_previewMask; { @@ -257,17 +257,24 @@ void FilterManagerImpl::applyToTarget() void FilterManagerImpl::flush() { - if (m_row >= 0) { + int h = m_row - m_nextRowToFlush; + + if (m_row >= 0 && h > 0) { Editor* editor = current_editor; + + // We expand the region one pixel at the top and bottom of the + // region [m_row,m_nextRowToFlush) to be updated on the screen to + // avoid screen artifacts when we apply filters like convolution + // matrices. gfx::Rect rect( editor->editorToScreen( gfx::Point( m_bounds.x, - m_bounds.y+m_row-1)), + m_bounds.y+m_nextRowToFlush-1)), gfx::Size( editor->projection().applyX(m_bounds.w), - (editor->projection().scaleY() >= 1 ? editor->projection().applyY(1): - editor->projection().removeY(1)))); + (editor->projection().scaleY() >= 1 ? editor->projection().applyY(h+2): + editor->projection().removeY(h+2)))); gfx::Region reg1(rect); gfx::Region reg2; @@ -275,6 +282,7 @@ void FilterManagerImpl::flush() reg1.createIntersection(reg1, reg2); editor->invalidateRegion(reg1); + m_nextRowToFlush = m_row+1; } } diff --git a/src/app/commands/filters/filter_manager_impl.h b/src/app/commands/filters/filter_manager_impl.h index 5b810ccf0..aeab3e2c9 100644 --- a/src/app/commands/filters/filter_manager_impl.h +++ b/src/app/commands/filters/filter_manager_impl.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -120,6 +120,7 @@ namespace app { doc::ImageRef m_src; doc::ImageRef m_dst; int m_row; + int m_nextRowToFlush; gfx::Rect m_bounds; doc::Mask* m_mask; base::UniquePtr m_previewMask; diff --git a/src/app/commands/filters/filter_preview.cpp b/src/app/commands/filters/filter_preview.cpp index 9d65cc582..c5cf4bf7d 100644 --- a/src/app/commands/filters/filter_preview.cpp +++ b/src/app/commands/filters/filter_preview.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2016 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -13,6 +13,8 @@ #include "app/commands/filters/filter_manager_impl.h" #include "app/modules/editors.h" #include "app/ui/editor/editor.h" +#include "base/bind.h" +#include "base/scoped_lock.h" #include "doc/layer.h" #include "doc/sprite.h" #include "ui/manager.h" @@ -28,6 +30,7 @@ FilterPreview::FilterPreview(FilterManagerImpl* filterMgr) : Widget(kGenericWidget) , m_filterMgr(filterMgr) , m_timer(1, this) + , m_filterThread(nullptr) { setVisible(false); } @@ -39,25 +42,32 @@ FilterPreview::~FilterPreview() void FilterPreview::stop() { - if (m_timer.isRunning()) { - ASSERT(m_filterMgr != NULL); - - m_filterMgr->end(); + { + base::scoped_lock lock(m_filterMgrMutex); + if (m_timer.isRunning()) { + ASSERT(m_filterMgr); + m_filterMgr->end(); + } } - m_filterMgr = NULL; m_timer.stop(); + + if (m_filterThread) { + m_filterThread->join(); + m_filterThread.reset(); + } } void FilterPreview::restartPreview() { - m_filterMgr->beginForPreview(); - m_timer.start(); -} + base::scoped_lock lock(m_filterMgrMutex); -FilterManagerImpl* FilterPreview::getFilterManager() const -{ - return m_filterMgr; + m_filterMgr->beginForPreview(); + m_filterIsDone = false; + m_filterThread.reset(new base::thread( + base::Bind(&FilterPreview::onFilterThread, this))); + + m_timer.start(); } bool FilterPreview::onProcessMessage(Message* msg) @@ -80,17 +90,30 @@ bool FilterPreview::onProcessMessage(Message* msg) m_timer.stop(); break; - case kTimerMessage: + case kTimerMessage: { + base::scoped_lock lock(m_filterMgrMutex); if (m_filterMgr) { - if (m_filterMgr->applyStep()) - m_filterMgr->flush(); - else + m_filterMgr->flush(); + if (m_filterIsDone) m_timer.stop(); } break; + } } return Widget::onProcessMessage(msg); } +// This is executed in other thread. +void FilterPreview::onFilterThread() +{ + while (!m_filterIsDone && m_timer.isRunning()) { + { + base::scoped_lock lock(m_filterMgrMutex); + m_filterIsDone = !m_filterMgr->applyStep(); + } + base::this_thread::yield(); + } +} + } // namespace app diff --git a/src/app/commands/filters/filter_preview.h b/src/app/commands/filters/filter_preview.h index 261aad428..cb41fb6a9 100644 --- a/src/app/commands/filters/filter_preview.h +++ b/src/app/commands/filters/filter_preview.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -8,6 +8,9 @@ #define APP_COMMANDS_FILTERS_FILTER_PREVIEW_H_INCLUDED #pragma once +#include "base/mutex.h" +#include "base/thread.h" +#include "base/unique_ptr.h" #include "ui/timer.h" #include "ui/widget.h" @@ -23,14 +26,18 @@ namespace app { void stop(); void restartPreview(); - FilterManagerImpl* getFilterManager() const; protected: bool onProcessMessage(ui::Message* msg) override; private: + void onFilterThread(); + FilterManagerImpl* m_filterMgr; ui::Timer m_timer; + base::mutex m_filterMgrMutex; + base::UniquePtr m_filterThread; + bool m_filterIsDone; }; } // namespace app diff --git a/src/app/commands/filters/filter_window.cpp b/src/app/commands/filters/filter_window.cpp index a2606c0c1..4997398fd 100644 --- a/src/app/commands/filters/filter_window.cpp +++ b/src/app/commands/filters/filter_window.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -121,6 +121,8 @@ void FilterWindow::restartPreview() void FilterWindow::setNewTarget(Target target) { + stopPreview(); + m_filterMgr->setTarget(target); m_targetButton.setTarget(target); } @@ -143,6 +145,8 @@ void FilterWindow::onShowPreview(Event& ev) // Called when the user changes the target-buttons. void FilterWindow::onTargetButtonChange() { + stopPreview(); + // Change the targets in the filter manager and restart the filter preview. m_filterMgr->setTarget(m_targetButton.getTarget()); restartPreview(); @@ -150,7 +154,8 @@ void FilterWindow::onTargetButtonChange() void FilterWindow::onTiledChange() { - ASSERT(m_tiledCheck != NULL); + ASSERT(m_tiledCheck); + stopPreview(); // Call derived class implementation of setupTiledMode() so the // filter is modified. @@ -162,4 +167,9 @@ void FilterWindow::onTiledChange() restartPreview(); } +void FilterWindow::stopPreview() +{ + m_preview.stop(); +} + } // namespace app diff --git a/src/app/commands/filters/filter_window.h b/src/app/commands/filters/filter_window.h index 6c8149270..86a5b1d36 100644 --- a/src/app/commands/filters/filter_window.h +++ b/src/app/commands/filters/filter_window.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2001-2015 David Capello +// Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. @@ -62,6 +62,8 @@ namespace app { virtual void setupTiledMode(TiledMode tiledMode) { } private: + void stopPreview(); + const char* m_cfgSection; FilterManagerImpl* m_filterMgr; ui::Box m_hbox;