lua: Add Range and RangeType

This commit is contained in:
David Capello 2018-11-23 12:56:30 -03:00
parent ecec485fe6
commit bf916fe029
17 changed files with 456 additions and 28 deletions

View File

@ -160,6 +160,7 @@ if(ENABLE_SCRIPTING)
script/image_class.cpp
script/image_iterator_class.cpp
script/image_spec_class.cpp
script/images_class.cpp
script/layer_class.cpp
script/layers_class.cpp
script/luacpp.cpp
@ -167,6 +168,7 @@ if(ENABLE_SCRIPTING)
script/palettes_class.cpp
script/pixel_color_object.cpp
script/point_class.cpp
script/range_class.cpp
script/rectangle_class.cpp
script/security.cpp
script/selection_class.cpp

View File

@ -10,11 +10,9 @@
#include "app/cmd/with_sprite.h"
#include "app/cmd_sequence.h"
#include "doc/object_id.h"
#include "doc/object_ids.h"
#include "doc/selected_layers.h"
#include <vector>
namespace app {
namespace cmd {
@ -28,7 +26,7 @@ namespace cmd {
void onExecute() override;
private:
std::vector<doc::ObjectId> m_layerIds;
ObjectIds m_layerIds;
};
} // namespace cmd

View File

@ -295,6 +295,23 @@ int App_get_site(lua_State* L)
return 1;
}
int App_get_range(lua_State* L)
{
#ifdef ENABLE_UI
app::Context* ctx = App::instance()->context();
Site site = ctx->activeSite();
if (site.sprite() && App::instance()->timeline()) {
push_doc_range(L, site.sprite(), App::instance()->timeline()->range());
}
else {
lua_pushnil(L);
}
#else
lua_pushnil(L);
#endif
return 1;
}
int App_get_isUIAvailable(lua_State* L)
{
app::Context* ctx = App::instance()->context();
@ -420,6 +437,7 @@ const Property App_properties[] = {
{ "version", App_get_version, nullptr },
{ "apiVersion", App_get_apiVersion, nullptr },
{ "site", App_get_site, nullptr },
{ "range", App_get_range, nullptr },
{ "isUIAvailable", App_get_isUIAvailable, nullptr },
{ nullptr, nullptr, nullptr }
};

View File

@ -14,6 +14,7 @@
#include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/layer.h"
#include "doc/object_ids.h"
#include "doc/sprite.h"
#include <algorithm>
@ -27,15 +28,18 @@ using namespace doc;
namespace {
struct CelsObj {
std::vector<ObjectId> cels;
ObjectIds cels;
CelsObj(CelsRange& range) {
for (Cel* cel : range)
for (const Cel* cel : range)
cels.push_back(cel->id());
}
CelsObj(CelList& list) {
for (Cel* cel : list)
cels.push_back(cel->id());
}
CelsObj(const ObjectIds& cels)
: cels(cels) {
}
CelsObj(const CelsObj&) = delete;
CelsObj& operator=(const CelsObj&) = delete;
};
@ -81,13 +85,13 @@ void register_cels_class(lua_State* L)
REG_CLASS(L, Cels);
}
void push_sprite_cels(lua_State* L, Sprite* sprite)
void push_cels(lua_State* L, Sprite* sprite)
{
CelsRange cels = sprite->cels();
push_new<CelsObj>(L, cels);
}
void push_layer_cels(lua_State* L, Layer* layer)
void push_cels(lua_State* L, Layer* layer)
{
CelList cels;
if (layer->isImage())
@ -95,5 +99,10 @@ void push_layer_cels(lua_State* L, Layer* layer)
push_new<CelsObj>(L, cels);
}
void push_cels(lua_State* L, const ObjectIds& cels)
{
push_new<CelsObj>(L, cels);
}
} // namespace script
} // namespace app

View File

