1// Aseprite
2// Copyright (C) 2018-2022 Igara Studio S.A.
3// Copyright (C) 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/cmd/set_layer_blend_mode.h"
13#include "app/cmd/set_layer_name.h"
14#include "app/cmd/set_layer_opacity.h"
15#include "app/doc.h"
16#include "app/doc_api.h"
17#include "app/script/docobj.h"
18#include "app/script/engine.h"
19#include "app/script/luacpp.h"
20#include "app/script/userdata.h"
21#include "app/tx.h"
22#include "doc/layer.h"
23#include "doc/layer_tilemap.h"
24#include "doc/sprite.h"
25
26namespace app {
27namespace script {
28
29using namespace doc;
30
31namespace {
32
33int Layer_eq(lua_State* L)
34{
35 const auto a = may_get_docobj<Layer>(L, 1);
36 const auto b = may_get_docobj<Layer>(L, 2);
37 lua_pushboolean(L, (!a && !b) || (a && b && a->id() == b->id()));
38 return 1;
39}
40
41int Layer_cel(lua_State* L)
42{
43 auto layer = get_docobj<Layer>(L, 1);
44 auto cel = layer->cel(get_frame_number_from_arg(L, 2));
45 if (cel)
46 push_docobj<Cel>(L, cel);
47 else
48 lua_pushnil(L);
49 return 1;
50}
51
52int Layer_get_sprite(lua_State* L)
53{
54 auto layer = get_docobj<Layer>(L, 1);
55 push_docobj<Sprite>(L, layer->sprite());
56 return 1;
57}
58
59int Layer_get_parent(lua_State* L)
60{
61 auto layer = get_docobj<Layer>(L, 1);
62 if (layer->parent() == layer->sprite()->root())
63 push_docobj<Sprite>(L, layer->sprite());
64 else
65 push_docobj<Layer>(L, layer->parent());
66 return 1;
67}
68
69int Layer_get_layers(lua_State* L)
70{
71 auto layer = get_docobj<Layer>(L, 1);
72 if (layer->isGroup())
73 push_group_layers(L, static_cast<LayerGroup*>(layer));
74 else
75 lua_pushnil(L);
76 return 1;
77}
78
79int Layer_get_stackIndex(lua_State* L)
80{
81 auto layer = get_docobj<Layer>(L, 1);
82 const auto& layers = layer->parent()->layers();
83 auto it = std::find(layers.begin(), layers.end(), layer);
84 ASSERT(it != layers.end());
85 if (it != layers.end())
86 lua_pushinteger(L, it - layers.begin() + 1);
87 else
88 lua_pushnil(L);
89 return 1;
90}
91
92int Layer_get_previous(lua_State* L)
93{
94 auto layer = get_docobj<Layer>(L, 1);
95 if (auto previous = layer->getPrevious())
96 push_docobj<Layer>(L, previous);
97 else
98 lua_pushnil(L);
99 return 1;
100}
101
102int Layer_get_next(lua_State* L)
103{
104 auto layer = get_docobj<Layer>(L, 1);
105 if (auto next = layer->getNext())
106 push_docobj<Layer>(L, next);
107 else
108 lua_pushnil(L);
109 return 1;
110}
111
112int Layer_get_name(lua_State* L)
113{
114 auto layer = get_docobj<Layer>(L, 1);
115 lua_pushstring(L, layer->name().c_str());
116 return 1;
117}
118
119int Layer_get_opacity(lua_State* L)
120{
121 auto layer = get_docobj<Layer>(L, 1);
122 if (layer->isImage()) {
123 lua_pushinteger(L, static_cast<LayerImage*>(layer)->opacity());
124 return 1;
125 }
126 else
127 return 0;
128}
129
130int Layer_get_blendMode(lua_State* L)
131{
132 auto layer = get_docobj<Layer>(L, 1);
133 if (layer->isImage()) {
134 lua_pushinteger(L, (int)static_cast<LayerImage*>(layer)->blendMode());
135 return 1;
136 }
137 else
138 return 0;
139}
140
141int Layer_get_isImage(lua_State* L)
142{
143 auto layer = get_docobj<Layer>(L, 1);
144 lua_pushboolean(L, layer->isImage());
145 return 1;
146}
147
148int Layer_get_isGroup(lua_State* L)
149{
150 auto layer = get_docobj<Layer>(L, 1);
151 lua_pushboolean(L, layer->isGroup());
152 return 1;
153}
154
155int Layer_get_isTilemap(lua_State* L)
156{
157 auto layer = get_docobj<Layer>(L, 1);
158 lua_pushboolean(L, layer->isTilemap());
159 return 1;
160}
161
162int Layer_get_isTransparent(lua_State* L)
163{
164 auto layer = get_docobj<Layer>(L, 1);
165 lua_pushboolean(L, layer->isTransparent());
166 return 1;
167}
168
169int Layer_get_isBackground(lua_State* L)
170{
171 auto layer = get_docobj<Layer>(L, 1);
172 lua_pushboolean(L, layer->isBackground());
173 return 1;
174}
175
176int Layer_get_isEditable(lua_State* L)
177{
178 auto layer = get_docobj<Layer>(L, 1);
179 lua_pushboolean(L, layer->isEditable());
180 return 1;
181}
182
183int Layer_get_isVisible(lua_State* L)
184{
185 auto layer = get_docobj<Layer>(L, 1);
186 lua_pushboolean(L, layer->isVisible());
187 return 1;
188}
189
190int Layer_get_isContinuous(lua_State* L)
191{
192 auto layer = get_docobj<Layer>(L, 1);
193 lua_pushboolean(L, layer->isContinuous());
194 return 1;
195}
196
197int Layer_get_isCollapsed(lua_State* L)
198{
199 auto layer = get_docobj<Layer>(L, 1);
200 lua_pushboolean(L, layer->isCollapsed());
201 return 1;
202}
203
204int Layer_get_isExpanded(lua_State* L)
205{
206 auto layer = get_docobj<Layer>(L, 1);
207 lua_pushboolean(L, layer->isExpanded());
208 return 1;
209}
210
211int Layer_get_isReference(lua_State* L)
212{
213 auto layer = get_docobj<Layer>(L, 1);
214 lua_pushboolean(L, layer->isReference());
215 return 1;
216}
217
218int Layer_get_cels(lua_State* L)
219{
220 auto layer = get_docobj<Layer>(L, 1);
221 push_cels(L, layer);
222 return 1;
223}
224
225int Layer_get_tileset(lua_State* L)
226{
227 auto layer = get_docobj<Layer>(L, 1);
228 if (layer->isTilemap())
229 push_tileset(L, static_cast<doc::LayerTilemap*>(layer)->tileset());
230 else
231 lua_pushnil(L);
232 return 1;
233}
234
235int Layer_set_name(lua_State* L)
236{
237 auto layer = get_docobj<Layer>(L, 1);
238 const char* name = lua_tostring(L, 2);
239 if (name) {
240 Tx tx;
241 tx(new cmd::SetLayerName(layer, name));
242 tx.commit();
243 }
244 return 0;
245}
246
247int Layer_set_opacity(lua_State* L)
248{
249 auto layer = get_docobj<Layer>(L, 1);
250 const int opacity = lua_tointeger(L, 2);
251 if (layer->isImage()) {
252 Tx tx;
253 tx(new cmd::SetLayerOpacity(static_cast<LayerImage*>(layer), opacity));
254 tx.commit();
255 }
256 return 0;
257}
258
259int Layer_set_blendMode(lua_State* L)
260{
261 auto layer = get_docobj<Layer>(L, 1);
262 const int blendMode = lua_tointeger(L, 2);
263 if (layer->isImage()) {
264 Tx tx;
265 tx(new cmd::SetLayerBlendMode(static_cast<LayerImage*>(layer),
266 (doc::BlendMode)blendMode));
267 tx.commit();
268 }
269 return 0;
270}
271
272int Layer_set_stackIndex(lua_State* L)
273{
274 auto layer = get_docobj<Layer>(L, 1);
275 const auto& layers = layer->parent()->layers();
276 int newStackIndex = lua_tointeger(L, 2);
277 int stackIndex = 1;
278 auto parent = layer->parent();
279
280 // First we need to know this layer stackIndex because we'll use
281 auto it = std::find(layers.begin(), layers.end(), layer);
282 ASSERT(it != layers.end());
283 if (it != layers.end())
284 stackIndex = it - layers.begin() + 1;
285
286 Layer* beforeThis = nullptr;
287 if (newStackIndex > stackIndex) {
288 ++newStackIndex;
289 }
290
291 if (newStackIndex-1 < int(parent->layers().size())) {
292 beforeThis = parent->layers()[std::clamp(newStackIndex-1, 0, (int)parent->layers().size())];
293 }
294 else {
295 beforeThis = nullptr;
296 }
297
298 // Do nothing
299 if (beforeThis == layer)
300 return 0;
301
302 Doc* doc = static_cast<Doc*>(layer->sprite()->document());
303 Tx tx;
304 DocApi(doc, tx).restackLayerBefore(layer, parent, beforeThis);
305 tx.commit();
306 return 0;
307}
308
309int Layer_set_isEditable(lua_State* L)
310{
311 auto layer = get_docobj<Layer>(L, 1);
312 layer->setEditable(lua_toboolean(L, 2));
313 return 0;
314}
315
316int Layer_set_isVisible(lua_State* L)
317{
318 auto layer = get_docobj<Layer>(L, 1);
319 layer->setVisible(lua_toboolean(L, 2));
320 return 0;
321}
322
323int Layer_set_isContinuous(lua_State* L)
324{
325 auto layer = get_docobj<Layer>(L, 1);
326 layer->setContinuous(lua_toboolean(L, 2));
327 return 0;
328}
329
330int Layer_set_isCollapsed(lua_State* L)
331{
332 auto layer = get_docobj<Layer>(L, 1);
333 layer->setCollapsed(lua_toboolean(L, 2));
334 return 0;
335}
336
337int Layer_set_isExpanded(lua_State* L)
338{
339 auto layer = get_docobj<Layer>(L, 1);
340 layer->setCollapsed(!lua_toboolean(L, 2));
341 return 0;
342}
343
344int Layer_set_parent(lua_State* L)
345{
346 auto layer = get_docobj<Layer>(L, 1);
347 LayerGroup* parent = nullptr;
348 if (auto sprite = may_get_docobj<Sprite>(L, 2)) {
349 parent = sprite->root();
350 }
351 else if (auto parentLayer = may_get_docobj<Layer>(L, 2)) {
352 if (parentLayer->isGroup())
353 parent = static_cast<LayerGroup*>(parentLayer);
354 else
355 return luaL_error(L, "the given parent is not a layer group or sprite");
356 }
357
358 if (!parent)
359 return luaL_error(L, "parent cannot be nil");
360 else if (parent == layer)
361 return luaL_error(L, "the parent of a layer cannot be the layer itself");
362
363 // TODO Why? should we be able to do this? It would require some hard work:
364 // 1. convert color modes
365 // 2. multiple transactions for both modified sprites?
366 if (parent->sprite() != layer->sprite())
367 return luaL_error(L, "you cannot move layers between sprites");
368
369 if (parent) {
370 Doc* doc = static_cast<Doc*>(layer->sprite()->document());
371 Tx tx;
372 DocApi(doc, tx).restackLayerAfter(
373 layer, parent, parent->lastLayer());
374 tx.commit();
375 }
376 return 0;
377}
378
379const luaL_Reg Layer_methods[] = {
380 { "__eq", Layer_eq },
381 { "cel", Layer_cel },
382 { nullptr, nullptr }
383};
384
385const Property Layer_properties[] = {
386 { "sprite", Layer_get_sprite, nullptr },
387 { "parent", Layer_get_parent, Layer_set_parent },
388 { "layers", Layer_get_layers, nullptr },
389 { "stackIndex", Layer_get_stackIndex, Layer_set_stackIndex },
390 { "previous", Layer_get_previous, nullptr },
391 { "next", Layer_get_next, nullptr },
392 { "name", Layer_get_name, Layer_set_name },
393 { "opacity", Layer_get_opacity, Layer_set_opacity },
394 { "blendMode", Layer_get_blendMode, Layer_set_blendMode },
395 { "isImage", Layer_get_isImage, nullptr },
396 { "isGroup", Layer_get_isGroup, nullptr },
397 { "isTilemap", Layer_get_isTilemap, nullptr },
398 { "isTransparent", Layer_get_isTransparent, nullptr },
399 { "isBackground", Layer_get_isBackground, nullptr },
400 { "isEditable", Layer_get_isEditable, Layer_set_isEditable },
401 { "isVisible", Layer_get_isVisible, Layer_set_isVisible },
402 { "isContinuous", Layer_get_isContinuous, Layer_set_isContinuous },
403 { "isCollapsed", Layer_get_isCollapsed, Layer_set_isCollapsed },
404 { "isExpanded", Layer_get_isExpanded, Layer_set_isExpanded },
405 { "isReference", Layer_get_isReference, nullptr },
406 { "cels", Layer_get_cels, nullptr },
407 { "color", UserData_get_color<Layer>, UserData_set_color<Layer> },
408 { "data", UserData_get_text<Layer>, UserData_set_text<Layer> },
409 { "tileset", Layer_get_tileset, nullptr },
410 { nullptr, nullptr, nullptr }
411};
412
413} // anonymous namespace
414
415DEF_MTNAME(Layer);
416
417void register_layer_class(lua_State* L)
418{
419 using Layer = doc::Layer;
420 REG_CLASS(L, Layer);
421 REG_CLASS_PROPERTIES(L, Layer);
422}
423
424} // namespace script
425} // namespace app
426