Add base support for Lua object overcommit
[lsnes.git] / src / lua / gui-tilemap.cpp
blob0cd3aaf1a4cf9bb3fcc1d9f9e9c00f959b21209d
1 #include "lua/internal.hpp"
2 #include "core/framebuffer.hpp"
3 #include "library/framebuffer.hpp"
4 #include "library/png.hpp"
5 #include "library/string.hpp"
6 #include "library/threads.hpp"
7 #include "library/lua-framebuffer.hpp"
8 #include "library/zip.hpp"
9 #include "lua/bitmap.hpp"
10 #include <vector>
11 #include <sstream>
13 namespace
15 struct tilemap_entry
17 tilemap_entry()
20 void erase()
22 b.clear();
23 d.clear();
24 p.clear();
26 lua::objpin<lua_bitmap> b;
27 lua::objpin<lua_dbitmap> d;
28 lua::objpin<lua_palette> p;
31 struct tilemap
33 tilemap(lua::state& L, size_t _width, size_t _height, size_t _cwidth, size_t _cheight);
34 static size_t overcommit(size_t _width, size_t _height, size_t _cwidth, size_t _cheight) { return 0; }
35 ~tilemap()
37 threads::alock h(lock);
38 render_kill_request(this);
40 static int create(lua::state& L, lua::parameters& P);
41 int draw(lua::state& L, lua::parameters& P);
42 int get(lua::state& L, lua::parameters& P)
44 uint32_t x, y;
46 P(P.skipped(), x, y);
48 threads::alock h(lock);
49 if(x >= width || y >= height)
50 return 0;
51 tilemap_entry& e = map[y * width + x];
52 if(e.b) {
53 e.b.luapush(L);
54 e.p.luapush(L);
55 return 2;
56 } else if(e.d) {
57 e.d.luapush(L);
58 return 1;
59 } else
60 return 0;
62 int set(lua::state& L, lua::parameters& P)
64 uint32_t x, y;
66 P(P.skipped(), x, y);
67 int oidx = P.skip();
69 threads::alock h(lock);
70 if(x >= width || y >= height)
71 return 0;
72 tilemap_entry& e = map[y * width + x];
73 if(P.is<lua_dbitmap>(oidx)) {
74 auto d = P.arg<lua::objpin<lua_dbitmap>>(oidx);
75 e.erase();
76 e.d = d;
77 } else if(P.is<lua_bitmap>(oidx)) {
78 auto b = P.arg<lua::objpin<lua_bitmap>>(oidx);
79 auto p = P.arg<lua::objpin<lua_palette>>(oidx + 1);
80 e.erase();
81 e.b = b;
82 e.p = p;
83 } else if(P.is_novalue(oidx)) {
84 e.erase();
85 } else
86 P.expected("BITMAP, DBITMAP or nil", oidx);
87 return 0;
89 int getsize(lua::state& L, lua::parameters& P)
91 L.pushnumber(width);
92 L.pushnumber(height);
93 return 2;
95 int getcsize(lua::state& L, lua::parameters& P)
97 L.pushnumber(cwidth);
98 L.pushnumber(cheight);
99 return 2;
101 size_t calcshift(size_t orig, int32_t shift, size_t dimension, size_t offset, bool circular)
103 if(circular) {
104 orig -= offset;
105 //Now the widow is scaled [0,dimension).
106 if(shift >= 0)
107 orig = (orig + shift) % dimension;
108 else {
109 orig += shift;
110 while(orig > dimension) {
111 //It overflowed.
112 orig += dimension;
115 orig += offset;
116 return orig;
117 } else
118 return orig + shift;
120 int scroll(lua::state& L, lua::parameters& P)
122 int32_t ox, oy;
123 size_t x0, y0, w, h;
124 bool circx, circy;
126 P(P.skipped(), ox, oy, P.optional(x0, 0), P.optional(y0, 0), P.optional(w, width),
127 P.optional(h, height), P.optional(circx, false), P.optional(circy, false));
129 threads::alock mh(lock);
130 if(x0 > width || x0 + w > width || x0 + w < x0 || y0 > height || y0 + h > height ||
131 y0 + h < y0)
132 throw std::runtime_error("Scroll window out of range");
133 if(!ox && !oy) return 0;
134 std::vector<tilemap_entry> tmp;
135 tmp.resize(w * h);
136 for(size_t _y = 0; _y < h; _y++) {
137 size_t y = _y + y0;
138 size_t sy = calcshift(y, oy, h, y0, circy);
139 if(sy < y0 || sy >= y0 + h)
140 continue;
141 for(size_t _x = 0; _x < w; _x++) {
142 size_t x = _x + x0;
143 size_t sx = calcshift(x, ox, w, x0, circx);
144 if(sx < x0 || sx >= x0 + w)
145 continue;
146 else
147 tmp[_y * w + _x] = map[sy * width + sx];
150 for(size_t _y = 0; _y < h; _y++)
151 for(size_t _x = 0; _x < w; _x++)
152 map[(_y + y0) * width + (_x + x0)] = tmp[_y * w + _x];
153 return 0;
155 std::string print()
157 return (stringfmt() << width << "*" << height << " (cell " << cwidth << "*" << cheight
158 << ")").str();
160 size_t width;
161 size_t height;
162 size_t cwidth;
163 size_t cheight;
164 std::vector<tilemap_entry> map;
165 threads::lock lock;
168 struct render_object_tilemap : public framebuffer::object
170 render_object_tilemap(int32_t _x, int32_t _y, int32_t _x0, int32_t _y0, uint32_t _w,
171 uint32_t _h, lua::objpin<tilemap> _map)
172 : x(_x), y(_y), x0(_x0), y0(_y0), w(_w), h(_h), map(_map) {}
173 ~render_object_tilemap() throw()
176 bool kill_request(void* obj) throw()
178 return kill_request_ifeq(map.object(), obj);
180 template<bool T> void composite_op(struct framebuffer::fb<T>& scr) throw()
182 tilemap& _map = *map;
183 threads::alock h(_map.lock);
184 for(size_t ty = 0; ty < _map.height; ty++) {
185 size_t basey = _map.cheight * ty;
186 for(size_t tx = 0; tx < _map.width; tx++) {
187 size_t basex = _map.cwidth * tx;
188 composite_op(scr, _map.map[ty * _map.width + tx], basex, basey);
192 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, int32_t xp,
193 int32_t yp, int32_t xmin, int32_t xmax, int32_t ymin, int32_t ymax, lua_dbitmap& d) throw()
195 if(xmin >= xmax || ymin >= ymax) return;
197 for(int32_t r = ymin; r < ymax; r++) {
198 typename framebuffer::fb<T>::element_t* rptr = scr.rowptr(yp + r);
199 size_t eptr = xp + xmin;
200 for(int32_t c = xmin; c < xmax; c++, eptr++)
201 d.pixels[r * d.width + c].apply(rptr[eptr]);
204 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, int32_t xp,
205 int32_t yp, int32_t xmin, int32_t xmax, int32_t ymin, int32_t ymax, lua_bitmap& b,
206 lua_palette& p)
207 throw()
209 if(xmin >= xmax || ymin >= ymax) return;
210 p.palette_mutex.lock();
211 framebuffer::color* palette = &p.colors[0];
212 size_t pallim = p.colors.size();
214 for(int32_t r = ymin; r < ymax; r++) {
215 typename framebuffer::fb<T>::element_t* rptr = scr.rowptr(yp + r);
216 size_t eptr = xp + xmin;
217 for(int32_t c = xmin; c < xmax; c++, eptr++) {
218 uint16_t i = b.pixels[r * b.width + c];
219 if(i < pallim)
220 palette[i].apply(rptr[eptr]);
223 p.palette_mutex.unlock();
225 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, tilemap_entry& e, int32_t bx,
226 int32_t by) throw()
228 size_t _w, _h;
229 if(e.b) {
230 _w = e.b->width;
231 _h = e.b->height;
232 } else if(e.d) {
233 _w = e.d->width;
234 _h = e.d->height;
235 } else
236 return;
237 //Calculate notional screen coordinates for the tile.
238 int32_t scrx = x + scr.get_origin_x() + bx - x0;
239 int32_t scry = y + scr.get_origin_y() + by - y0;
240 int32_t scrw = scr.get_width();
241 int32_t scrh = scr.get_height();
242 int32_t xmin = 0;
243 int32_t xmax = _w;
244 int32_t ymin = 0;
245 int32_t ymax = _h;
246 clip(scrx, scrw, x + scr.get_origin_x(), w, xmin, xmax);
247 clip(scry, scrh, y + scr.get_origin_y(), h, ymin, ymax);
248 if(e.b)
249 composite_op(scr, scrx, scry, xmin, xmax, ymin, ymax, *e.b, *e.p);
250 else if(e.d)
251 composite_op(scr, scrx, scry, xmin, xmax, ymin, ymax, *e.d);
253 //scrc + cmin >= 0 and scrc + cmax <= scrd (Clip on screen).
254 //scrc + cmin >= bc and scrc + cmax <= bc + d (Clip on texture).
255 void clip(int32_t scrc, int32_t scrd, int32_t bc, int32_t d, int32_t& cmin, int32_t& cmax)
257 if(scrc + cmin < 0)
258 cmin = -scrc;
259 if(scrc + cmax > scrd)
260 cmax = scrd - scrc;
261 if(scrc + cmin < bc)
262 cmin = bc - scrc;
263 if(scrc + cmax > bc + d)
264 cmax = bc + d - scrc;
266 void operator()(struct framebuffer::fb<false>& x) throw() { composite_op(x); }
267 void operator()(struct framebuffer::fb<true>& x) throw() { composite_op(x); }
268 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
269 private:
270 int32_t x;
271 int32_t y;
272 int32_t x0;
273 int32_t y0;
274 uint32_t w;
275 uint32_t h;
276 lua::objpin<tilemap> map;
279 int tilemap::draw(lua::state& L, lua::parameters& P)
281 uint32_t x, y, w, h;
282 int32_t x0, y0;
283 lua::objpin<tilemap> t;
285 if(!lua_render_ctx) return 0;
287 P(t, x, y, P.optional(x0, 0), P.optional(y0, 0), P.optional(w, width * cwidth),
288 P.optional(h, height * cheight));
290 lua_render_ctx->queue->create_add<render_object_tilemap>(x, y, x0, y0, w, h, t);
291 return 0;
294 int tilemap::create(lua::state& L, lua::parameters& P)
296 uint32_t w, h, px, py;
298 P(w, h, px, py);
300 lua::_class<tilemap>::create(L, w, h, px, py);
301 return 1;
304 lua::_class<tilemap> class_tilemap(lua_class_gui, "TILEMAP", {
305 {"new", tilemap::create},
306 }, {
307 {"draw", &tilemap::draw},
308 {"set", &tilemap::set},
309 {"get", &tilemap::get},
310 {"scroll", &tilemap::scroll},
311 {"getsize", &tilemap::getsize},
312 {"getcsize", &tilemap::getcsize},
315 tilemap::tilemap(lua::state& L, size_t _width, size_t _height, size_t _cwidth, size_t _cheight)
316 : width(_width), height(_height), cwidth(_cwidth), cheight(_cheight)
318 if(width * height / height != width)
319 throw std::bad_alloc();
320 map.resize(width * height);