@ -13,6 +13,7 @@
#include "app/app.h"
#include "app/console.h"
#include "app/doc_range.h"
#include "app/script/luacpp.h"
#include "app/script/security.h"
#include "base/chrono.h"
@ -142,11 +143,13 @@ void register_frames_class(lua_State* L);
void register_image_class(lua_State* L);
void register_image_iterator_class(lua_State* L);
void register_image_spec_class(lua_State* L);
void register_images_class(lua_State* L);
void register_layer_class(lua_State* L);
void register_layers_class(lua_State* L);
void register_palette_class(lua_State* L);
void register_palettes_class(lua_State* L);
void register_point_class(lua_State* L);
void register_range_class(lua_State* L);
void register_rect_class(lua_State* L);
void register_selection_class(lua_State* L);
void register_site_class(lua_State* L);
@ -263,6 +266,15 @@ Engine::Engine()
setfield_integer(L, "DIVIDE", doc::BlendMode::DIVIDE);
lua_pop(L, 1);
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setglobal(L, "RangeType");
setfield_integer(L, "EMPTY", DocRange::kNone);
setfield_integer(L, "LAYERS", DocRange::kLayers);
setfield_integer(L, "FRAMES", DocRange::kFrames);
setfield_integer(L, "CELS", DocRange::kCels);
lua_pop(L, 1);
// Register classes/prototypes
register_cel_class(L);
register_cels_class(L);
@ -273,11 +285,13 @@ Engine::Engine()
register_image_class(L);
register_image_iterator_class(L);
register_image_spec_class(L);
register_images_class(L);
register_layer_class(L);
register_layers_class(L);
register_palette_class(L);
register_palettes_class(L);
register_point_class(L);
register_range_class(L);
register_rect_class(L);
register_selection_class(L);
register_site_class(L);

View File

@ -15,7 +15,7 @@
#include "app/color.h"
#include "doc/frame.h"
#include "doc/object_id.h"
#include "doc/object_ids.h"
#include "gfx/fwd.h"
#include <cstdio>
@ -35,7 +35,10 @@ namespace doc {
}
namespace app {
class DocRange;
class Site;
namespace script {
enum class FileAccessMode {
@ -95,11 +98,16 @@ namespace app {
int push_image_iterator_function(lua_State* L, const doc::Image* image, int extraArgIndex);
void push_cel_image(lua_State* L, doc::Cel* cel);
void push_cels(lua_State* L, const doc::ObjectIds& cels);
void push_cels(lua_State* L, doc::Layer* layer);
void push_cels(lua_State* L, doc::Sprite* sprite);
void push_doc_range(lua_State* L, doc::Sprite* sprite, const DocRange& docRange);
void push_images(lua_State* L, const doc::ObjectIds& images);
void push_layers(lua_State* L, const doc::ObjectIds& layers);
void push_sprite_cel(lua_State* L, doc::Cel* cel);
void push_sprite_cels(lua_State* L, doc::Sprite* sprite);
void push_layer_cels(lua_State* L, doc::Layer* layer);
void push_sprite_frame(lua_State* L, doc::Sprite* sprite, doc::frame_t frame);
void push_sprite_frames(lua_State* L, doc::Sprite* sprite);
void push_sprite_frames(lua_State* L, doc::Sprite* sprite, const std::vector<doc::frame_t>& frames);
void push_sprite_layers(lua_State* L, doc::Sprite* sprite);
void push_sprite_palette(lua_State* L, doc::Sprite* sprite, doc::Palette* palette);
void push_sprite_palettes(lua_State* L, doc::Sprite* sprite);
@ -118,6 +126,7 @@ namespace app {
doc::Image* may_get_image_from_arg(lua_State* L, int index);
doc::Image* get_image_from_arg(lua_State* L, int index);
doc::Cel* get_image_cel_from_arg(lua_State* L, int index);
doc::frame_t get_frame_number_from_arg(lua_State* L, int index);
} // namespace script
} // namespace app

View File

@ -114,5 +114,14 @@ void push_sprite_frame(lua_State* L, Sprite* sprite, frame_t frame)
push_new<FrameObj>(L, sprite, frame);
}
doc::frame_t get_frame_number_from_arg(lua_State* L, int index)
{
auto obj = may_get_obj<FrameObj>(L, 1);
if (obj)
return obj->frame;
else
return lua_tointeger(L, index)-1;
}
} // namespace script
} // namespace app

View File

