1// Aseprite
2// Copyright (C) 2018-2022 Igara Studio S.A.
3// Copyright (C) 2015-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/cmd/add_layer.h"
14#include "app/cmd/add_slice.h"
15#include "app/cmd/assign_color_profile.h"
16#include "app/cmd/clear_cel.h"
17#include "app/cmd/convert_color_profile.h"
18#include "app/cmd/copy_region.h"
19#include "app/cmd/flatten_layers.h"
20#include "app/cmd/remove_layer.h"
21#include "app/cmd/remove_slice.h"
22#include "app/cmd/remove_tag.h"
23#include "app/cmd/set_grid_bounds.h"
24#include "app/cmd/set_mask.h"
25#include "app/cmd/set_pixel_ratio.h"
26#include "app/cmd/set_sprite_size.h"
27#include "app/cmd/set_transparent_color.h"
28#include "app/color_spaces.h"
29#include "app/commands/commands.h"
30#include "app/commands/params.h"
31#include "app/console.h"
32#include "app/context.h"
33#include "app/doc.h"
34#include "app/doc_access.h"
35#include "app/doc_api.h"
36#include "app/doc_range.h"
37#include "app/doc_undo.h"
38#include "app/doc_undo_observer.h"
39#include "app/file/palette_file.h"
40#include "app/script/docobj.h"
41#include "app/script/engine.h"
42#include "app/script/luacpp.h"
43#include "app/script/security.h"
44#include "app/script/userdata.h"
45#include "app/site.h"
46#include "app/transaction.h"
47#include "app/tx.h"
48#include "app/ui/doc_view.h"
49#include "base/convert_to.h"
50#include "base/fs.h"
51#include "doc/layer.h"
52#include "doc/mask.h"
53#include "doc/palette.h"
54#include "doc/slice.h"
55#include "doc/sprite.h"
56#include "doc/tag.h"
57
58#include <algorithm>
59
60namespace app {
61namespace script {
62
63namespace {
64
65int Sprite_new(lua_State* L)
66{
67 std::unique_ptr<Doc> doc;
68
69 // Duplicate a sprite
70 if (auto otherSpr = may_get_docobj<doc::Sprite>(L, 1)) {
71 Doc* otherDoc = static_cast<Doc*>(otherSpr->document());
72 doc.reset(otherDoc->duplicate(DuplicateExactCopy));
73 }
74 else {
75 doc::ImageSpec spec(doc::ColorMode::RGB, 1, 1, 0);
76 if (auto spec2 = may_get_obj<doc::ImageSpec>(L, 1)) {
77 spec = *spec2;
78 }
79 else {
80 if (lua_istable(L, 1)) {
81 // Sprite{ fromFile }
82 int type = lua_getfield(L, 1, "fromFile");
83 if (type != LUA_TNIL) {
84 if (const char* fromFile = lua_tostring(L, -1)) {
85 std::string fn = fromFile;
86 lua_pop(L, 1);
87
88 bool oneFrame = (lua_is_key_true(L, -1, "oneFrame"));
89
90 return load_sprite_from_file(
91 L, fn.c_str(),
92 (oneFrame ? LoadSpriteFromFileParam::OneFrameAsSprite:
93 LoadSpriteFromFileParam::FullAniAsSprite));
94 }
95 }
96 lua_pop(L, 1);
97
98 // In case that there is no "fromFile" field
99 if (type == LUA_TNIL) {
100 // Sprite{ width, height, colorMode }
101 lua_getfield(L, 1, "width");
102 lua_getfield(L, 1, "height");
103 spec.setWidth(lua_tointeger(L, -2));
104 spec.setHeight(lua_tointeger(L, -1));
105 lua_pop(L, 2);
106
107 type = lua_getfield(L, 1, "colorMode");
108 if (type != LUA_TNIL)
109 spec.setColorMode((doc::ColorMode)lua_tointeger(L, -1));
110 lua_pop(L, 1);
111 }
112 }
113 else {
114 const int w = lua_tointeger(L, 1);
115 const int h = lua_tointeger(L, 2);
116 const int colorMode = (lua_isnone(L, 3) ? IMAGE_RGB: lua_tointeger(L, 3));
117 spec.setWidth(w);
118 spec.setHeight(h);
119 spec.setColorMode((doc::ColorMode)colorMode);
120 }
121 spec.setColorSpace(get_working_rgb_space_from_preferences());
122 }
123
124 if (spec.width() < 1)
125 return luaL_error(L, "invalid width value = %d in Sprite()", spec.width());
126 if (spec.height() < 1)
127 return luaL_error(L, "invalid height value = %d in Sprite()", spec.height());
128
129 std::unique_ptr<Sprite> sprite(Sprite::MakeStdSprite(spec, 256));
130 doc.reset(new Doc(sprite.get()));
131 sprite.release();
132 }
133
134 app::Context* ctx = App::instance()->context();
135 doc->setContext(ctx);
136
137 push_docobj(L, doc->sprite());
138 doc.release();
139 return 1;
140}
141
142int Sprite_eq(lua_State* L)
143{
144 const auto a = may_get_docobj<Sprite>(L, 1);
145 const auto b = may_get_docobj<Sprite>(L, 2);
146 lua_pushboolean(L, (!a && !b) || (a && b && a->id() == b->id()));
147 return 1;
148}
149
150int Sprite_resize(lua_State* L)
151{
152 auto sprite = get_docobj<Sprite>(L, 1);
153 gfx::Size size = convert_args_into_size(L, 2);
154
155 // Fix invalid sizes
156 size.w = std::max(1, size.w);
157 size.h = std::max(1, size.h);
158
159 Command* resizeCommand =
160 Commands::instance()->byId(CommandId::SpriteSize());
161
162 // TODO use SpriteSizeParams directly instead of converting back and
163 // forth between strings.
164 Params params;
165 params.set("ui", "false");
166 params.set("width", base::convert_to<std::string>(size.w).c_str());
167 params.set("height", base::convert_to<std::string>(size.h).c_str());
168
169 app::Context* appCtx = App::instance()->context();
170 auto oldDoc = appCtx->activeDocument();
171 appCtx->setActiveDocument(static_cast<Doc*>(sprite->document()));
172 appCtx->executeCommand(resizeCommand, params);
173 appCtx->setActiveDocument(oldDoc);
174 return 0;
175}
176
177int Sprite_crop(lua_State* L)
178{
179 auto sprite = get_docobj<Sprite>(L, 1);
180 Doc* doc = static_cast<Doc*>(sprite->document());
181 gfx::Rect bounds;
182
183 // Use mask bounds
184 if (lua_isnone(L, 2)) {
185 if (doc->isMaskVisible())
186 bounds = doc->mask()->bounds();
187 else
188 bounds = sprite->bounds();
189 }
190 else {
191 bounds = convert_args_into_rect(L, 2);
192 }
193
194 if (!bounds.isEmpty()) {
195 Tx tx;
196 DocApi(doc, tx).cropSprite(sprite, bounds);
197 tx.commit();
198 }
199 return 0;
200}
201
202int Sprite_saveAs_base(lua_State* L, std::string& absFn)
203{
204 bool result = false;
205 auto sprite = get_docobj<Sprite>(L, 1);
206 const char* fn = luaL_checkstring(L, 2);
207 if (fn && sprite) {
208 Doc* doc = static_cast<Doc*>(sprite->document());
209 app::Context* appCtx = App::instance()->context();
210 appCtx->setActiveDocument(doc);
211
212 absFn = base::get_absolute_path(fn);
213 if (!ask_access(L, absFn.c_str(), FileAccessMode::Write, ResourceType::File))
214 return luaL_error(L, "script doesn't have access to write file %s",
215 absFn.c_str());
216
217 Command* saveCommand =
218 Commands::instance()->byId(CommandId::SaveFileCopyAs());
219
220 Params params;
221 params.set("filename", absFn.c_str());
222 params.set("useUI", "false");
223 appCtx->executeCommand(saveCommand, params);
224
225 result = true;
226 }
227 lua_pushboolean(L, result);
228 return 1;
229}
230
231int Sprite_saveAs(lua_State* L)
232{
233 std::string fn;
234 int res = Sprite_saveAs_base(L, fn);
235 if (!fn.empty()) {
236 auto sprite = get_docobj<Sprite>(L, 1);
237 if (sprite) {
238 Doc* doc = static_cast<Doc*>(sprite->document());
239 doc->setFilename(fn);
240 doc->markAsSaved();
241 }
242 }
243 return res;
244}
245
246int Sprite_saveCopyAs(lua_State* L)
247{
248 std::string fn;
249 return Sprite_saveAs_base(L, fn);
250}
251
252int Sprite_close(lua_State* L)
253{
254 auto sprite = get_docobj<Sprite>(L, 1);
255 Doc* doc = static_cast<Doc*>(sprite->document());
256 try {
257 DocDestroyer destroyer(static_cast<app::Context*>(doc->context()), doc, 500);
258 destroyer.destroyDocument();
259 return 0;
260 }
261 catch (const LockedDocException& ex) {
262 return luaL_error(L, "cannot lock document to close it\n%s", ex.what());
263 }
264}
265
266int Sprite_loadPalette(lua_State* L)
267{
268 auto sprite = get_docobj<Sprite>(L, 1);
269 const char* fn = luaL_checkstring(L, 2);
270 if (fn && sprite) {
271 std::string absFn = base::get_absolute_path(fn);
272 if (!ask_access(L, absFn.c_str(), FileAccessMode::Read, ResourceType::File))
273 return luaL_error(L, "script doesn't have access to open file %s",
274 absFn.c_str());
275
276 Doc* doc = static_cast<Doc*>(sprite->document());
277 std::unique_ptr<doc::Palette> palette(load_palette(absFn.c_str()));
278 if (palette) {
279 Tx tx;
280 // TODO Merge this with the code in LoadPaletteCommand
281 doc->getApi(tx).setPalette(sprite, 0, palette.get());
282 tx.commit();
283 }
284 }
285 return 0;
286}
287
288int Sprite_setPalette(lua_State* L)
289{
290 auto sprite = get_docobj<Sprite>(L, 1);
291 auto pal = get_palette_from_arg(L, 2);
292 if (sprite && pal) {
293 Doc* doc = static_cast<Doc*>(sprite->document());
294
295 Tx tx;
296 doc->getApi(tx).setPalette(sprite, 0, pal);
297 tx.commit();
298 }
299 return 0;
300}
301
302int Sprite_assignColorSpace(lua_State* L)
303{
304 auto sprite = get_docobj<Sprite>(L, 1);
305 auto cs = get_obj<gfx::ColorSpace>(L, 2);
306 Tx tx;
307 tx(new cmd::AssignColorProfile(
308 sprite, base::make_ref<gfx::ColorSpace>(*cs)));
309 tx.commit();
310 return 1;
311}
312
313int Sprite_convertColorSpace(lua_State* L)
314{
315 auto sprite = get_docobj<Sprite>(L, 1);
316 auto cs = get_obj<gfx::ColorSpace>(L, 2);
317 Tx tx;
318 tx(new cmd::ConvertColorProfile(
319 sprite, base::make_ref<gfx::ColorSpace>(*cs)));
320 tx.commit();
321 return 1;
322}
323
324int Sprite_flatten(lua_State* L)
325{
326 auto sprite = get_docobj<Sprite>(L, 1);
327
328 DocRange range;
329 for (auto layer : sprite->root()->layers())
330 range.selectLayer(layer);
331
332 Tx tx;
333 tx(new cmd::FlattenLayers(sprite, range.selectedLayers(), true));
334 tx.commit();
335 return 0;
336}
337
338int Sprite_newLayer(lua_State* L)
339{
340 auto sprite = get_docobj<Sprite>(L, 1);
341 doc::Layer* newLayer = new doc::LayerImage(sprite);
342
343 Tx tx;
344 tx(new cmd::AddLayer(sprite->root(), newLayer, sprite->root()->lastLayer()));
345 tx.commit();
346
347 push_docobj(L, newLayer);
348 return 1;
349}
350
351int Sprite_newGroup(lua_State* L)
352{
353 auto sprite = get_docobj<Sprite>(L, 1);
354 doc::Layer* newGroup = new doc::LayerGroup(sprite);
355
356 Tx tx;
357 tx(new cmd::AddLayer(sprite->root(), newGroup, sprite->root()->lastLayer()));
358 tx.commit();
359
360 push_docobj(L, newGroup);
361 return 1;
362}
363
364int Sprite_deleteLayer(lua_State* L)
365{
366 auto sprite = get_docobj<Sprite>(L, 1);
367 auto layer = may_get_docobj<Layer>(L, 2);
368 if (!layer && lua_isstring(L, 2)) {
369 const char* layerName = lua_tostring(L, 2);
370 if (layerName) {
371 for (Layer* child : sprite->allLayers()) {
372 if (child->name() == layerName) {
373 layer = child;
374 break;
375 }
376 }
377 }
378 }
379 if (layer) {
380 if (sprite != layer->sprite())
381 return luaL_error(L, "the layer doesn't belong to the sprite");
382 Tx tx;
383 tx(new cmd::RemoveLayer(layer));
384 tx.commit();
385 return 0;
386 }
387 else {
388 return luaL_error(L, "layer not found");
389 }
390}
391
392int Sprite_newFrame(lua_State* L)
393{
394 auto sprite = get_docobj<Sprite>(L, 1);
395 doc::frame_t frame = sprite->lastFrame()+1;
396 doc::frame_t copyThis = frame;
397 if (lua_gettop(L) >= 2) {
398 frame = get_frame_number_from_arg(L, 2);
399 if (frame < 0)
400 return luaL_error(L, "frame index out of bounds %d", frame+1);
401 copyThis = frame+1; // addFrame() copies the previous frame of the given one.
402 }
403
404 Doc* doc = static_cast<Doc*>(sprite->document());
405
406 Tx tx;
407 doc->getApi(tx).addFrame(sprite, copyThis);
408 tx.commit();
409
410 push_sprite_frame(L, sprite, frame);
411 return 1;
412}
413
414int Sprite_newEmptyFrame(lua_State* L)
415{
416 auto sprite = get_docobj<Sprite>(L, 1);
417 doc::frame_t frame = sprite->lastFrame()+1;
418 if (lua_gettop(L) >= 2) {
419 frame = get_frame_number_from_arg(L, 2);
420 if (frame < 0)
421 return luaL_error(L, "frame index out of bounds %d", frame+1);
422 }
423
424 Doc* doc = static_cast<Doc*>(sprite->document());
425
426 Tx tx;
427 DocApi(doc, tx).addEmptyFrame(sprite, frame);
428 tx.commit();
429
430 push_sprite_frame(L, sprite, frame);
431 return 1;
432}
433
434int Sprite_deleteFrame(lua_State* L)
435{
436 auto sprite = get_docobj<Sprite>(L, 1);
437 doc::frame_t frame = get_frame_number_from_arg(L, 2);
438 if (frame < 0 || frame > sprite->lastFrame())
439 return luaL_error(L, "frame index out of bounds %d", frame+1);
440
441 Doc* doc = static_cast<Doc*>(sprite->document());
442
443 Tx tx;
444 doc->getApi(tx).removeFrame(sprite, frame);
445 tx.commit();
446 return 0;
447}
448
449int Sprite_newCel(lua_State* L)
450{
451 auto sprite = get_docobj<Sprite>(L, 1);
452 auto layerBase = get_docobj<Layer>(L, 2);
453 if (!layerBase->isImage())
454 return luaL_error(L, "unexpected kinf of layer in Sprite:newCel()");
455
456 frame_t frame = get_frame_number_from_arg(L, 3);
457 if (frame < 0 || frame > sprite->lastFrame())
458 return luaL_error(L, "frame index out of bounds %d", frame+1);
459
460 Doc* doc = static_cast<Doc*>(sprite->document());
461 LayerImage* layer = static_cast<LayerImage*>(layerBase);
462 ImageRef image(nullptr);
463
464 Image* srcImage = may_get_image_from_arg(L, 4);
465 gfx::Point pos = convert_args_into_point(L, 5);
466 Cel* cel = nullptr;
467
468 // For background layers we just draw the image in the existent cel
469 if (layer->isBackground()) {
470 cel = layer->cel(frame);
471 ASSERT(cel);
472
473 Tx tx;
474 DocApi api = doc->getApi(tx);
475 api.clearCel(layer, frame);
476 if (srcImage) {
477 tx(new cmd::CopyRegion(cel->image(), srcImage,
478 gfx::Region(srcImage->bounds()),
479 pos, false));
480 }
481 tx.commit();
482 }
483 // For transparent layers we just draw the image in the existent cel
484 else {
485 if (srcImage)
486 image.reset(Image::createCopy(srcImage));
487 else
488 image.reset(Image::create(sprite->spec()));
489
490 cel = new Cel(frame, image);
491 cel->setPosition(pos);
492
493 Tx tx;
494 DocApi api = doc->getApi(tx);
495 if (layer->cel(frame))
496 api.clearCel(layer, frame);
497 api.addCel(layer, cel);
498 tx.commit();
499 }
500
501 push_docobj(L, cel);
502 return 1;
503}
504
505int Sprite_deleteCel(lua_State* L)
506{
507 auto sprite = get_docobj<Sprite>(L, 1);
508 auto cel = may_get_docobj<doc::Cel>(L, 2);
509 if (!cel) {
510 if (auto layer = may_get_docobj<doc::Layer>(L, 2)) {
511 if (sprite != layer->sprite())
512 return luaL_error(L, "the layer doesn't belong to the sprite");
513 doc::frame_t frame = get_frame_number_from_arg(L, 3);
514 if (layer->isImage())
515 cel = static_cast<doc::LayerImage*>(layer)->cel(frame);
516 }
517 }
518
519 if (cel) {
520 Tx tx;
521 tx(new cmd::ClearCel(cel));
522 tx.commit();
523 return 0;
524 }
525 else {
526 return luaL_error(L, "cel not found");
527 }
528}
529
530int Sprite_newTag(lua_State* L)
531{
532 auto sprite = get_docobj<Sprite>(L, 1);
533 auto from = get_frame_number_from_arg(L, 2);
534 auto to = get_frame_number_from_arg(L, 3);
535 auto tag = new doc::Tag(from, to);
536
537 Tx tx;
538 tx(new cmd::AddTag(sprite, tag));
539 tx.commit();
540
541 push_docobj(L, tag);
542 return 1;
543}
544
545int Sprite_deleteTag(lua_State* L)
546{
547 auto sprite = get_docobj<Sprite>(L, 1);
548 auto tag = may_get_docobj<Tag>(L, 2);
549 if (!tag && lua_isstring(L, 2)) {
550 const char* tagName = lua_tostring(L, 2);
551 if (tagName)
552 tag = sprite->tags().getByName(tagName);
553 }
554 if (tag) {
555 if (sprite != tag->owner()->sprite())
556 return luaL_error(L, "the tag doesn't belong to the sprite");
557 Tx tx;
558 tx(new cmd::RemoveTag(sprite, tag));
559 tx.commit();
560 return 0;
561 }
562 else {
563 return luaL_error(L, "tag not found");
564 }
565}
566
567int Sprite_newSlice(lua_State* L)
568{
569 auto sprite = get_docobj<Sprite>(L, 1);
570 auto slice = new doc::Slice();
571
572 gfx::Rect bounds = convert_args_into_rect(L, 2);
573 if (!bounds.isEmpty())
574 slice->insert(0, doc::SliceKey(bounds));
575
576 Tx tx;
577 tx(new cmd::AddSlice(sprite, slice));
578 tx.commit();
579
580 push_docobj(L, slice);
581 return 1;
582}
583
584int Sprite_deleteSlice(lua_State* L)
585{
586 auto sprite = get_docobj<Sprite>(L, 1);
587 doc::Slice* slice = may_get_docobj<Slice>(L, 2);
588 if (!slice && lua_isstring(L, 2)) {
589 const char* sliceName = lua_tostring(L, 2);
590 if (sliceName)
591 slice = sprite->slices().getByName(sliceName);
592 }
593 if (slice) {
594 if (sprite != slice->owner()->sprite())
595 return luaL_error(L, "the slice doesn't belong to the sprite");
596 Tx tx;
597 tx(new cmd::RemoveSlice(sprite, slice));
598 tx.commit();
599 return 0;
600 }
601 else {
602 return luaL_error(L, "slice not found");
603 }
604}
605
606int Sprite_get_events(lua_State* L)
607{
608 auto sprite = get_docobj<Sprite>(L, 1);
609 push_sprite_events(L, sprite);
610 return 1;
611}
612
613int Sprite_get_filename(lua_State* L)
614{
615 auto sprite = get_docobj<Sprite>(L, 1);
616 lua_pushstring(L, sprite->document()->filename().c_str());
617 return 1;
618}
619
620int Sprite_get_width(lua_State* L)
621{
622 auto sprite = get_docobj<Sprite>(L, 1);
623 lua_pushinteger(L, sprite->width());
624 return 1;
625}
626
627int Sprite_get_height(lua_State* L)
628{
629 auto sprite = get_docobj<Sprite>(L, 1);
630 lua_pushinteger(L, sprite->height());
631 return 1;
632}
633
634int Sprite_get_colorMode(lua_State* L)
635{
636 auto sprite = get_docobj<Sprite>(L, 1);
637 lua_pushinteger(L, sprite->pixelFormat());
638 return 1;
639}
640
641int Sprite_get_colorSpace(lua_State* L)
642{
643 auto sprite = get_docobj<Sprite>(L, 1);
644 auto cs = sprite->colorSpace();
645 if (cs)
646 push_color_space(L, *cs);
647 else
648 lua_pushnil(L);
649 return 1;
650}
651
652int Sprite_get_spec(lua_State* L)
653{
654 const auto sprite = get_docobj<Sprite>(L, 1);
655 push_obj(L, sprite->spec());
656 return 1;
657}
658
659int Sprite_get_selection(lua_State* L)
660{
661 auto sprite = get_docobj<Sprite>(L, 1);
662 push_sprite_selection(L, sprite);
663 return 1;
664}
665
666int Sprite_get_frames(lua_State* L)
667{
668 auto sprite = get_docobj<Sprite>(L, 1);
669 push_sprite_frames(L, sprite);
670 return 1;
671}
672
673int Sprite_get_palettes(lua_State* L)
674{
675 auto sprite = get_docobj<Sprite>(L, 1);
676 push_sprite_palettes(L, sprite);
677 return 1;
678}
679
680int Sprite_get_layers(lua_State* L)
681{
682 auto sprite = get_docobj<Sprite>(L, 1);
683 push_sprite_layers(L, sprite);
684 return 1;
685}
686
687int Sprite_get_cels(lua_State* L)
688{
689 auto sprite = get_docobj<Sprite>(L, 1);
690 push_cels(L, sprite);
691 return 1;
692}
693
694int Sprite_get_tags(lua_State* L)
695{
696 auto sprite = get_docobj<Sprite>(L, 1);
697 push_sprite_tags(L, sprite);
698 return 1;
699}
700
701int Sprite_get_slices(lua_State* L)
702{
703 auto sprite = get_docobj<Sprite>(L, 1);
704 push_sprite_slices(L, sprite);
705 return 1;
706}
707
708int Sprite_get_tilesets(lua_State* L)
709{
710 auto sprite = get_docobj<Sprite>(L, 1);
711 push_tilesets(L, sprite->tilesets());
712 return 1;
713}
714
715int Sprite_get_backgroundLayer(lua_State* L)
716{
717 auto sprite = get_docobj<Sprite>(L, 1);
718 doc::Layer* layer = sprite->backgroundLayer();
719 if (layer)
720 push_docobj(L, layer);
721 else
722 lua_pushnil(L);
723 return 1;
724}
725
726int Sprite_get_transparentColor(lua_State* L)
727{
728 const auto sprite = get_docobj<Sprite>(L, 1);
729 lua_pushinteger(L, sprite->transparentColor());
730 return 1;
731}
732
733int Sprite_set_transparentColor(lua_State* L)
734{
735 auto sprite = get_docobj<Sprite>(L, 1);
736 const int index = lua_tointeger(L, 2);
737 Tx tx;
738 tx(new cmd::SetTransparentColor(sprite, index));
739 tx.commit();
740 return 0;
741}
742
743int Sprite_set_filename(lua_State* L)
744{
745 auto sprite = get_docobj<Sprite>(L, 1);
746 const char* fn = lua_tostring(L, 2);
747 sprite->document()->setFilename(fn ? std::string(fn): std::string());
748 return 0;
749}
750
751int Sprite_set_width(lua_State* L)
752{
753 auto sprite = get_docobj<Sprite>(L, 1);
754 const int width = lua_tointeger(L, 2);
755 Tx tx;
756 tx(new cmd::SetSpriteSize(sprite, width, sprite->height()));
757 tx.commit();
758 return 0;
759}
760
761int Sprite_set_height(lua_State* L)
762{
763 auto sprite = get_docobj<Sprite>(L, 1);
764 const int height = lua_tointeger(L, 2);
765 Tx tx;
766 tx(new cmd::SetSpriteSize(sprite, sprite->width(), height));
767 tx.commit();
768 return 0;
769}
770
771int Sprite_set_selection(lua_State* L)
772{
773 auto sprite = get_docobj<Sprite>(L, 1);
774 const auto mask = get_mask_from_arg(L, 2);
775 Doc* doc = static_cast<Doc*>(sprite->document());
776 Tx tx;
777 tx(new cmd::SetMask(doc, mask));
778 tx.commit();
779 return 0;
780}
781
782int Sprite_get_bounds(lua_State* L)
783{
784 const auto sprite = get_docobj<Sprite>(L, 1);
785 push_obj<gfx::Rect>(L, sprite->bounds());
786 return 1;
787}
788
789int Sprite_get_gridBounds(lua_State* L)
790{
791 const auto sprite = get_docobj<Sprite>(L, 1);
792 push_obj<gfx::Rect>(L, sprite->gridBounds());
793 return 1;
794}
795
796int Sprite_set_gridBounds(lua_State* L)
797{
798 auto sprite = get_docobj<Sprite>(L, 1);
799 const gfx::Rect bounds = convert_args_into_rect(L, 2);
800 Tx tx;
801 tx(new cmd::SetGridBounds(sprite, bounds));
802 tx.commit();
803 return 0;
804}
805
806int Sprite_get_pixelRatio(lua_State* L)
807{
808 const auto sprite = get_docobj<Sprite>(L, 1);
809 push_obj<gfx::Size>(L, sprite->pixelRatio());
810 return 1;
811}
812
813int Sprite_set_pixelRatio(lua_State* L)
814{
815 auto sprite = get_docobj<Sprite>(L, 1);
816 const gfx::Size pixelRatio = convert_args_into_size(L, 2);
817 Tx tx;
818 tx(new cmd::SetPixelRatio(sprite, pixelRatio));
819 tx.commit();
820 return 0;
821}
822
823const luaL_Reg Sprite_methods[] = {
824 { "__eq", Sprite_eq },
825 { "resize", Sprite_resize },
826 { "crop", Sprite_crop },
827 { "saveAs", Sprite_saveAs },
828 { "saveCopyAs", Sprite_saveCopyAs },
829 { "close", Sprite_close },
830 { "loadPalette", Sprite_loadPalette },
831 { "setPalette", Sprite_setPalette },
832 { "assignColorSpace", Sprite_assignColorSpace },
833 { "convertColorSpace", Sprite_convertColorSpace },
834 { "flatten", Sprite_flatten },
835 // Layers
836 { "newLayer", Sprite_newLayer },
837 { "newGroup", Sprite_newGroup },
838 { "deleteLayer", Sprite_deleteLayer },
839 // Frames
840 { "newFrame", Sprite_newFrame },
841 { "newEmptyFrame", Sprite_newEmptyFrame },
842 { "deleteFrame", Sprite_deleteFrame },
843 // Cel
844 { "newCel", Sprite_newCel },
845 { "deleteCel", Sprite_deleteCel },
846 // Tag
847 { "newTag", Sprite_newTag },
848 { "deleteTag", Sprite_deleteTag },
849 // Slices
850 { "newSlice", Sprite_newSlice },
851 { "deleteSlice", Sprite_deleteSlice },
852 { nullptr, nullptr }
853};
854
855const Property Sprite_properties[] = {
856 { "filename", Sprite_get_filename, Sprite_set_filename },
857 { "width", Sprite_get_width, Sprite_set_width },
858 { "height", Sprite_get_height, Sprite_set_height },
859 { "colorMode", Sprite_get_colorMode, nullptr },
860 { "colorSpace", Sprite_get_colorSpace, Sprite_assignColorSpace },
861 { "spec", Sprite_get_spec, nullptr },
862 { "selection", Sprite_get_selection, Sprite_set_selection },
863 { "frames", Sprite_get_frames, nullptr },
864 { "palettes", Sprite_get_palettes, nullptr },
865 { "layers", Sprite_get_layers, nullptr },
866 { "cels", Sprite_get_cels, nullptr },
867 { "tags", Sprite_get_tags, nullptr },
868 { "slices", Sprite_get_slices, nullptr },
869 { "tilesets", Sprite_get_tilesets, nullptr },
870 { "backgroundLayer", Sprite_get_backgroundLayer, nullptr },
871 { "transparentColor", Sprite_get_transparentColor, Sprite_set_transparentColor },
872 { "bounds", Sprite_get_bounds, nullptr },
873 { "gridBounds", Sprite_get_gridBounds, Sprite_set_gridBounds },
874 { "color", UserData_get_color<Sprite>, UserData_set_color<Sprite> },
875 { "data", UserData_get_text<Sprite>, UserData_set_text<Sprite> },
876 { "pixelRatio", Sprite_get_pixelRatio, Sprite_set_pixelRatio },
877 { "events", Sprite_get_events, nullptr },
878 { nullptr, nullptr, nullptr }
879};
880
881} // anonymous namespace
882
883DEF_MTNAME(doc::Sprite);
884
885void register_sprite_class(lua_State* L)
886{
887 using doc::Sprite;
888 REG_CLASS(L, Sprite);
889 REG_CLASS_NEW(L, Sprite);
890 REG_CLASS_PROPERTIES(L, Sprite);
891}
892
893} // namespace script
894} // namespace app
895