Add support to call ReplaceColor from scripts when the UI is disabled (e.g. from CLI)

This commit is contained in:
David Capello 2019-07-16 16:36:09 -03:00
parent 9143523827
commit 814250e325
9 changed files with 150 additions and 63 deletions

View File

@ -300,14 +300,11 @@ if(ENABLE_UI)
commands/filters/cmd_hue_saturation.cpp
commands/filters/cmd_invert_color.cpp
commands/filters/cmd_outline.cpp
commands/filters/cmd_replace_color.cpp
commands/filters/color_curve_editor.cpp
commands/filters/convolution_matrix_stock.cpp
commands/filters/filter_manager_impl.cpp
commands/filters/filter_preview.cpp
commands/filters/filter_target_buttons.cpp
commands/filters/filter_window.cpp
commands/filters/filter_worker.cpp
file_selector.cpp
modules/editors.cpp
modules/gfx.cpp
@ -523,6 +520,9 @@ add_library(app-lib
commands/cmd_undo.cpp
commands/command.cpp
commands/commands.cpp
commands/filters/cmd_replace_color.cpp
commands/filters/filter_manager_impl.cpp
commands/filters/filter_worker.cpp
commands/move_thing.cpp
commands/new_params.cpp
commands/quick_command.cpp

View File

@ -23,6 +23,7 @@ FOR_EACH_COMMAND(OpenFile)
FOR_EACH_COMMAND(PaletteSize)
FOR_EACH_COMMAND(Redo)
FOR_EACH_COMMAND(RemoveLayer)
FOR_EACH_COMMAND(ReplaceColor)
FOR_EACH_COMMAND(SaveFile)
FOR_EACH_COMMAND(SaveFileAs)
FOR_EACH_COMMAND(SaveFileCopyAs)
@ -119,7 +120,6 @@ FOR_EACH_COMMAND(RemoveFrameTag)
FOR_EACH_COMMAND(RemoveSlice)
FOR_EACH_COMMAND(ReopenClosedFile)
FOR_EACH_COMMAND(RepeatLastExport)
FOR_EACH_COMMAND(ReplaceColor)
FOR_EACH_COMMAND(ReselectMask)
FOR_EACH_COMMAND(ReverseFrames)
FOR_EACH_COMMAND(Rotate)

View File

@ -24,6 +24,7 @@
#include "app/find_widget.h"
#include "app/ini_file.h"
#include "app/load_widget.h"
#include "app/pref/preferences.h"
#include "app/site.h"
#include "app/ui/color_bar.h"
#include "app/ui/color_button.h"
@ -38,7 +39,9 @@
namespace app {
#ifdef ENABLE_UI
static const char* ConfigSection = "ReplaceColor";
#endif
struct ReplaceColorParams : public NewParams {
Param<bool> ui { this, true, "ui" };
@ -73,6 +76,8 @@ private:
app::Color m_to;
};
#ifdef ENABLE_UI
class ReplaceColorWindow : public FilterWindow {
public:
ReplaceColorWindow(ReplaceColorFilterWrapper& filter, FilterManagerImpl& filterMgr)
@ -138,6 +143,8 @@ private:
ui::Slider* m_toleranceSlider;
};
#endif // ENABLE_UI
class ReplaceColorCommand : public CommandWithNewParams<ReplaceColorParams> {
public:
ReplaceColorCommand();
@ -160,7 +167,9 @@ bool ReplaceColorCommand::onEnabled(Context* context)
void ReplaceColorCommand::onExecute(Context* context)
{
#ifdef ENABLE_UI
const bool ui = (params().ui() && context->isUIAvailable());
#endif
Site site = context->activeSite();
ReplaceColorFilterWrapper filter(site.layer());
@ -174,14 +183,17 @@ void ReplaceColorCommand::onExecute(Context* context)
TARGET_GRAY_CHANNEL |
TARGET_ALPHA_CHANNEL);
#ifdef ENABLE_UI
if (ui) {
filter.setFrom(get_config_color(ConfigSection, "Color1", ColorBar::instance()->getFgColor()));
filter.setTo(get_config_color(ConfigSection, "Color2", ColorBar::instance()->getBgColor()));
filter.setFrom(get_config_color(ConfigSection, "Color1", Preferences::instance().colorBar.fgColor()));
filter.setTo(get_config_color(ConfigSection, "Color2", Preferences::instance().colorBar.bgColor()));
filter.setTolerance(get_config_int(ConfigSection, "Tolerance", 0));
}
else {
filter.setFrom(ColorBar::instance()->getFgColor());
filter.setTo(ColorBar::instance()->getBgColor());
else
#endif // ENABLE_UI
{
filter.setFrom(Preferences::instance().colorBar.fgColor());
filter.setTo(Preferences::instance().colorBar.bgColor());
filter.setTolerance(params().tolerance());
}
@ -190,6 +202,7 @@ void ReplaceColorCommand::onExecute(Context* context)
if (params().tolerance.isSet()) filter.setTolerance(params().tolerance());
if (params().target.isSet()) filterMgr.setTarget(params().target());
#ifdef ENABLE_UI
if (ui) {
ReplaceColorWindow window(filter, filterMgr);
if (window.doModal()) {
@ -198,7 +211,9 @@ void ReplaceColorCommand::onExecute(Context* context)
set_config_int(ConfigSection, "Tolerance", filter.getTolerance());
}
}
else {
else
#endif // ENABLE_UI
{
start_filter_worker(&filterMgr);
}
}

View File

@ -123,6 +123,8 @@ void FilterManagerImpl::begin()
updateBounds(m_mask);
}
#ifdef ENABLE_UI
void FilterManagerImpl::beginForPreview()
{
Doc* document = m_site.document();
@ -161,6 +163,8 @@ void FilterManagerImpl::beginForPreview()
}
}
#endif // ENABLE_UI
void FilterManagerImpl::end()
{
m_maskBits.unlock();
@ -244,7 +248,7 @@ void FilterManagerImpl::applyToTarget()
switch (m_celsTarget) {
case CelsTarget::Selected: {
auto range = App::instance()->timeline()->range();
auto range = m_site.range();
if (range.enabled()) {
for (Cel* cel : get_unlocked_unique_cels(m_site.sprite(), range)) {
if (!cel->layer()->isReference())
@ -332,6 +336,8 @@ void FilterManagerImpl::commitTransaction()
m_tx->commit();
}
#ifdef ENABLE_UI
void FilterManagerImpl::flush()
{
int h = m_row - m_nextRowToFlush;
@ -382,6 +388,8 @@ void FilterManagerImpl::disablePreview()
}
}
#endif // ENABLE_UI
const void* FilterManagerImpl::getSourceAddress()
{
return m_src->getPixelAddress(m_bounds.x, m_bounds.y+m_row);
@ -429,9 +437,11 @@ Palette* FilterManagerImpl::getNewPalette()
doc::PalettePicks FilterManagerImpl::getPalettePicks()
{
doc::PalettePicks picks;
#ifdef ENABLE_UI // TODO add palette entries in Site and use activeSite here
ColorBar::instance()
->getPaletteView()
->getSelectedEntries(picks);
#endif
return picks;
}
@ -495,12 +505,16 @@ void FilterManagerImpl::restoreSpritePalette()
m_site.sprite()->setPalette(m_oldPalette.get(), false);
}
#ifdef ENABLE_UI
void FilterManagerImpl::redrawColorPalette()
{
set_current_palette(getNewPalette(), false);
ColorBar::instance()->invalidate();
}
#endif // ENABLE_UI
bool FilterManagerImpl::isMaskActive() const
{
return m_site.document()->isMaskVisible();

View File

@ -83,7 +83,9 @@ namespace app {
void setCelsTarget(CelsTarget celsTarget);
void begin();
#ifdef ENABLE_UI
void beginForPreview();
#endif
void end();
bool applyStep();
void applyToTarget();
@ -97,10 +99,11 @@ namespace app {
doc::Image* destinationImage() const { return m_dst.get(); }
gfx::Point position() const { return gfx::Point(0, 0); }
#ifdef ENABLE_UI
// Updates the current editor to show the progress of the preview.
void flush();
void disablePreview();
#endif
// FilterManager implementation
const void* getSourceAddress() override;
@ -131,7 +134,10 @@ namespace app {
// modifies the palette).
bool paletteHasChanged();
void restoreSpritePalette();
#ifdef ENABLE_UI
void redrawColorPalette();
#endif
Context* m_context;
Site m_site;
@ -140,7 +146,9 @@ namespace app {
doc::ImageRef m_src;
doc::ImageRef m_dst;
int m_row;
#ifdef ENABLE_UI
int m_nextRowToFlush;
#endif
gfx::Rect m_bounds;
doc::Mask* m_mask;
std::unique_ptr<doc::Mask> m_previewMask;

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -25,13 +26,54 @@
#include <cstdlib>
#include <cstring>
#include <functional>
#include <thread>
namespace app {
using namespace base;
using namespace ui;
static const int kMonitoringPeriod = 100;
#ifdef ENABLE_UI
namespace {
const int kMonitoringPeriod = 100;
class FilterWorkerAlert {
public:
FilterWorkerAlert(std::function<void()>&& onTick)
: m_timer(kMonitoringPeriod)
, m_window(ui::Alert::create(Strings::alerts_applying_filter())) {
m_window->addProgress();
m_timer.Tick.connect(std::move(onTick));
m_timer.start();
}
void openAndWait() {
m_window->openWindowInForeground();
// Stop the monitoring timer.
m_timer.stop();
}
void close() {
m_window->closeWindow(nullptr);
}
void setProgress(double progress) {
m_window->setProgress(progress);
}
private:
ui::Timer m_timer; // Monitoring timer to update the progress-bar
AlertPtr m_window; // Alert for the user to cancel the filter-progress if he wants.
};
} // anonymous namespace
#endif // ENABLE_UI
// Applies filters in two threads: a background worker thread to
// modify the sprite, and the main thread to monitoring the progress
@ -50,12 +92,9 @@ public:
private:
void applyFilterInBackground();
#ifdef ENABLE_UI
void onMonitoringTick();
static void thread_proxy(void* data) {
FilterWorker* filterWorker = (FilterWorker*)data;
filterWorker->applyFilterInBackground();
}
#endif
FilterManagerImpl* m_filterMgr; // Effect to be applied.
base::mutex m_mutex; // Mutex to access to 'pos', 'done' and 'cancelled' fields in different threads.
@ -63,14 +102,14 @@ private:
bool m_done; // Was the effect completely applied?
bool m_cancelled; // Was the effect cancelled by the user?
bool m_abort; // An exception was thrown
ui::Timer m_timer; // Monitoring timer to update the progress-bar
AlertPtr m_alertWindow; // Alert for the user to cancel the filter-progress if he wants.
std::string m_error;
#ifdef ENABLE_UI
std::unique_ptr<FilterWorkerAlert> m_alert;
#endif
};
FilterWorker::FilterWorker(FilterManagerImpl* filterMgr)
: m_filterMgr(filterMgr)
, m_timer(kMonitoringPeriod)
{
m_filterMgr->setProgressDelegate(this);
@ -79,29 +118,36 @@ FilterWorker::FilterWorker(FilterManagerImpl* filterMgr)
m_cancelled = false;
m_abort = false;
m_alertWindow = ui::Alert::create(Strings::alerts_applying_filter());
m_alertWindow->addProgress();
m_timer.Tick.connect(&FilterWorker::onMonitoringTick, this);
m_timer.start();
#ifdef ENABLE_UI
if (Manager::getDefault())
m_alert.reset(new FilterWorkerAlert([this]{ onMonitoringTick(); }));
#endif
}
FilterWorker::~FilterWorker()
{
if (m_alertWindow)
m_alertWindow->closeWindow(NULL);
#ifdef ENABLE_UI
if (m_alert)
m_alert->close();
#endif
}
void FilterWorker::run()
{
// Launch the thread to apply the effect in background
base::thread thread(&FilterWorker::thread_proxy, this);
#ifdef ENABLE_UI
std::thread thread;
// Open the alert window in foreground (this is modal, locks the main thread)
m_alertWindow->openWindowInForeground();
// Stop the monitoring timer.
m_timer.stop();
if (m_alert) {
// Launch the thread to apply the effect in background
thread = std::thread([this]{ applyFilterInBackground(); });
m_alert->openAndWait();
}
else
#endif // ENABLE_UI
{
// Without UI? Apply filter from the main thread
applyFilterInBackground();
}
{
scoped_lock lock(m_mutex);
@ -111,8 +157,10 @@ void FilterWorker::run()
m_cancelled = true;
}
#ifdef ENABLE_UI
// Wait the `effect_bg' thread
thread.join();
if (thread.joinable())
thread.join();
if (!m_error.empty()) {
Console console;
@ -122,6 +170,7 @@ void FilterWorker::run()
StatusBar::instance()
->showTip(2500, "No unlocked layers to apply filter");
}
#endif // ENABLE_UI
}
// Called by FilterManagerImpl to informate the progress of the filter.
@ -168,19 +217,24 @@ void FilterWorker::applyFilterInBackground()
}
}
#ifdef ENABLE_UI
// Called by the GUI monitor (a timer in the gui module that is called
// every 100 milliseconds).
void FilterWorker::onMonitoringTick()
{
scoped_lock lock(m_mutex);
if (m_alertWindow)
m_alertWindow->setProgress(m_pos);
if (m_alert) {
m_alert->setProgress(m_pos);
if (m_done || m_abort)
m_alertWindow->closeWindow(NULL);
if (m_done || m_abort)
m_alert->close();
}
}
#endif
// Applies the filter in a background thread meanwhile a progress bar
// is shown to the user.
//

View File

@ -1,5 +1,6 @@
// Aseprite
// Copyright (c) 2001-2018 David Capello
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -61,4 +62,14 @@ Palette* Site::palette() const
return (m_sprite ? m_sprite->palette(m_frame): nullptr);
}
void Site::range(const DocRange& range)
{
m_range = range;
switch (range.type()) {
case DocRange::kCels: m_focus = Site::InCels; break;
case DocRange::kFrames: m_focus = Site::InFrames; break;
case DocRange::kLayers: m_focus = Site::InLayers; break;
}
}
} // namespace app

View File

@ -9,9 +9,8 @@
#define APP_SITE_H_INCLUDED
#pragma once
#include "app/doc_range.h"
#include "doc/frame.h"
#include "doc/selected_frames.h"
#include "doc/selected_layers.h"
#include "doc/selected_objects.h"
namespace doc {
@ -67,24 +66,17 @@ namespace app {
doc::Sprite* sprite() { return m_sprite; }
doc::Layer* layer() { return m_layer; }
doc::Cel* cel();
const DocRange& range() const { return m_range; }
void focus(Focus focus) { m_focus = focus; }
void document(Doc* document) { m_document = document; }
void sprite(doc::Sprite* sprite) { m_sprite = sprite; }
void layer(doc::Layer* layer) { m_layer = layer; }
void frame(doc::frame_t frame) { m_frame = frame; }
void range(const DocRange& range);
const doc::SelectedLayers& selectedLayers() const { return m_selectedLayers; }
doc::SelectedLayers& selectedLayers() { return m_selectedLayers; }
void selectedLayers(const doc::SelectedLayers& selectedLayers) {
m_selectedLayers = selectedLayers;
}
const doc::SelectedFrames& selectedFrames() const { return m_selectedFrames; }
doc::SelectedFrames& selectedFrames() { return m_selectedFrames; }
void selectedFrames(const doc::SelectedFrames& selectedFrames) {
m_selectedFrames = selectedFrames;
}
const doc::SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); }
const doc::SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); }
const doc::SelectedObjects& selectedSlices() const { return m_selectedSlices; }
doc::SelectedObjects& selectedSlices() { return m_selectedSlices; }
@ -102,8 +94,7 @@ namespace app {
doc::Sprite* m_sprite;
doc::Layer* m_layer;
doc::frame_t m_frame;
doc::SelectedLayers m_selectedLayers;
doc::SelectedFrames m_selectedFrames;
DocRange m_range;
doc::SelectedObjects m_selectedSlices;
};

View File

@ -291,13 +291,7 @@ void UIContext::onGetActiveSite(Site* site) const
Timeline* timeline = App::instance()->timeline();
if (timeline &&
timeline->range().enabled()) {
switch (timeline->range().type()) {
case DocRange::kCels: site->focus(Site::InCels); break;
case DocRange::kFrames: site->focus(Site::InFrames); break;
case DocRange::kLayers: site->focus(Site::InLayers); break;
}
site->selectedLayers(timeline->selectedLayers());
site->selectedFrames(timeline->selectedFrames());
site->range(timeline->range());
}
else {
ColorBar* colorBar = ColorBar::instance();