@ -23,9 +23,17 @@ namespace {
struct FramesObj {
ObjectId spriteId;
const std::vector<frame_t>* frames;
FramesObj(Sprite* sprite)
: spriteId(sprite->id()) {
: spriteId(sprite->id()),
frames(nullptr) {
}
FramesObj(Sprite* sprite, const std::vector<frame_t>& frames)
: spriteId(sprite->id()),
frames(new std::vector<frame_t>(frames)) {
}
FramesObj(const FramesObj&) = delete;
FramesObj& operator=(const FramesObj&) = delete;
@ -41,7 +49,10 @@ int Frames_gc(lua_State* L)
int Frames_len(lua_State* L)
{
auto obj = get_obj<FramesObj>(L, 1);
lua_pushinteger(L, obj->sprite(L)->totalFrames());
if (obj->frames)
lua_pushinteger(L, int(obj->frames->size()));
else
lua_pushinteger(L, obj->sprite(L)->totalFrames());
return 1;
}
@ -50,10 +61,19 @@ int Frames_index(lua_State* L)
auto obj = get_obj<FramesObj>(L, 1);
auto sprite = obj->sprite(L);
const int i = lua_tonumber(L, 2);
if (i >= 1 && i <= sprite->totalFrames())
push_sprite_frame(L, sprite, i-1);
else
lua_pushnil(L);
if (obj->frames) {
if (i >= 1 && i <= obj->frames->size())
push_sprite_frame(L, sprite, i-1);
else
lua_pushnil(L);
}
else {
if (i >= 1 && i <= sprite->totalFrames())
push_sprite_frame(L, sprite, i-1);
else
lua_pushnil(L);
}
return 1;
}
@ -79,5 +99,10 @@ void push_sprite_frames(lua_State* L, Sprite* sprite)
push_new<FramesObj>(L, sprite);
}
void push_sprite_frames(lua_State* L, doc::Sprite* sprite, const std::vector<doc::frame_t>& frames)
{
push_new<FramesObj>(L, sprite, frames);
}
} // namespace script
} // namespace app

View File

@ -0,0 +1,81 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/script/docobj.h"
#include "app/script/luacpp.h"
#include "doc/image.h"
#include "doc/object_ids.h"
namespace app {
namespace script {
using namespace doc;
namespace {
struct ImagesObj {
ObjectIds images;
ImagesObj(const ObjectIds& images)
: images(images) {
}
ImagesObj(const ImagesObj&) = delete;
ImagesObj& operator=(const ImagesObj&) = delete;
};
int Images_gc(lua_State* L)
{
get_obj<ImagesObj>(L, 1)->~ImagesObj();
return 0;
}
int Images_len(lua_State* L)
{
auto obj = get_obj<ImagesObj>(L, 1);
lua_pushinteger(L, obj->images.size());
return 1;
}
int Images_index(lua_State* L)
{
auto obj = get_obj<ImagesObj>(L, 1);
const int i = lua_tointeger(L, 2);
if (i >= 1 && i <= obj->images.size())
push_docobj<Image>(L, obj->images[i-1]);
else
lua_pushnil(L);
return 1;
}
const luaL_Reg Images_methods[] = {
{ "__gc", Images_gc },
{ "__len", Images_len },
{ "__index", Images_index },
{ nullptr, nullptr }
};
} // anonymous namespace
DEF_MTNAME(ImagesObj);
void register_images_class(lua_State* L)
{
using Images = ImagesObj;
REG_CLASS(L, Images);
}
void push_images(lua_State* L, const ObjectIds& images)
{
push_new<ImagesObj>(L, images);
}
} // namespace script
} // namespace app

View File

@ -167,7 +167,7 @@ int Layer_get_isExpanded(lua_State* L)
int Layer_get_cels(lua_State* L)
{
auto layer = get_docobj<Layer>(L, 1);
push_layer_cels(L, layer);
push_cels(L, layer);
return 1;
}

View File

@ -13,10 +13,9 @@
#include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "doc/layer.h"
#include "doc/object_ids.h"
#include "doc/sprite.h"
#include <vector>
namespace app {
namespace script {
@ -25,11 +24,16 @@ using namespace doc;
namespace {
struct LayersObj {
std::vector<ObjectId> layers;
ObjectIds layers;
LayersObj(Sprite* sprite) {
for (const Layer* layer : sprite->allLayers())
layers.push_back(layer->id());
}
LayersObj(const ObjectIds& layers)
: layers(layers) {
}
LayersObj(const LayersObj&) = delete;
LayersObj& operator=(const LayersObj&) = delete;
};
@ -80,5 +84,10 @@ void push_sprite_layers(lua_State* L, Sprite* sprite)
push_new<LayersObj>(L, sprite);
}
void push_layers(lua_State* L, const ObjectIds& layers)
{
push_new<LayersObj>(L, layers);
}
} // namespace script
} // namespace app

