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 | |
23 | namespace app { |
24 | namespace script { |
25 | |
26 | namespace { |
27 | |
28 | template<typename ImageTraits> |
29 | struct 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 | |
42 | using RgbImageIterator = ImageIteratorObj<RgbTraits>; |
43 | using GrayscaleImageIterator = ImageIteratorObj<GrayscaleTraits>; |
44 | using IndexedImageIterator = ImageIteratorObj<IndexedTraits>; |
45 | using TilemapImageIterator = ImageIteratorObj<TilemapTraits>; |
46 | |
47 | template<typename ImageTraits> |
48 | int ImageIterator_gc(lua_State* L) |
49 | { |
50 | get_obj<ImageIteratorObj<ImageTraits>>(L, 1)->~ImageIteratorObj<ImageTraits>(); |
51 | return 0; |
52 | } |
53 | |
54 | template<typename ImageTraits> |
55 | int 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 | |
70 | template<typename ImageTraits> |
71 | int 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 | |
96 | DEFINE_METHODS(Rgb); |
97 | DEFINE_METHODS(Grayscale); |
98 | DEFINE_METHODS(Indexed); |
99 | DEFINE_METHODS(Tilemap); |
100 | |
101 | } // anonymous namespace |
102 | |
103 | DEF_MTNAME(ImageIteratorObj<RgbTraits>); |
104 | DEF_MTNAME(ImageIteratorObj<GrayscaleTraits>); |
105 | DEF_MTNAME(ImageIteratorObj<IndexedTraits>); |
106 | DEF_MTNAME(ImageIteratorObj<TilemapTraits>); |
107 | |
108 | void 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 | |
116 | template<typename ImageTrais> |
117 | static 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. |
134 | static int image_iterator_do_nothing(lua_State* L) |
135 | { |
136 | lua_pushnil(L); |
137 | return 1; |
138 | } |
139 | |
140 | int push_image_iterator_function(lua_State* L, const doc::Image* image, int ) |
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 | |