1 | // Aseprite |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/app.h" |
13 | #include "app/commands/command.h" |
14 | #include "app/commands/filters/filter_manager_impl.h" |
15 | #include "app/commands/filters/filter_window.h" |
16 | #include "app/commands/filters/filter_worker.h" |
17 | #include "app/commands/new_params.h" |
18 | #include "app/context.h" |
19 | #include "app/doc.h" |
20 | #include "app/find_widget.h" |
21 | #include "app/ini_file.h" |
22 | #include "app/load_widget.h" |
23 | #include "app/pref/preferences.h" |
24 | #include "doc/mask.h" |
25 | #include "doc/sprite.h" |
26 | #include "filters/median_filter.h" |
27 | #include "ui/button.h" |
28 | #include "ui/entry.h" |
29 | #include "ui/grid.h" |
30 | #include "ui/widget.h" |
31 | #include "ui/window.h" |
32 | |
33 | #include "despeckle.xml.h" |
34 | |
35 | #include <stdio.h> |
36 | |
37 | namespace app { |
38 | |
39 | using namespace filters; |
40 | |
41 | struct DespeckleParams : public NewParams { |
42 | Param<bool> ui { this, true, "ui" }; |
43 | Param<filters::Target> channels { this, 0, "channels" }; |
44 | Param<int> width { this, 3, "width" }; |
45 | Param<int> height { this, 3, "height" }; |
46 | Param<filters::TiledMode> tiledMode { this, filters::TiledMode::NONE, "tiledMode" }; |
47 | }; |
48 | |
49 | #ifdef ENABLE_UI |
50 | |
51 | static const char* ConfigSection = "Despeckle" ; |
52 | |
53 | class DespeckleWindow : public FilterWindow { |
54 | public: |
55 | DespeckleWindow(MedianFilter& filter, FilterManagerImpl& filterMgr) |
56 | : FilterWindow("Median Blur" , ConfigSection, &filterMgr, |
57 | WithChannelsSelector, |
58 | WithTiledCheckBox, |
59 | filter.getTiledMode()) |
60 | , m_filter(filter) |
61 | , m_controlsWidget(new gen::Despeckle) |
62 | , m_widthEntry(m_controlsWidget->width()) |
63 | , m_heightEntry(m_controlsWidget->height()) |
64 | { |
65 | getContainer()->addChild(m_controlsWidget.get()); |
66 | |
67 | m_widthEntry->setTextf("%d" , m_filter.getWidth()); |
68 | m_heightEntry->setTextf("%d" , m_filter.getHeight()); |
69 | |
70 | m_widthEntry->Change.connect(&DespeckleWindow::onSizeChange, this); |
71 | m_heightEntry->Change.connect(&DespeckleWindow::onSizeChange, this); |
72 | } |
73 | |
74 | private: |
75 | void onSizeChange() { |
76 | gfx::Size newSize(m_widthEntry->textInt(), |
77 | m_heightEntry->textInt()); |
78 | |
79 | // Avoid negative numbers |
80 | newSize.w = std::clamp(newSize.w, 1, 100); |
81 | newSize.h = std::clamp(newSize.h, 1, 100); |
82 | |
83 | // If we had a previous filter preview running in the background, |
84 | // we explicitly request it be stopped. Otherwise, changing the |
85 | // size of the filter would cause a race condition on |
86 | // MedianFilter::m_channel field. |
87 | stopPreview(); |
88 | |
89 | m_filter.setSize(newSize.w, newSize.h); |
90 | restartPreview(); |
91 | } |
92 | |
93 | void setupTiledMode(TiledMode tiledMode) override { |
94 | m_filter.setTiledMode(tiledMode); |
95 | } |
96 | |
97 | MedianFilter& m_filter; |
98 | std::unique_ptr<gen::Despeckle> m_controlsWidget; |
99 | ExprEntry* m_widthEntry; |
100 | ExprEntry* m_heightEntry; |
101 | }; |
102 | |
103 | #endif // ENABLE_UI |
104 | |
105 | class DespeckleCommand : public CommandWithNewParams<DespeckleParams> { |
106 | public: |
107 | DespeckleCommand(); |
108 | |
109 | protected: |
110 | bool onEnabled(Context* context) override; |
111 | void onExecute(Context* context) override; |
112 | }; |
113 | |
114 | DespeckleCommand::DespeckleCommand() |
115 | : CommandWithNewParams<DespeckleParams>(CommandId::Despeckle(), CmdRecordableFlag) |
116 | { |
117 | } |
118 | |
119 | bool DespeckleCommand::onEnabled(Context* context) |
120 | { |
121 | return context->checkFlags(ContextFlags::ActiveDocumentIsWritable | |
122 | ContextFlags::HasActiveSprite); |
123 | } |
124 | |
125 | void DespeckleCommand::onExecute(Context* context) |
126 | { |
127 | #ifdef ENABLE_UI |
128 | const bool ui = (params().ui() && context->isUIAvailable()); |
129 | #endif |
130 | |
131 | MedianFilter filter; |
132 | filter.setSize(3, 3); // Default size |
133 | |
134 | FilterManagerImpl filterMgr(context, &filter); |
135 | filterMgr.setTarget(TARGET_RED_CHANNEL | |
136 | TARGET_GREEN_CHANNEL | |
137 | TARGET_BLUE_CHANNEL | |
138 | TARGET_GRAY_CHANNEL); |
139 | |
140 | #ifdef ENABLE_UI |
141 | if (ui) { |
142 | DocumentPreferences& docPref = Preferences::instance() |
143 | .document(context->activeDocument()); |
144 | filter.setTiledMode((filters::TiledMode)docPref.tiled.mode()); |
145 | filter.setSize(get_config_int(ConfigSection, "Width" , 3), |
146 | get_config_int(ConfigSection, "Height" , 3)); |
147 | } |
148 | #endif |
149 | |
150 | if (params().width.isSet()) filter.setSize(params().width(), filter.getHeight()); |
151 | if (params().height.isSet()) filter.setSize(filter.getWidth(), params().height()); |
152 | if (params().channels.isSet()) filterMgr.setTarget(params().channels()); |
153 | if (params().tiledMode.isSet()) filter.setTiledMode(params().tiledMode()); |
154 | |
155 | #ifdef ENABLE_UI |
156 | if (ui) { |
157 | DespeckleWindow window(filter, filterMgr); |
158 | if (window.doModal()) { |
159 | set_config_int(ConfigSection, "Width" , filter.getWidth()); |
160 | set_config_int(ConfigSection, "Height" , filter.getHeight()); |
161 | } |
162 | } |
163 | else |
164 | #endif // ENABLE_UI |
165 | { |
166 | start_filter_worker(&filterMgr); |
167 | } |
168 | } |
169 | |
170 | Command* CommandFactory::createDespeckleCommand() |
171 | { |
172 | return new DespeckleCommand; |
173 | } |
174 | |
175 | } // namespace app |
176 | |