1// Aseprite
2// Copyright (C) 2018 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/copy_region.h"
13#include "app/script/engine.h"
14#include "app/script/luacpp.h"
15#include "app/tx.h"
16#include "doc/algorithm/shrink_bounds.h"
17#include "doc/cel.h"
18#include "doc/image.h"
19#include "doc/image_impl.h"
20#include "doc/image_ref.h"
21#include "doc/primitives.h"
22
23namespace app {
24namespace script {
25
26namespace {
27
28template<typename ImageTraits>
29struct ImageIteratorObj {
30 typename doc::LockImageBits<ImageTraits> bits;
31 typename doc::LockImageBits<ImageTraits>::iterator begin, next, end;
32 ImageIteratorObj(const doc::Image* image, const gfx::Rect& bounds)
33 : bits(image, bounds),
34 begin(bits.begin()),
35 next(begin),
36 end(bits.end()) {
37 }
38 ImageIteratorObj(const ImageIteratorObj&) = delete;
39 ImageIteratorObj& operator=(const ImageIteratorObj&) = delete;
40};
41
42using RgbImageIterator = ImageIteratorObj<RgbTraits>;
43using GrayscaleImageIterator = ImageIteratorObj<GrayscaleTraits>;
44using IndexedImageIterator = ImageIteratorObj<IndexedTraits>;
45using TilemapImageIterator = ImageIteratorObj<TilemapTraits>;
46
47template<typename ImageTraits>
48int ImageIterator_gc(lua_State* L)
49{
50 get_obj<ImageIteratorObj<ImageTraits>>(L, 1)->~ImageIteratorObj<ImageTraits>();
51 return 0;
52}
53
54template<typename ImageTraits>
55int ImageIterator_call(lua_State* L)
56{
57 auto obj = get_obj<ImageIteratorObj<ImageTraits>>(L, 1);
58 // Get value
59 if (lua_isnone(L, 2)) {
60 lua_pushinteger(L, *obj->begin);
61 return 1;
62 }
63 // Set value
64 else {
65 *obj->begin = lua_tointeger(L, 2);
66 return 1;
67 }
68}
69
70template<typename ImageTraits>
71int ImageIterator_index(lua_State* L)
72{
73 auto obj = get_obj<ImageIteratorObj<ImageTraits>>(L, 1);
74 const char* field = lua_tostring(L, 2);
75 if (field) {
76 if (field[0] == 'x' && field[1] == 0) {
77 lua_pushinteger(L, obj->begin.x());
78 return 1;
79 }
80 else if (field[0] == 'y' && field[1] == 0) {
81 lua_pushinteger(L, obj->begin.y());
82 return 1;
83 }
84 }
85 return 0;
86}
87
88#define DEFINE_METHODS(Prefix) \
89 const luaL_Reg Prefix##ImageIterator_methods[] = { \
90 { "__index", ImageIterator_index<Prefix##Traits> }, \
91 { "__call", ImageIterator_call<Prefix##Traits> }, \
92 { "__gc", ImageIterator_gc<Prefix##Traits> }, \
93 { nullptr, nullptr } \
94 }
95
96DEFINE_METHODS(Rgb);
97DEFINE_METHODS(Grayscale);
98DEFINE_METHODS(Indexed);
99DEFINE_METHODS(Tilemap);
100
101} // anonymous namespace
102
103DEF_MTNAME(ImageIteratorObj<RgbTraits>);
104DEF_MTNAME(ImageIteratorObj<GrayscaleTraits>);
105DEF_MTNAME(ImageIteratorObj<IndexedTraits>);
106DEF_MTNAME(ImageIteratorObj<TilemapTraits>);
107
108void register_image_iterator_class(lua_State* L)
109{
110 REG_CLASS(L, RgbImageIterator);
111 REG_CLASS(L, GrayscaleImageIterator);
112 REG_CLASS(L, IndexedImageIterator);
113 REG_CLASS(L, TilemapImageIterator);
114}
115
116template<typename ImageTrais>
117static int image_iterator_step_closure(lua_State* L)
118{
119 int idx = lua_upvalueindex(1);
120 auto obj = get_obj<ImageIteratorObj<ImageTrais>>(L, idx);
121 obj->begin = obj->next;
122 if (obj->begin != obj->end) {
123 ++obj->next;
124 lua_pushvalue(L, idx);
125 }
126 else {
127 lua_pushnil(L);
128 }
129 return 1;
130}
131
132// Used to when an area outside the image canvas is specified, so
133// there is pixel to be iterated.
134static int image_iterator_do_nothing(lua_State* L)
135{
136 lua_pushnil(L);
137 return 1;
138}
139
140int push_image_iterator_function(lua_State* L, const doc::Image* image, int extraArgIndex)
141{
142 gfx::Rect bounds = image->bounds();
143
144 if (!lua_isnone(L, extraArgIndex)) {
145 auto specificBounds = convert_args_into_rect(L, extraArgIndex);
146 if (!specificBounds.isEmpty())
147 bounds &= specificBounds;
148 }
149
150 if (bounds.isEmpty()) {
151 lua_pushcclosure(L, image_iterator_do_nothing, 0);
152 return 1;
153 }
154
155 switch (image->pixelFormat()) {
156 case IMAGE_RGB:
157 push_new<RgbImageIterator>(L, image, bounds);
158 lua_pushcclosure(L, image_iterator_step_closure<doc::RgbTraits>, 1);
159 return 1;
160 case IMAGE_GRAYSCALE:
161 push_new<GrayscaleImageIterator>(L, image, bounds);
162 lua_pushcclosure(L, image_iterator_step_closure<doc::GrayscaleTraits>, 1);
163 return 1;
164 case IMAGE_INDEXED:
165 push_new<IndexedImageIterator>(L, image, bounds);
166 lua_pushcclosure(L, image_iterator_step_closure<doc::IndexedTraits>, 1);
167 return 1;
168 case IMAGE_TILEMAP:
169 push_new<TilemapImageIterator>(L, image, bounds);
170 lua_pushcclosure(L, image_iterator_step_closure<doc::TilemapTraits>, 1);
171 return 1;
172 default:
173 return 0;
174 }
175}
176
177} // namespace script
178} // namespace app
179