Use master state for trampolines
[lsnes.git] / src / lua / gui-tilemap.cpp
blobca499d431ad8a262c974946eacd596afb4bb7ac3
1 #include "lua/internal.hpp"
2 #include "core/framebuffer.hpp"
3 #include "core/instance.hpp"
4 #include "library/png.hpp"
5 #include "library/range.hpp"
6 #include "library/string.hpp"
7 #include "library/threads.hpp"
8 #include "library/lua-framebuffer.hpp"
9 #include "library/zip.hpp"
10 #include "lua/bitmap.hpp"
11 #include <vector>
12 #include <sstream>
14 namespace
16 struct tilemap_entry
18 tilemap_entry()
21 void erase()
23 b.clear();
24 d.clear();
25 p.clear();
27 lua::objpin<lua_bitmap> b;
28 lua::objpin<lua_dbitmap> d;
29 lua::objpin<lua_palette> p;
32 struct tilemap
34 tilemap(lua::state& L, size_t _width, size_t _height, size_t _cwidth, size_t _cheight);
35 static size_t overcommit(size_t _width, size_t _height, size_t _cwidth, size_t _cheight) {
36 return lua::overcommit_std_align + 2 * sizeof(tilemap_entry) * (size_t)_width * _height;
38 ~tilemap()
40 threads::alock h(lock);
41 CORE().fbuf->render_kill_request(this);
43 static int create(lua::state& L, lua::parameters& P);
44 template<bool outside> int draw(lua::state& L, lua::parameters& P);
45 int get(lua::state& L, lua::parameters& P)
47 uint32_t x, y;
49 P(P.skipped(), x, y);
51 threads::alock h(lock);
52 if(x >= width || y >= height)
53 return 0;
54 tilemap_entry& e = map[y * width + x];
55 if(e.b) {
56 e.b.luapush(L);
57 e.p.luapush(L);
58 return 2;
59 } else if(e.d) {
60 e.d.luapush(L);
61 return 1;
62 } else
63 return 0;
65 int set(lua::state& L, lua::parameters& P)
67 uint32_t x, y;
69 P(P.skipped(), x, y);
70 int oidx = P.skip();
72 threads::alock h(lock);
73 if(x >= width || y >= height)
74 return 0;
75 tilemap_entry& e = map[y * width + x];
76 if(P.is<lua_dbitmap>(oidx)) {
77 auto d = P.arg<lua::objpin<lua_dbitmap>>(oidx);
78 e.erase();
79 e.d = d;
80 } else if(P.is<lua_bitmap>(oidx)) {
81 auto b = P.arg<lua::objpin<lua_bitmap>>(oidx);
82 auto p = P.arg<lua::objpin<lua_palette>>(oidx + 1);
83 e.erase();
84 e.b = b;
85 e.p = p;
86 } else if(P.is_novalue(oidx)) {
87 e.erase();
88 } else
89 P.expected("BITMAP, DBITMAP or nil", oidx);
90 return 0;
92 int getsize(lua::state& L, lua::parameters& P)
94 L.pushnumber(width);
95 L.pushnumber(height);
96 return 2;
98 int getcsize(lua::state& L, lua::parameters& P)
100 L.pushnumber(cwidth);
101 L.pushnumber(cheight);
102 return 2;
104 size_t calcshift(size_t orig, int32_t shift, size_t dimension, size_t offset, bool circular)
106 if(circular) {
107 orig -= offset;
108 //Now the widow is scaled [0,dimension).
109 if(shift >= 0)
110 orig = (orig + shift) % dimension;
111 else {
112 orig += shift;
113 while(orig > dimension) {
114 //It overflowed.
115 orig += dimension;
118 orig += offset;
119 return orig;
120 } else
121 return orig + shift;
123 int scroll(lua::state& L, lua::parameters& P)
125 int32_t ox, oy;
126 size_t x0, y0, w, h;
127 bool circx, circy;
129 P(P.skipped(), ox, oy, P.optional(x0, 0), P.optional(y0, 0), P.optional(w, width),
130 P.optional(h, height), P.optional(circx, false), P.optional(circy, false));
132 threads::alock mh(lock);
133 if(x0 > width || x0 + w > width || x0 + w < x0 || y0 > height || y0 + h > height ||
134 y0 + h < y0)
135 throw std::runtime_error("Scroll window out of range");
136 if(!ox && !oy) return 0;
137 tilemap_entry* tmp = tmpmap;
138 for(size_t _y = 0; _y < h; _y++) {
139 size_t y = _y + y0;
140 size_t sy = calcshift(y, oy, h, y0, circy);
141 if(sy < y0 || sy >= y0 + h)
142 continue;
143 for(size_t _x = 0; _x < w; _x++) {
144 size_t x = _x + x0;
145 size_t sx = calcshift(x, ox, w, x0, circx);
146 if(sx < x0 || sx >= x0 + w)
147 continue;
148 else
149 tmp[_y * w + _x] = map[sy * width + sx];
152 for(size_t _y = 0; _y < h; _y++)
153 for(size_t _x = 0; _x < w; _x++)
154 map[(_y + y0) * width + (_x + x0)] = tmp[_y * w + _x];
155 return 0;
157 std::string print()
159 return (stringfmt() << width << "*" << height << " (cell " << cwidth << "*" << cheight
160 << ")").str();
162 size_t width;
163 size_t height;
164 size_t cwidth;
165 size_t cheight;
166 tilemap_entry* map;
167 tilemap_entry* tmpmap;
168 threads::lock lock;
171 struct render_object_tilemap : public framebuffer::object
173 render_object_tilemap(int32_t _x, int32_t _y, int32_t _x0, int32_t _y0, uint32_t _w,
174 uint32_t _h, bool _outside, lua::objpin<tilemap>& _map)
175 : x(_x), y(_y), x0(_x0), y0(_y0), w(_w), h(_h), outside(_outside), map(_map) {}
176 ~render_object_tilemap() throw()
179 bool kill_request(void* obj) throw()
181 return kill_request_ifeq(map.object(), obj);
183 template<bool T> void composite_op(struct framebuffer::fb<T>& scr) throw()
185 tilemap& _map = *map;
186 threads::alock h(_map.lock);
187 for(size_t ty = 0; ty < _map.height; ty++) {
188 size_t basey = _map.cheight * ty;
189 for(size_t tx = 0; tx < _map.width; tx++) {
190 size_t basex = _map.cwidth * tx;
191 composite_op(scr, _map.map[ty * _map.width + tx], basex, basey);
195 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, int32_t xp,
196 int32_t yp, const range& X, const range& Y, const range& sX, const range& sY,
197 lua_dbitmap& d) throw()
199 if(!X.size() || !Y.size()) return;
201 for(uint32_t r = Y.low(); r != Y.high(); r++) {
202 typename framebuffer::fb<T>::element_t* rptr = scr.rowptr(yp + r);
203 size_t eptr = xp + X.low();
204 uint32_t xmin = X.low();
205 bool cut = outside && sY.in(r);
206 if(cut && sX.in(xmin)) {
207 xmin = sX.high();
208 //FIXME: This may overrun buffer (but the overrun pointer is not accessed.)
209 eptr += (sX.high() - X.low());
211 for(uint32_t c = xmin; c < X.high(); c++, eptr++) {
212 if(__builtin_expect(cut && c == sX.low(), 0)) {
213 c += sX.size();
214 eptr += sX.size();
216 d.pixels[r * d.width + c].apply(rptr[eptr]);
220 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, int32_t xp,
221 int32_t yp, const range& X, const range& Y, const range& sX, const range& sY, lua_bitmap& b,
222 lua_palette& p) throw()
224 if(!X.size() || !Y.size()) return;
226 p.palette_mutex.lock();
227 framebuffer::color* palette = p.colors;
228 size_t pallim = p.color_count;
230 for(uint32_t r = Y.low(); r != Y.high(); r++) {
231 typename framebuffer::fb<T>::element_t* rptr = scr.rowptr(yp + r);
232 size_t eptr = xp + X.low();
233 uint32_t xmin = X.low();
234 bool cut = outside && sY.in(r);
235 if(cut && sX.in(xmin)) {
236 xmin = sX.high();
237 //FIXME: This may overrun buffer (but the overrun pointer is not accessed.)
238 eptr += (sX.high() - X.low());
240 for(uint32_t c = xmin; c < X.high(); c++, eptr++) {
241 if(__builtin_expect(cut && c == sX.low(), 0)) {
242 c += sX.size();
243 eptr += sX.size();
245 uint16_t i = b.pixels[r * b.width + c];
246 if(i < pallim)
247 palette[i].apply(rptr[eptr]);
250 p.palette_mutex.unlock();
252 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, tilemap_entry& e, int32_t bx,
253 int32_t by) throw()
255 size_t _w, _h;
256 if(e.b) {
257 _w = e.b->width;
258 _h = e.b->height;
259 } else if(e.d) {
260 _w = e.d->width;
261 _h = e.d->height;
262 } else
263 return;
265 uint32_t oX = x + scr.get_origin_x() - x0;
266 uint32_t oY = y + scr.get_origin_y() - y0;
267 range bX = ((range::make_w(scr.get_width()) - oX) & range::make_s(bx, _w) &
268 range::make_s(x0, w)) - bx;
269 range bY = ((range::make_w(scr.get_height()) - oY) & range::make_s(by, _h) &
270 range::make_s(y0, h)) - by;
271 range sX = range::make_s(-x - bx + x0, scr.get_last_blit_width());
272 range sY = range::make_s(-y - by + y0, scr.get_last_blit_height());
274 if(e.b)
275 composite_op(scr, oX + bx, oY + by, bX, bY, sX, sY, *e.b, *e.p);
276 else if(e.d)
277 composite_op(scr, oX + bx, oY + by, bX, bY, sX, sY, *e.d);
279 void operator()(struct framebuffer::fb<false>& x) throw() { composite_op(x); }
280 void operator()(struct framebuffer::fb<true>& x) throw() { composite_op(x); }
281 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
282 private:
283 int32_t x;
284 int32_t y;
285 int32_t x0;
286 int32_t y0;
287 uint32_t w;
288 uint32_t h;
289 bool outside;
290 lua::objpin<tilemap> map;
293 template<bool outside> int tilemap::draw(lua::state& L, lua::parameters& P)
295 auto& core = CORE();
296 uint32_t x, y, w, h;
297 int32_t x0, y0;
298 lua::objpin<tilemap> t;
300 if(!core.lua2->render_ctx) return 0;
302 P(t, x, y, P.optional(x0, 0), P.optional(y0, 0), P.optional(w, width * cwidth),
303 P.optional(h, height * cheight));
305 core.lua2->render_ctx->queue->create_add<render_object_tilemap>(x, y, x0, y0, w, h, outside, t);
306 return 0;
309 int tilemap::create(lua::state& L, lua::parameters& P)
311 uint32_t w, h, px, py;
313 P(w, h, px, py);
315 lua::_class<tilemap>::create(L, w, h, px, py);
316 return 1;
319 lua::_class<tilemap> LUA_class_tilemap(lua_class_gui, "TILEMAP", {
320 {"new", tilemap::create},
321 }, {
322 {"draw", &tilemap::draw<false>},
323 {"draw_outside", &tilemap::draw<true>},
324 {"set", &tilemap::set},
325 {"get", &tilemap::get},
326 {"scroll", &tilemap::scroll},
327 {"getsize", &tilemap::getsize},
328 {"getcsize", &tilemap::getcsize},
329 }, &tilemap::print);
331 tilemap::tilemap(lua::state& L, size_t _width, size_t _height, size_t _cwidth, size_t _cheight)
332 : width(_width), height(_height), cwidth(_cwidth), cheight(_cheight)
334 if(overcommit(width, height, cwidth, cheight) / height / sizeof(tilemap_entry) < width)
335 throw std::bad_alloc();
337 map = lua::align_overcommit<tilemap, tilemap_entry>(this);
338 tmpmap = &map[width * height];
339 //Initialize the map!
340 for(size_t i = 0; i < 2 * width * height; i++)
341 new(map + i) tilemap_entry();