1 | // Aseprite |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // |
4 | // This program is distributed under the terms of |
5 | // the End-User License Agreement for Aseprite. |
6 | |
7 | #ifdef HAVE_CONFIG_H |
8 | #include "config.h" |
9 | #endif |
10 | |
11 | #include "app/commands/new_params.h" |
12 | |
13 | #include "app/color.h" |
14 | #include "app/doc_exporter.h" |
15 | #include "app/sprite_sheet_type.h" |
16 | #include "app/tools/ink_type.h" |
17 | #include "base/convert_to.h" |
18 | #include "base/split_string.h" |
19 | #include "base/string.h" |
20 | #include "doc/algorithm/resize_image.h" |
21 | #include "doc/anidir.h" |
22 | #include "doc/color_mode.h" |
23 | #include "doc/rgbmap_algorithm.h" |
24 | #include "filters/color_curve.h" |
25 | #include "filters/hue_saturation_filter.h" |
26 | #include "filters/outline_filter.h" |
27 | #include "filters/tiled_mode.h" |
28 | #include "gfx/rect.h" |
29 | #include "gfx/size.h" |
30 | |
31 | #ifdef ENABLE_SCRIPTING |
32 | #include "app/script/engine.h" |
33 | #include "app/script/luacpp.h" |
34 | #include "app/script/values.h" |
35 | #endif |
36 | |
37 | namespace app { |
38 | |
39 | ////////////////////////////////////////////////////////////////////// |
40 | // Convert values from strings (e.g. useful for values from gui.xml) |
41 | ////////////////////////////////////////////////////////////////////// |
42 | |
43 | template<> |
44 | void Param<bool>::fromString(const std::string& value) |
45 | { |
46 | setValue(value == "1" || value == "true" ); |
47 | } |
48 | |
49 | template<> |
50 | void Param<int>::fromString(const std::string& value) |
51 | { |
52 | setValue(base::convert_to<int>(value)); |
53 | } |
54 | |
55 | template<> |
56 | void Param<double>::fromString(const std::string& value) |
57 | { |
58 | setValue(base::convert_to<double>(value)); |
59 | } |
60 | |
61 | template<> |
62 | void Param<std::string>::fromString(const std::string& value) |
63 | { |
64 | setValue(value); |
65 | } |
66 | |
67 | template<> |
68 | void Param<gfx::Size>::fromString(const std::string& value) |
69 | { |
70 | gfx::Size size; |
71 | std::vector<std::string> parts; |
72 | base::split_string(value, parts, "," ); |
73 | if (parts.size() == 2) { |
74 | size.w = base::convert_to<int>(parts[0]); |
75 | size.h = base::convert_to<int>(parts[1]); |
76 | } |
77 | setValue(size); |
78 | } |
79 | |
80 | template<> |
81 | void Param<gfx::Rect>::fromString(const std::string& value) |
82 | { |
83 | gfx::Rect rect; |
84 | std::vector<std::string> parts; |
85 | base::split_string(value, parts, "," ); |
86 | if (parts.size() == 4) { |
87 | rect.x = base::convert_to<int>(parts[0]); |
88 | rect.y = base::convert_to<int>(parts[1]); |
89 | rect.w = base::convert_to<int>(parts[2]); |
90 | rect.h = base::convert_to<int>(parts[3]); |
91 | } |
92 | setValue(rect); |
93 | } |
94 | |
95 | template<> |
96 | void Param<doc::algorithm::ResizeMethod>::fromString(const std::string& value) |
97 | { |
98 | if (base::utf8_icmp(value, "bilinear" ) == 0) |
99 | setValue(doc::algorithm::RESIZE_METHOD_BILINEAR); |
100 | else if (base::utf8_icmp(value, "rotsprite" ) == 0) |
101 | setValue(doc::algorithm::RESIZE_METHOD_ROTSPRITE); |
102 | else |
103 | setValue(doc::algorithm::ResizeMethod::RESIZE_METHOD_NEAREST_NEIGHBOR); |
104 | } |
105 | |
106 | template<> |
107 | void Param<app::SpriteSheetType>::fromString(const std::string& value) |
108 | { |
109 | if (value == "horizontal" ) |
110 | setValue(app::SpriteSheetType::Horizontal); |
111 | else if (value == "vertical" ) |
112 | setValue(app::SpriteSheetType::Vertical); |
113 | else if (value == "rows" ) |
114 | setValue(app::SpriteSheetType::Rows); |
115 | else if (value == "columns" ) |
116 | setValue(app::SpriteSheetType::Columns); |
117 | else if (value == "packed" ) |
118 | setValue(app::SpriteSheetType::Packed); |
119 | else |
120 | setValue(app::SpriteSheetType::None); |
121 | } |
122 | |
123 | template<> |
124 | void Param<app::SpriteSheetDataFormat>::fromString(const std::string& value) |
125 | { |
126 | // JsonArray, json-array, json_array, etc. |
127 | if (base::utf8_icmp(value, "JsonArray" ) == 0 || |
128 | base::utf8_icmp(value, "json-array" ) == 0 || |
129 | base::utf8_icmp(value, "json_array" ) == 0) |
130 | setValue(app::SpriteSheetDataFormat::JsonArray); |
131 | else |
132 | setValue(app::SpriteSheetDataFormat::JsonHash); |
133 | } |
134 | |
135 | template<> |
136 | void Param<doc::ColorMode>::fromString(const std::string& value) |
137 | { |
138 | if (base::utf8_icmp(value, "rgb" ) == 0) |
139 | setValue(doc::ColorMode::RGB); |
140 | else if (base::utf8_icmp(value, "gray" ) == 0 || |
141 | base::utf8_icmp(value, "grayscale" ) == 0) |
142 | setValue(doc::ColorMode::GRAYSCALE); |
143 | else if (base::utf8_icmp(value, "indexed" ) == 0) |
144 | setValue(doc::ColorMode::INDEXED); |
145 | else |
146 | setValue(doc::ColorMode::RGB); |
147 | } |
148 | |
149 | template<> |
150 | void Param<doc::AniDir>::fromString(const std::string& value) |
151 | { |
152 | setValue(convert_string_to_anidir(value)); |
153 | } |
154 | |
155 | template<> |
156 | void Param<app::Color>::fromString(const std::string& value) |
157 | { |
158 | setValue(app::Color::fromString(value)); |
159 | } |
160 | |
161 | template<> |
162 | void Param<filters::TiledMode>::fromString(const std::string& value) |
163 | { |
164 | if (base::utf8_icmp(value, "both" ) == 0) |
165 | setValue(filters::TiledMode::BOTH); |
166 | else if (base::utf8_icmp(value, "x" ) == 0) |
167 | setValue(filters::TiledMode::X_AXIS); |
168 | else if (base::utf8_icmp(value, "y" ) == 0) |
169 | setValue(filters::TiledMode::Y_AXIS); |
170 | else |
171 | setValue(filters::TiledMode::NONE); |
172 | } |
173 | |
174 | template<> |
175 | void Param<filters::OutlineFilter::Place>::fromString(const std::string& value) |
176 | { |
177 | if (base::utf8_icmp(value, "inside" ) == 0) |
178 | setValue(filters::OutlineFilter::Place::Inside); |
179 | else |
180 | setValue(filters::OutlineFilter::Place::Outside); |
181 | } |
182 | |
183 | template<> |
184 | void Param<filters::OutlineFilter::Matrix>::fromString(const std::string& value) |
185 | { |
186 | if (base::utf8_icmp(value, "circle" ) == 0) |
187 | setValue(filters::OutlineFilter::Matrix::Circle); |
188 | else if (base::utf8_icmp(value, "square" ) == 0) |
189 | setValue(filters::OutlineFilter::Matrix::Square); |
190 | else if (base::utf8_icmp(value, "horizontal" ) == 0) |
191 | setValue(filters::OutlineFilter::Matrix::Horizontal); |
192 | else if (base::utf8_icmp(value, "vertical" ) == 0) |
193 | setValue(filters::OutlineFilter::Matrix::Vertical); |
194 | else |
195 | setValue((filters::OutlineFilter::Matrix)0); |
196 | } |
197 | |
198 | template<> |
199 | void Param<filters::HueSaturationFilter::Mode>::fromString(const std::string& value) |
200 | { |
201 | if (base::utf8_icmp(value, "hsv" ) == 0 || |
202 | base::utf8_icmp(value, "hsv_mul" ) == 0) |
203 | setValue(filters::HueSaturationFilter::Mode::HSV_MUL); |
204 | else if (base::utf8_icmp(value, "hsv_add" ) == 0) |
205 | setValue(filters::HueSaturationFilter::Mode::HSV_ADD); |
206 | else if (base::utf8_icmp(value, "hsl_add" ) == 0) |
207 | setValue(filters::HueSaturationFilter::Mode::HSL_ADD); |
208 | else |
209 | setValue(filters::HueSaturationFilter::Mode::HSL_MUL); |
210 | } |
211 | |
212 | template<> |
213 | void Param<filters::ColorCurve>::fromString(const std::string& value) |
214 | { |
215 | filters::ColorCurve curve; |
216 | std::vector<std::string> parts; |
217 | base::split_string(value, parts, "," ); |
218 | for (int i=0; i+1<int(parts.size()); i+=2) { |
219 | curve.addPoint( |
220 | gfx::Point( |
221 | base::convert_to<int>(parts[i]), |
222 | base::convert_to<int>(parts[i+1]))); |
223 | } |
224 | setValue(curve); |
225 | } |
226 | |
227 | template<> |
228 | void Param<tools::InkType>::fromString(const std::string& value) |
229 | { |
230 | setValue(tools::string_id_to_ink_type(value)); |
231 | } |
232 | |
233 | template<> |
234 | void Param<doc::RgbMapAlgorithm>::fromString(const std::string& value) |
235 | { |
236 | if (base::utf8_icmp(value, "octree" ) == 0) |
237 | setValue(doc::RgbMapAlgorithm::OCTREE); |
238 | else if (base::utf8_icmp(value, "rgb5a3" ) == 0) |
239 | setValue(doc::RgbMapAlgorithm::RGB5A3); |
240 | else |
241 | setValue(doc::RgbMapAlgorithm::DEFAULT); |
242 | } |
243 | |
244 | ////////////////////////////////////////////////////////////////////// |
245 | // Convert values from Lua |
246 | ////////////////////////////////////////////////////////////////////// |
247 | |
248 | #ifdef ENABLE_SCRIPTING |
249 | |
250 | template<> |
251 | void Param<bool>::fromLua(lua_State* L, int index) |
252 | { |
253 | setValue(lua_toboolean(L, index)); |
254 | } |
255 | |
256 | template<> |
257 | void Param<int>::fromLua(lua_State* L, int index) |
258 | { |
259 | setValue(lua_tointeger(L, index)); |
260 | } |
261 | |
262 | template<> |
263 | void Param<double>::fromLua(lua_State* L, int index) |
264 | { |
265 | setValue(lua_tonumber(L, index)); |
266 | } |
267 | |
268 | template<> |
269 | void Param<std::string>::fromLua(lua_State* L, int index) |
270 | { |
271 | if (const char* s = lua_tostring(L, index)) |
272 | setValue(s); |
273 | else |
274 | setValue(std::string()); |
275 | } |
276 | |
277 | template<> |
278 | void Param<gfx::Size>::fromLua(lua_State* L, int index) |
279 | { |
280 | setValue(script::convert_args_into_size(L, index)); |
281 | } |
282 | |
283 | template<> |
284 | void Param<gfx::Rect>::fromLua(lua_State* L, int index) |
285 | { |
286 | setValue(script::convert_args_into_rect(L, index)); |
287 | } |
288 | |
289 | template<> |
290 | void Param<doc::algorithm::ResizeMethod>::fromLua(lua_State* L, int index) |
291 | { |
292 | if (lua_type(L, index) == LUA_TSTRING) |
293 | fromString(lua_tostring(L, index)); |
294 | else |
295 | setValue((doc::algorithm::ResizeMethod)lua_tointeger(L, index)); |
296 | } |
297 | |
298 | template<> |
299 | void Param<app::SpriteSheetType>::fromLua(lua_State* L, int index) |
300 | { |
301 | if (lua_type(L, index) == LUA_TSTRING) |
302 | fromString(lua_tostring(L, index)); |
303 | else |
304 | setValue((app::SpriteSheetType)lua_tointeger(L, index)); |
305 | } |
306 | |
307 | template<> |
308 | void Param<app::SpriteSheetDataFormat>::fromLua(lua_State* L, int index) |
309 | { |
310 | if (lua_type(L, index) == LUA_TSTRING) |
311 | fromString(lua_tostring(L, index)); |
312 | else |
313 | setValue((app::SpriteSheetDataFormat)lua_tointeger(L, index)); |
314 | } |
315 | |
316 | template<> |
317 | void Param<doc::ColorMode>::fromLua(lua_State* L, int index) |
318 | { |
319 | if (lua_type(L, index) == LUA_TSTRING) |
320 | fromString(lua_tostring(L, index)); |
321 | else |
322 | setValue((doc::ColorMode)lua_tointeger(L, index)); |
323 | } |
324 | |
325 | template<> |
326 | void Param<doc::AniDir>::fromLua(lua_State* L, int index) |
327 | { |
328 | if (lua_type(L, index) == LUA_TSTRING) |
329 | fromString(lua_tostring(L, index)); |
330 | else |
331 | setValue((doc::AniDir)lua_tointeger(L, index)); |
332 | } |
333 | |
334 | template<> |
335 | void Param<app::Color>::fromLua(lua_State* L, int index) |
336 | { |
337 | setValue(script::convert_args_into_color(L, index)); |
338 | } |
339 | |
340 | template<> |
341 | void Param<filters::TiledMode>::fromLua(lua_State* L, int index) |
342 | { |
343 | if (lua_type(L, index) == LUA_TSTRING) |
344 | fromString(lua_tostring(L, index)); |
345 | else |
346 | setValue((filters::TiledMode)lua_tointeger(L, index)); |
347 | } |
348 | |
349 | template<> |
350 | void Param<filters::OutlineFilter::Place>::fromLua(lua_State* L, int index) |
351 | { |
352 | if (lua_type(L, index) == LUA_TSTRING) |
353 | fromString(lua_tostring(L, index)); |
354 | else |
355 | setValue((filters::OutlineFilter::Place)lua_tointeger(L, index)); |
356 | } |
357 | |
358 | template<> |
359 | void Param<filters::OutlineFilter::Matrix>::fromLua(lua_State* L, int index) |
360 | { |
361 | if (lua_type(L, index) == LUA_TSTRING) |
362 | fromString(lua_tostring(L, index)); |
363 | else |
364 | setValue((filters::OutlineFilter::Matrix)lua_tointeger(L, index)); |
365 | } |
366 | |
367 | template<> |
368 | void Param<filters::HueSaturationFilter::Mode>::fromLua(lua_State* L, int index) |
369 | { |
370 | if (lua_type(L, index) == LUA_TSTRING) |
371 | fromString(lua_tostring(L, index)); |
372 | else |
373 | setValue((filters::HueSaturationFilter::Mode)lua_tointeger(L, index)); |
374 | } |
375 | |
376 | template<> |
377 | void Param<filters::ColorCurve>::fromLua(lua_State* L, int index) |
378 | { |
379 | if (lua_type(L, index) == LUA_TSTRING) |
380 | fromString(lua_tostring(L, index)); |
381 | else if (lua_type(L, index) == LUA_TTABLE) { |
382 | filters::ColorCurve curve; |
383 | lua_pushnil(L); |
384 | while (lua_next(L, -2) != 0) { |
385 | gfx::Point pt = script::convert_args_into_point(L, -1); |
386 | curve.addPoint(pt); |
387 | lua_pop(L, 1); |
388 | } |
389 | setValue(curve); |
390 | } |
391 | } |
392 | |
393 | template<> |
394 | void Param<tools::InkType>::fromLua(lua_State* L, int index) |
395 | { |
396 | setValue(script::get_value_from_lua<tools::InkType>(L, index)); |
397 | } |
398 | |
399 | template<> |
400 | void Param<doc::RgbMapAlgorithm>::fromLua(lua_State* L, int index) |
401 | { |
402 | if (lua_type(L, index) == LUA_TSTRING) |
403 | fromString(lua_tostring(L, index)); |
404 | else |
405 | setValue((doc::RgbMapAlgorithm)lua_tointeger(L, index)); |
406 | } |
407 | |
408 | void CommandWithNewParamsBase::loadParamsFromLuaTable(lua_State* L, int index) |
409 | { |
410 | onResetValues(); |
411 | if (lua_istable(L, index)) { |
412 | lua_pushnil(L); |
413 | while (lua_next(L, index) != 0) { |
414 | if (const char* k = lua_tostring(L, -2)) { |
415 | if (ParamBase* p = onGetParam(k)) |
416 | p->fromLua(L, -1); |
417 | } |
418 | lua_pop(L, 1); // Pop the value, leave the key |
419 | } |
420 | } |
421 | m_skipLoadParams = true; |
422 | } |
423 | |
424 | #endif // ENABLE_SCRIPTING |
425 | |
426 | void CommandWithNewParamsBase::onLoadParams(const Params& params) |
427 | { |
428 | #ifdef ENABLE_SCRIPTING |
429 | if (m_skipLoadParams) { |
430 | m_skipLoadParams = false; |
431 | return; |
432 | } |
433 | #endif // ENABLE_SCRIPTING |
434 | onResetValues(); |
435 | for (const auto& pair : params) { |
436 | if (ParamBase* p = onGetParam(pair.first)) |
437 | p->fromString(pair.second); |
438 | } |
439 | } |
440 | |
441 | } // namespace app |
442 | |