Get rid of DECLARE_LUACLASS
[lsnes.git] / src / lua / gui-tilemap.cpp
blob66a96d238a5e33989b5a51e8658490fb32a1a51f
1 #include "lua/internal.hpp"
2 #include "core/framebuffer.hpp"
3 #include "library/framebuffer.hpp"
4 #include "library/png-codec.hpp"
5 #include "library/string.hpp"
6 #include "library/threadtypes.hpp"
7 #include "library/zip.hpp"
8 #include "lua/bitmap.hpp"
9 #include <vector>
10 #include <sstream>
12 namespace
14 struct tilemap_entry
16 tilemap_entry()
19 void erase()
21 b.clear();
22 d.clear();
23 p.clear();
25 lua_obj_pin<lua_bitmap> b;
26 lua_obj_pin<lua_dbitmap> d;
27 lua_obj_pin<lua_palette> p;
30 struct tilemap
32 tilemap(lua_state& L, size_t _width, size_t _height, size_t _cwidth, size_t _cheight);
33 ~tilemap()
35 umutex_class h(mutex);
36 render_kill_request(this);
38 int draw(lua_state& L, const std::string& fname);
39 int get(lua_state& L, const std::string& fname)
41 umutex_class h(mutex);
42 uint32_t x = L.get_numeric_argument<uint32_t>(2, fname.c_str());
43 uint32_t y = L.get_numeric_argument<uint32_t>(3, fname.c_str());
44 if(x >= width || y >= height)
45 return 0;
46 tilemap_entry& e = map[y * width + x];
47 if(e.b) {
48 e.b.luapush(L);
49 e.p.luapush(L);
50 return 2;
51 } else if(e.d) {
52 e.d.luapush(L);
53 return 1;
54 } else
55 return 0;
57 int set(lua_state& L, const std::string& fname)
59 umutex_class h(mutex);
60 uint32_t x = L.get_numeric_argument<uint32_t>(2, fname.c_str());
61 uint32_t y = L.get_numeric_argument<uint32_t>(3, fname.c_str());
62 if(x >= width || y >= height)
63 return 0;
64 tilemap_entry& e = map[y * width + x];
65 if(lua_class<lua_dbitmap>::is(L, 4)) {
66 auto d = lua_class<lua_dbitmap>::pin(L, 4, fname.c_str());
67 e.erase();
68 e.d = d;
69 } else if(lua_class<lua_bitmap>::is(L, 4)) {
70 auto b = lua_class<lua_bitmap>::pin(L, 4, fname.c_str());
71 auto p = lua_class<lua_palette>::pin(L, 5, fname.c_str());
72 e.erase();
73 e.b = b;
74 e.p = p;
75 } else if(L.type(4) == LUA_TNIL || L.type(4) == LUA_TNONE) {
76 e.erase();
77 } else
78 throw std::runtime_error("Expected BITMAP, DBITMAP or nil as argument 4 to "
79 + fname);
80 return 0;
82 int getsize(lua_state& L, const std::string& fname)
84 L.pushnumber(width);
85 L.pushnumber(height);
86 return 2;
88 int getcsize(lua_state& L, const std::string& fname)
90 L.pushnumber(cwidth);
91 L.pushnumber(cheight);
92 return 2;
94 size_t calcshift(size_t orig, int32_t shift, size_t dimension, size_t offset, bool circular)
96 if(circular) {
97 orig -= offset;
98 //Now the widow is scaled [0,dimension).
99 if(shift >= 0)
100 orig = (orig + shift) % dimension;
101 else {
102 orig += shift;
103 while(orig > dimension) {
104 //It overflowed.
105 orig += dimension;
108 orig += offset;
109 return orig;
110 } else
111 return orig + shift;
113 int scroll(lua_state& L, const std::string& fname)
115 umutex_class mh(mutex);
116 int32_t ox = -L.get_numeric_argument<int32_t>(2, fname.c_str());
117 int32_t oy = -L.get_numeric_argument<int32_t>(3, fname.c_str());
118 size_t x0 = 0, y0 = 0, w = width, h = height;
119 L.get_numeric_argument<size_t>(4, x0, fname.c_str());
120 L.get_numeric_argument<size_t>(5, y0, fname.c_str());
121 L.get_numeric_argument<size_t>(6, w, fname.c_str());
122 L.get_numeric_argument<size_t>(7, h, fname.c_str());
123 bool circx = (L.type(8) == LUA_TBOOLEAN && L.toboolean(8));
124 bool circy = (L.type(9) == LUA_TBOOLEAN && L.toboolean(9));
125 if(x0 > width || x0 + w > width || x0 + w < x0 || y0 > height || y0 + h > height ||
126 y0 + h < y0)
127 throw std::runtime_error("Scroll window out of range");
128 if(!ox && !oy) return 0;
129 std::vector<tilemap_entry> tmp;
130 tmp.resize(w * h);
131 for(size_t _y = 0; _y < h; _y++) {
132 size_t y = _y + y0;
133 size_t sy = calcshift(y, oy, h, y0, circy);
134 if(sy < y0 || sy >= y0 + h)
135 continue;
136 for(size_t _x = 0; _x < w; _x++) {
137 size_t x = _x + x0;
138 size_t sx = calcshift(x, ox, w, x0, circx);
139 if(sx < x0 || sx >= x0 + w)
140 continue;
141 else
142 tmp[_y * w + _x] = map[sy * width + sx];
145 for(size_t _y = 0; _y < h; _y++)
146 for(size_t _x = 0; _x < w; _x++)
147 map[(_y + y0) * width + (_x + x0)] = tmp[_y * w + _x];
148 return 0;
150 std::string print()
152 return (stringfmt() << width << "*" << height << " (cell " << cwidth << "*" << cheight
153 << ")").str();
155 size_t width;
156 size_t height;
157 size_t cwidth;
158 size_t cheight;
159 std::vector<tilemap_entry> map;
160 mutex_class mutex;
163 struct render_object_tilemap : public framebuffer::object
165 render_object_tilemap(int32_t _x, int32_t _y, int32_t _x0, int32_t _y0, uint32_t _w,
166 uint32_t _h, lua_obj_pin<tilemap> _map)
167 : x(_x), y(_y), x0(_x0), y0(_y0), w(_w), h(_h), map(_map) {}
168 ~render_object_tilemap() throw()
171 bool kill_request(void* obj) throw()
173 return kill_request_ifeq(map.object(), obj);
175 template<bool T> void composite_op(struct framebuffer::fb<T>& scr) throw()
177 tilemap& _map = *map;
178 umutex_class h(_map.mutex);
179 for(size_t ty = 0; ty < _map.height; ty++) {
180 size_t basey = _map.cheight * ty;
181 for(size_t tx = 0; tx < _map.width; tx++) {
182 size_t basex = _map.cwidth * tx;
183 composite_op(scr, _map.map[ty * _map.width + tx], basex, basey);
187 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, int32_t xp,
188 int32_t yp, int32_t xmin, int32_t xmax, int32_t ymin, int32_t ymax, lua_dbitmap& d) throw()
190 if(xmin >= xmax || ymin >= ymax) return;
191 for(auto& c : d.pixels)
192 c.set_palette(scr);
194 for(int32_t r = ymin; r < ymax; r++) {
195 typename framebuffer::fb<T>::element_t* rptr = scr.rowptr(yp + r);
196 size_t eptr = xp + xmin;
197 for(int32_t c = xmin; c < xmax; c++, eptr++)
198 d.pixels[r * d.width + c].apply(rptr[eptr]);
201 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, int32_t xp,
202 int32_t yp, int32_t xmin, int32_t xmax, int32_t ymin, int32_t ymax, lua_bitmap& b,
203 lua_palette& p)
204 throw()
206 if(xmin >= xmax || ymin >= ymax) return;
207 p.palette_mutex.lock();
208 framebuffer::color* palette = &p.colors[0];
209 for(auto& c : p.colors)
210 c.set_palette(scr);
211 size_t pallim = p.colors.size();
213 for(int32_t r = ymin; r < ymax; r++) {
214 typename framebuffer::fb<T>::element_t* rptr = scr.rowptr(yp + r);
215 size_t eptr = xp + xmin;
216 for(int32_t c = xmin; c < xmax; c++, eptr++) {
217 uint16_t i = b.pixels[r * b.width + c];
218 if(i < pallim)
219 palette[i].apply(rptr[eptr]);
222 p.palette_mutex.unlock();
224 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, tilemap_entry& e, int32_t bx,
225 int32_t by) throw()
227 size_t _w, _h;
228 if(e.b) {
229 _w = e.b->width;
230 _h = e.b->height;
231 } else if(e.d) {
232 _w = e.d->width;
233 _h = e.d->height;
234 } else
235 return;
236 //Calculate notional screen coordinates for the tile.
237 int32_t scrx = x + scr.get_origin_x() + bx - x0;
238 int32_t scry = y + scr.get_origin_y() + by - y0;
239 int32_t scrw = scr.get_width();
240 int32_t scrh = scr.get_height();
241 int32_t xmin = 0;
242 int32_t xmax = _w;
243 int32_t ymin = 0;
244 int32_t ymax = _h;
245 clip(scrx, scrw, x + scr.get_origin_x(), w, xmin, xmax);
246 clip(scry, scrh, y + scr.get_origin_y(), h, ymin, ymax);
247 if(e.b)
248 composite_op(scr, scrx, scry, xmin, xmax, ymin, ymax, *e.b, *e.p);
249 else if(e.d)
250 composite_op(scr, scrx, scry, xmin, xmax, ymin, ymax, *e.d);
252 //scrc + cmin >= 0 and scrc + cmax <= scrd (Clip on screen).
253 //scrc + cmin >= bc and scrc + cmax <= bc + d (Clip on texture).
254 void clip(int32_t scrc, int32_t scrd, int32_t bc, int32_t d, int32_t& cmin, int32_t& cmax)
256 if(scrc + cmin < 0)
257 cmin = -scrc;
258 if(scrc + cmax > scrd)
259 cmax = scrd - scrc;
260 if(scrc + cmin < bc)
261 cmin = bc - scrc;
262 if(scrc + cmax > bc + d)
263 cmax = bc + d - scrc;
265 void operator()(struct framebuffer::fb<false>& x) throw() { composite_op(x); }
266 void operator()(struct framebuffer::fb<true>& x) throw() { composite_op(x); }
267 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
268 private:
269 int32_t x;
270 int32_t y;
271 int32_t x0;
272 int32_t y0;
273 uint32_t w;
274 uint32_t h;
275 lua_obj_pin<tilemap> map;
278 int tilemap::draw(lua_state& L, const std::string& fname)
280 if(!lua_render_ctx)
281 return 0;
282 uint32_t x = L.get_numeric_argument<int32_t>(2, fname.c_str());
283 uint32_t y = L.get_numeric_argument<int32_t>(3, fname.c_str());
284 int32_t x0 = 0, y0 = 0;
285 uint32_t w = width * cwidth, h = height * cheight;
286 L.get_numeric_argument<int32_t>(4, x0, fname.c_str());
287 L.get_numeric_argument<int32_t>(5, y0, fname.c_str());
288 L.get_numeric_argument<uint32_t>(6, w, fname.c_str());
289 L.get_numeric_argument<uint32_t>(7, h, fname.c_str());
290 auto t = lua_class<tilemap>::pin(L, 1, fname.c_str());
291 lua_render_ctx->queue->create_add<render_object_tilemap>(x, y, x0, y0, w, h, t);
292 return 0;
295 function_ptr_luafun gui_ctilemap(lua_func_misc, "gui.tilemap", [](lua_state& LS, const std::string& fname) ->
296 int {
297 uint32_t w = LS.get_numeric_argument<uint32_t>(1, fname.c_str());
298 uint32_t h = LS.get_numeric_argument<uint32_t>(2, fname.c_str());
299 uint32_t px = LS.get_numeric_argument<uint32_t>(3, fname.c_str());
300 uint32_t py = LS.get_numeric_argument<uint32_t>(4, fname.c_str());
301 tilemap* t = lua_class<tilemap>::create(LS, w, h, px, py);
302 return 1;
305 lua_class<tilemap> class_tilemap("TILEMAP");
307 tilemap::tilemap(lua_state& L, size_t _width, size_t _height, size_t _cwidth, size_t _cheight)
308 : width(_width), height(_height), cwidth(_cwidth), cheight(_cheight)
310 objclass<tilemap>().bind_multi(L, {
311 {"draw", &tilemap::draw},
312 {"set", &tilemap::set},
313 {"get", &tilemap::get},
314 {"scroll", &tilemap::scroll},
315 {"getsize", &tilemap::getsize},
316 {"getcsize", &tilemap::getcsize},
318 if(width * height / height != width)
319 throw std::bad_alloc();
320 map.resize(width * height);