Fix help for +tangent/-tangent to be less obscure
[lsnes.git] / src / lua / gui-tilemap.cpp
blob775ea844a6c8c1ba95778978739fc36cc80c38c5
1 #include "lua/internal.hpp"
2 #include "core/framebuffer.hpp"
3 #include "core/instance.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) {
35 return lua::overcommit_std_align + 2 * sizeof(tilemap_entry) * (size_t)_width * _height;
37 ~tilemap()
39 threads::alock h(lock);
40 CORE().fbuf->render_kill_request(this);
42 static int create(lua::state& L, lua::parameters& P);
43 template<bool outside> int draw(lua::state& L, lua::parameters& P);
44 int get(lua::state& L, lua::parameters& P)
46 uint32_t x, y;
48 P(P.skipped(), x, y);
50 threads::alock h(lock);
51 if(x >= width || y >= height)
52 return 0;
53 tilemap_entry& e = map[y * width + x];
54 if(e.b) {
55 e.b.luapush(L);
56 e.p.luapush(L);
57 return 2;
58 } else if(e.d) {
59 e.d.luapush(L);
60 return 1;
61 } else
62 return 0;
64 int set(lua::state& L, lua::parameters& P)
66 uint32_t x, y;
68 P(P.skipped(), x, y);
69 int oidx = P.skip();
71 threads::alock h(lock);
72 if(x >= width || y >= height)
73 return 0;
74 tilemap_entry& e = map[y * width + x];
75 if(P.is<lua_dbitmap>(oidx)) {
76 auto d = P.arg<lua::objpin<lua_dbitmap>>(oidx);
77 e.erase();
78 e.d = d;
79 } else if(P.is<lua_bitmap>(oidx)) {
80 auto b = P.arg<lua::objpin<lua_bitmap>>(oidx);
81 auto p = P.arg<lua::objpin<lua_palette>>(oidx + 1);
82 e.erase();
83 e.b = b;
84 e.p = p;
85 } else if(P.is_novalue(oidx)) {
86 e.erase();
87 } else
88 P.expected("BITMAP, DBITMAP or nil", oidx);
89 return 0;
91 int getsize(lua::state& L, lua::parameters& P)
93 L.pushnumber(width);
94 L.pushnumber(height);
95 return 2;
97 int getcsize(lua::state& L, lua::parameters& P)
99 L.pushnumber(cwidth);
100 L.pushnumber(cheight);
101 return 2;
103 size_t calcshift(size_t orig, int32_t shift, size_t dimension, size_t offset, bool circular)
105 if(circular) {
106 orig -= offset;
107 //Now the widow is scaled [0,dimension).
108 if(shift >= 0)
109 orig = (orig + shift) % dimension;
110 else {
111 orig += shift;
112 while(orig > dimension) {
113 //It overflowed.
114 orig += dimension;
117 orig += offset;
118 return orig;
119 } else
120 return orig + shift;
122 int scroll(lua::state& L, lua::parameters& P)
124 int32_t ox, oy;
125 size_t x0, y0, w, h;
126 bool circx, circy;
128 P(P.skipped(), ox, oy, P.optional(x0, 0), P.optional(y0, 0), P.optional(w, width),
129 P.optional(h, height), P.optional(circx, false), P.optional(circy, false));
131 threads::alock mh(lock);
132 if(x0 > width || x0 + w > width || x0 + w < x0 || y0 > height || y0 + h > height ||
133 y0 + h < y0)
134 throw std::runtime_error("Scroll window out of range");
135 if(!ox && !oy) return 0;
136 tilemap_entry* tmp = tmpmap;
137 for(size_t _y = 0; _y < h; _y++) {
138 size_t y = _y + y0;
139 size_t sy = calcshift(y, oy, h, y0, circy);
140 if(sy < y0 || sy >= y0 + h)
141 continue;
142 for(size_t _x = 0; _x < w; _x++) {
143 size_t x = _x + x0;
144 size_t sx = calcshift(x, ox, w, x0, circx);
145 if(sx < x0 || sx >= x0 + w)
146 continue;
147 else
148 tmp[_y * w + _x] = map[sy * width + sx];
151 for(size_t _y = 0; _y < h; _y++)
152 for(size_t _x = 0; _x < w; _x++)
153 map[(_y + y0) * width + (_x + x0)] = tmp[_y * w + _x];
154 return 0;
156 std::string print()
158 return (stringfmt() << width << "*" << height << " (cell " << cwidth << "*" << cheight
159 << ")").str();
161 size_t width;
162 size_t height;
163 size_t cwidth;
164 size_t cheight;
165 tilemap_entry* map;
166 tilemap_entry* tmpmap;
167 threads::lock lock;
170 struct render_object_tilemap : public framebuffer::object
172 render_object_tilemap(int32_t _x, int32_t _y, int32_t _x0, int32_t _y0, uint32_t _w,
173 uint32_t _h, bool _outside, lua::objpin<tilemap>& _map)
174 : x(_x), y(_y), x0(_x0), y0(_y0), w(_w), h(_h), outside(_outside), map(_map) {}
175 ~render_object_tilemap() throw()
178 bool kill_request(void* obj) throw()
180 return kill_request_ifeq(map.object(), obj);
182 template<bool T> void composite_op(struct framebuffer::fb<T>& scr) throw()
184 tilemap& _map = *map;
185 threads::alock h(_map.lock);
186 for(size_t ty = 0; ty < _map.height; ty++) {
187 size_t basey = _map.cheight * ty;
188 for(size_t tx = 0; tx < _map.width; tx++) {
189 size_t basex = _map.cwidth * tx;
190 composite_op(scr, _map.map[ty * _map.width + tx], basex, basey);
194 template<bool T> void composite_op(struct framebuffer::fb<T>& scr, tilemap_entry& e, int32_t bx,
195 int32_t by) throw()
197 size_t _w, _h;
198 if(e.b) {
199 _w = e.b->width;
200 _h = e.b->height;
201 } else if(e.d) {
202 _w = e.d->width;
203 _h = e.d->height;
204 } else
205 return;
207 uint32_t oX = x + scr.get_origin_x() - x0;
208 uint32_t oY = y + scr.get_origin_y() - y0;
209 range bX = ((range::make_w(scr.get_width()) - oX) & range::make_s(bx, _w) &
210 range::make_s(x0, w)) - bx;
211 range bY = ((range::make_w(scr.get_height()) - oY) & range::make_s(by, _h) &
212 range::make_s(y0, h)) - by;
213 range sX = range::make_s(-x - bx + x0, scr.get_last_blit_width());
214 range sY = range::make_s(-y - by + y0, scr.get_last_blit_height());
216 if(e.b)
217 lua_bitmap_composite(scr, oX + bx, oY + by, bX, bY, sX, sY, outside,
218 lua_bitmap_holder<T>(*e.b, *e.p));
219 else if(e.d)
220 lua_bitmap_composite(scr, oX + bx, oY + by, bX, bY, sX, sY, outside,
221 lua_dbitmap_holder<T>(*e.d));
223 void operator()(struct framebuffer::fb<false>& x) throw() { composite_op(x); }
224 void operator()(struct framebuffer::fb<true>& x) throw() { composite_op(x); }
225 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
226 private:
227 int32_t x;
228 int32_t y;
229 int32_t x0;
230 int32_t y0;
231 uint32_t w;
232 uint32_t h;
233 bool outside;
234 lua::objpin<tilemap> map;
237 template<bool outside> int tilemap::draw(lua::state& L, lua::parameters& P)
239 auto& core = CORE();
240 uint32_t x, y, w, h;
241 int32_t x0, y0;
242 lua::objpin<tilemap> t;
244 if(!core.lua2->render_ctx) return 0;
246 P(t, x, y, P.optional(x0, 0), P.optional(y0, 0), P.optional(w, width * cwidth),
247 P.optional(h, height * cheight));
249 core.lua2->render_ctx->queue->create_add<render_object_tilemap>(x, y, x0, y0, w, h, outside, t);
250 return 0;
253 int tilemap::create(lua::state& L, lua::parameters& P)
255 uint32_t w, h, px, py;
257 P(w, h, px, py);
259 lua::_class<tilemap>::create(L, w, h, px, py);
260 return 1;
263 lua::_class<tilemap> LUA_class_tilemap(lua_class_gui, "TILEMAP", {
264 {"new", tilemap::create},
265 }, {
266 {"draw", &tilemap::draw<false>},
267 {"draw_outside", &tilemap::draw<true>},
268 {"set", &tilemap::set},
269 {"get", &tilemap::get},
270 {"scroll", &tilemap::scroll},
271 {"getsize", &tilemap::getsize},
272 {"getcsize", &tilemap::getcsize},
273 }, &tilemap::print);
275 tilemap::tilemap(lua::state& L, size_t _width, size_t _height, size_t _cwidth, size_t _cheight)
276 : width(_width), height(_height), cwidth(_cwidth), cheight(_cheight)
278 if(height > 0 && overcommit(width, height, cwidth, cheight) / height / sizeof(tilemap_entry) < width)
279 throw std::bad_alloc();
281 map = lua::align_overcommit<tilemap, tilemap_entry>(this);
282 tmpmap = &map[width * height];
283 //Initialize the map!
284 for(size_t i = 0; i < 2 * width * height; i++)
285 new(map + i) tilemap_entry();