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
37namespace app {
38
39//////////////////////////////////////////////////////////////////////
40// Convert values from strings (e.g. useful for values from gui.xml)
41//////////////////////////////////////////////////////////////////////
42
43template<>
44void Param<bool>::fromString(const std::string& value)
45{
46 setValue(value == "1" || value == "true");
47}
48
49template<>
50void Param<int>::fromString(const std::string& value)
51{
52 setValue(base::convert_to<int>(value));
53}
54
55template<>
56void Param<double>::fromString(const std::string& value)
57{
58 setValue(base::convert_to<double>(value));
59}
60
61template<>
62void Param<std::string>::fromString(const std::string& value)
63{
64 setValue(value);
65}
66
67template<>
68void 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
80template<>
81void 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
95template<>
96void 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
106template<>
107void 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
123template<>
124void 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
135template<>
136void 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
149template<>
150void Param<doc::AniDir>::fromString(const std::string& value)
151{
152 setValue(convert_string_to_anidir(value));
153}
154
155template<>
156void Param<app::Color>::fromString(const std::string& value)
157{
158 setValue(app::Color::fromString(value));
159}
160
161template<>
162void 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
174template<>
175void 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
183template<>
184void 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
198template<>
199void 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
212template<>
213void 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
227template<>
228void Param<tools::InkType>::fromString(const std::string& value)
229{
230 setValue(tools::string_id_to_ink_type(value));
231}
232
233template<>
234void 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
250template<>
251void Param<bool>::fromLua(lua_State* L, int index)
252{
253 setValue(lua_toboolean(L, index));
254}
255
256template<>
257void Param<int>::fromLua(lua_State* L, int index)
258{
259 setValue(lua_tointeger(L, index));
260}
261
262template<>
263void Param<double>::fromLua(lua_State* L, int index)
264{
265 setValue(lua_tonumber(L, index));
266}
267
268template<>
269void 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
277template<>
278void Param<gfx::Size>::fromLua(lua_State* L, int index)
279{
280 setValue(script::convert_args_into_size(L, index));
281}
282
283template<>
284void Param<gfx::Rect>::fromLua(lua_State* L, int index)
285{
286 setValue(script::convert_args_into_rect(L, index));
287}
288
289template<>
290void 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
298template<>
299void 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
307template<>
308void 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
316template<>
317void 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
325template<>
326void 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
334template<>
335void Param<app::Color>::fromLua(lua_State* L, int index)
336{
337 setValue(script::convert_args_into_color(L, index));
338}
339
340template<>
341void 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
349template<>
350void 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
358template<>
359void 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
367template<>
368void 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
376template<>
377void 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
393template<>
394void Param<tools::InkType>::fromLua(lua_State* L, int index)
395{
396 setValue(script::get_value_from_lua<tools::InkType>(L, index));
397}
398
399template<>
400void 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
408void 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
426void 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