View File

@ -0,0 +1,208 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/doc_range.h"
#include "app/script/docobj.h"
#include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "app/util/range_utils.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/object_ids.h"
#include "doc/sprite.h"
#include <algorithm>
#include <set>
#include <vector>
namespace app {
namespace script {
namespace {
struct RangeObj { // This is like DocRange but referencing objects with IDs
DocRange::Type type;
ObjectId spriteId;
std::set<ObjectId> layers;
std::vector<frame_t> frames;
std::set<ObjectId> cels;
RangeObj(Sprite* sprite, const DocRange& docRange) {
spriteId = sprite->id();
type = docRange.type();
for (const Layer* layer : docRange.selectedLayers())
layers.insert(layer->id());
for (const frame_t frame : docRange.selectedFrames())
frames.push_back(frame);
// TODO improve this, in the best case we should defer layers,
// frames, and cels vectors when the properties are accessed, but
// it might not be possible because we have to save the IDs of the
// objects (and we cannot store the DocRange because it contains
// pointers instead of IDs).
for (Cel* cel : get_cels(sprite, docRange))
cels.insert(cel->id());
}
RangeObj(const RangeObj&) = delete;
RangeObj& operator=(const RangeObj&) = delete;
Sprite* sprite(lua_State* L) { return check_docobj(L, doc::get<Sprite>(spriteId)); }
bool contains(const Layer* layer) const {
return layers.find(layer->id()) != layers.end();
}
bool contains(const frame_t frame) const {
return std::find(frames.begin(), frames.end(), frame) != frames.end();
}
bool contains(const Cel* cel) const {
return cels.find(cel->id()) != cels.end();
}
};
int Range_gc(lua_State* L)
{
get_obj<RangeObj>(L, 1)->~RangeObj();
return 0;
}
int Range_get_sprite(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
push_docobj<Sprite>(L, obj->spriteId);
return 1;
}
int Range_get_type(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
lua_pushinteger(L, int(obj->type));
return 1;
}
int Range_contains(lua_State* L)
{
bool result = false;
auto obj = get_obj<RangeObj>(L, 1);
if (Layer* layer = may_get_docobj<Layer>(L, 2)) {
result = obj->contains(layer);
}
else if (Cel* cel = may_get_docobj<Cel>(L, 2)) {
result = obj->contains(cel);
}
else if (frame_t frame = get_frame_number_from_arg(L, 2)) {
result = obj->contains(frame);
}
lua_pushboolean(L, result);
return 1;
}
int Range_get_isEmpty(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
lua_pushboolean(L, obj->type == DocRange::kNone);
return 1;
}
int Range_get_layers(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
ObjectIds layers(obj->layers.size());
int i = 0;
for (auto id : obj->layers)
layers[i++] = id;
push_layers(L, layers);
return 1;
}
int Range_get_frames(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
push_sprite_frames(L, obj->sprite(L), obj->frames);
return 1;
}
int Range_get_cels(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
ObjectIds cels(obj->cels.size());
int i = 0;
for (auto id : obj->cels)
cels[i++] = id;
push_cels(L, cels);
return 1;
}
int Range_get_images(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
std::set<ObjectId> imagesSet;
for (auto celId : obj->cels) {
Cel* cel = check_docobj(L, doc::get<Cel>(celId));
imagesSet.insert(cel->image()->id());
}
ObjectIds images;
for (auto imageId : imagesSet)
images.push_back(imageId);
push_images(L, images);
return 1;
}
int Range_get_editableImages(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
std::set<ObjectId> imagesSet;
for (auto celId : obj->cels) {
Cel* cel = check_docobj(L, doc::get<Cel>(celId));
if (cel->layer()->isEditable())
imagesSet.insert(cel->image()->id());
}
ObjectIds images;
for (auto imageId : imagesSet)
images.push_back(imageId);
push_images(L, images);
return 1;
}
const luaL_Reg Range_methods[] = {
{ "__gc", Range_gc },
{ "contains", Range_contains },
{ nullptr, nullptr }
};
const Property Range_properties[] = {
{ "sprite", Range_get_sprite, nullptr },
{ "type", Range_get_type, nullptr },
{ "isEmpty", Range_get_isEmpty, nullptr },
{ "layers", Range_get_layers, nullptr },
{ "frames", Range_get_frames, nullptr },
{ "cels", Range_get_cels, nullptr },
{ "images", Range_get_images, nullptr },
{ "editableImages", Range_get_editableImages, nullptr },
{ nullptr, nullptr, nullptr }
};
} // anonymous namespace
DEF_MTNAME(RangeObj);
void register_range_class(lua_State* L)
{
using Range = RangeObj;
REG_CLASS(L, Range);
REG_CLASS_PROPERTIES(L, Range);
}
void push_doc_range(lua_State* L, Sprite* sprite, const DocRange& docRange)
{
push_new<RangeObj>(L, sprite, docRange);
}
} // namespace script
} // namespace app

View File

@ -505,7 +505,7 @@ int Sprite_get_layers(lua_State* L)
int Sprite_get_cels(lua_State* L)
{
auto sprite = get_docobj<Sprite>(L, 1);
push_sprite_cels(L, sprite);
push_cels(L, sprite);
return 1;
}

View File

@ -15,6 +15,7 @@
#include "app/script/docobj.h"
#include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "doc/object_ids.h"
#include <algorithm>
#include <iterator>
@ -28,11 +29,13 @@ using namespace doc;
namespace {
struct SpritesObj {
std::vector<ObjectId> docs;
ObjectIds docs;
SpritesObj(const Docs& docs) {
for (const Doc* doc : docs)
this->docs.push_back(doc->id());
}
SpritesObj(const SpritesObj&) = delete;
SpritesObj& operator=(const SpritesObj&) = delete;
};

View File

@ -24,9 +24,11 @@ namespace app {
using namespace doc;
// TODO the DocRange should be "iteratable" to replace this function
CelList get_unlocked_unique_cels(Sprite* sprite, const DocRange& inrange)
static CelList get_cels_templ(Sprite* sprite,
DocRange range,
const bool onlyUniqueCels,
const bool onlyUnlockedCel)
{
DocRange range = inrange;
CelList cels;
if (!range.convertToCels(sprite))
return cels;
@ -36,7 +38,7 @@ CelList get_unlocked_unique_cels(Sprite* sprite, const DocRange& inrange)
for (Layer* layer : range.selectedLayers()) {
if (!layer ||
!layer->isImage() ||
!layer->isEditable())
(onlyUnlockedCel && !layer->isEditable()))
continue;
LayerImage* layerImage = static_cast<LayerImage*>(layer);
@ -45,8 +47,11 @@ CelList get_unlocked_unique_cels(Sprite* sprite, const DocRange& inrange)
if (!cel)
continue;
if (visited.find(cel->data()->id()) == visited.end()) {
visited.insert(cel->data()->id());
if (!onlyUniqueCels ||
visited.find(cel->data()->id()) == visited.end()) {
if (onlyUniqueCels)
visited.insert(cel->data()->id());
cels.push_back(cel);
}
}
@ -54,4 +59,19 @@ CelList get_unlocked_unique_cels(Sprite* sprite, const DocRange& inrange)
return cels;
}
CelList get_cels(doc::Sprite* sprite, const DocRange& range)
{
return get_cels_templ(sprite, range, false, false);
}
CelList get_unique_cels(Sprite* sprite, const DocRange& range)
{
return get_cels_templ(sprite, range, true, false);
}
CelList get_unlocked_unique_cels(Sprite* sprite, const DocRange& range)
{
return get_cels_templ(sprite, range, true, true);
}
} // namespace app

View File

@ -21,6 +21,8 @@ namespace app {
class DocRange;
doc::CelList get_cels(doc::Sprite* sprite, const DocRange& range);
doc::CelList get_unique_cels(doc::Sprite* sprite, const DocRange& range);
doc::CelList get_unlocked_unique_cels(doc::Sprite* sprite, const DocRange& range);
} // namespace app

21
src/doc/object_ids.h Normal file
View File

@ -0,0 +1,21 @@
// Aseprite Document Library
// Copyright (c) 2018 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_OBJECT_IDS_H_INCLUDED
#define DOC_OBJECT_IDS_H_INCLUDED
#pragma once
#include "doc/object_id.h"
#include <vector>
namespace doc {
typedef std::vector<ObjectId> ObjectIds;
} // namespace doc
#endif // DOC_OBJECT_IDS_H_INCLUDED