Get rid of DECLARE_LUACLASS
[lsnes.git] / src / lua / gui-bitmap.cpp
blob8e0128a8fd6d2bb533c4a49a75be2914ed77ec96
1 #include "lua/internal.hpp"
2 #include "core/framebuffer.hpp"
3 #include "library/framebuffer.hpp"
4 #include "library/png-codec.hpp"
5 #include "library/sha256.hpp"
6 #include "library/serialization.hpp"
7 #include "library/string.hpp"
8 #include "library/zip.hpp"
9 #include "lua/bitmap.hpp"
10 #include "library/threadtypes.hpp"
11 #include <vector>
12 #include <sstream>
14 lua_bitmap::lua_bitmap(lua_state& L, uint32_t w, uint32_t h)
16 width = w;
17 height = h;
18 pixels.resize(width * height);
19 memset(&pixels[0], 0, width * height);
22 lua_bitmap::~lua_bitmap()
24 render_kill_request(this);
27 std::string lua_bitmap::print()
29 return (stringfmt() << width << "*" << height).str();
32 lua_dbitmap::lua_dbitmap(lua_state& L, uint32_t w, uint32_t h)
34 width = w;
35 height = h;
36 pixels.resize(width * height);
39 lua_dbitmap::~lua_dbitmap()
41 render_kill_request(this);
44 std::string lua_dbitmap::print()
46 return (stringfmt() << width << "*" << height).str();
49 lua_palette::lua_palette(lua_state& L)
53 lua_palette::~lua_palette()
57 std::string lua_palette::print()
59 size_t s = colors.size();
60 return (stringfmt() << s << " " << ((s != 1) ? "colors" : "color")).str();
63 std::vector<char> lua_dbitmap::save_png() const
65 png_encodedable_image img;
66 img.width = width;
67 img.height = height;
68 img.has_palette = false;
69 img.has_alpha = false;
70 img.data.resize(width * height);
71 for(size_t i = 0; i < width * height; i++) {
72 const framebuffer::color& c = pixels[i];
73 if(c.origa != 256)
74 img.has_alpha = true;
75 img.data[i] = c.orig + ((uint32_t)(c.origa - (c.origa >> 7) + (c.origa >> 8)) << 24);
77 std::ostringstream tmp1;
78 img.encode(tmp1);
79 std::string tmp2 = tmp1.str();
80 return std::vector<char>(tmp2.begin(), tmp2.end());
83 std::vector<char> lua_bitmap::save_png(const lua_palette& pal) const
85 png_encodedable_image img;
86 img.width = width;
87 img.height = height;
88 img.has_palette = true;
89 img.has_alpha = false;
90 img.data.resize(width * height);
91 img.palette.resize(pal.colors.size());
92 for(size_t i = 0; i < width * height; i++) {
93 img.data[i] = pixels[i];
95 for(size_t i = 0; i < pal.colors.size(); i++) {
96 const framebuffer::color& c = pal.colors[i];
97 if(c.origa != 256)
98 img.has_alpha = true;
99 img.palette[i] = c.orig + ((uint32_t)(c.origa - (c.origa >> 7) + (c.origa >> 8)) << 24);
101 std::ostringstream tmp1;
102 img.encode(tmp1);
103 std::string tmp2 = tmp1.str();
104 return std::vector<char>(tmp2.begin(), tmp2.end());
107 namespace
109 const char* base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
111 struct render_object_bitmap : public framebuffer::object
113 render_object_bitmap(int32_t _x, int32_t _y, lua_obj_pin<lua_bitmap> _bitmap,
114 lua_obj_pin<lua_palette> _palette) throw()
116 x = _x;
117 y = _y;
118 b = _bitmap;
119 p = _palette;
122 render_object_bitmap(int32_t _x, int32_t _y, lua_obj_pin<lua_dbitmap> _bitmap) throw()
124 x = _x;
125 y = _y;
126 b2 = _bitmap;
129 ~render_object_bitmap() throw()
133 bool kill_request(void* obj) throw()
135 return kill_request_ifeq(p.object(), obj) ||
136 kill_request_ifeq(b.object(), obj) ||
137 kill_request_ifeq(b2.object(), obj);
140 template<bool T> void composite_op(struct framebuffer::fb<T>& scr) throw()
142 if(p)
143 p->palette_mutex.lock();
144 uint32_t originx = scr.get_origin_x();
145 uint32_t originy = scr.get_origin_y();
146 size_t pallim = 0;
147 size_t w, h;
148 framebuffer::color* palette;
149 if(b) {
150 palette = &p->colors[0];
151 for(auto& c : p->colors)
152 c.set_palette(scr);
153 pallim = p->colors.size();
154 w = b->width;
155 h = b->height;
156 } else {
157 for(auto& c : b2->pixels)
158 c.set_palette(scr);
159 w = b2->width;
160 h = b2->height;
163 int32_t xmin = 0;
164 int32_t xmax = w;
165 int32_t ymin = 0;
166 int32_t ymax = h;
167 framebuffer::clip_range(originx, scr.get_width(), x, xmin, xmax);
168 framebuffer::clip_range(originy, scr.get_height(), y, ymin, ymax);
169 for(int32_t r = ymin; r < ymax; r++) {
170 typename framebuffer::fb<T>::element_t* rptr = scr.rowptr(y + r + originy);
171 size_t eptr = x + xmin + originx;
172 if(b)
173 for(int32_t c = xmin; c < xmax; c++, eptr++) {
174 uint16_t i = b->pixels[r * b->width + c];
175 if(i < pallim)
176 palette[i].apply(rptr[eptr]);
178 else
179 for(int32_t c = xmin; c < xmax; c++, eptr++)
180 b2->pixels[r * b2->width + c].apply(rptr[eptr]);
182 if(p)
183 p->palette_mutex.unlock();
185 void operator()(struct framebuffer::fb<false>& x) throw() { composite_op(x); }
186 void operator()(struct framebuffer::fb<true>& x) throw() { composite_op(x); }
187 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
188 private:
189 int32_t x;
190 int32_t y;
191 lua_obj_pin<lua_bitmap> b;
192 lua_obj_pin<lua_dbitmap> b2;
193 lua_obj_pin<lua_palette> p;
196 function_ptr_luafun gui_bitmap(lua_func_misc, "gui.bitmap_draw", [](lua_state& L, const std::string& fname)
197 -> int {
198 if(!lua_render_ctx)
199 return 0;
200 int32_t x = L.get_numeric_argument<int32_t>(1, fname.c_str());
201 int32_t y = L.get_numeric_argument<int32_t>(2, fname.c_str());
202 if(lua_class<lua_bitmap>::is(L, 3)) {
203 lua_class<lua_bitmap>::get(L, 3, fname.c_str());
204 lua_class<lua_palette>::get(L, 4, fname.c_str());
205 auto b = lua_class<lua_bitmap>::pin(L, 3, fname.c_str());
206 auto p = lua_class<lua_palette>::pin(L, 4, fname.c_str());
207 lua_render_ctx->queue->create_add<render_object_bitmap>(x, y, b, p);
208 } else if(lua_class<lua_dbitmap>::is(L, 3)) {
209 lua_class<lua_dbitmap>::get(L, 3, fname.c_str());
210 auto b = lua_class<lua_dbitmap>::pin(L, 3, fname.c_str());
211 lua_render_ctx->queue->create_add<render_object_bitmap>(x, y, b);
212 } else
213 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 3 for gui.bitmap_draw.");
214 return 0;
217 function_ptr_luafun gui_cpalette(lua_func_misc, "gui.palette_new", [](lua_state& L, const std::string& fname)
218 -> int {
219 lua_class<lua_palette>::create(L);
220 return 1;
223 function_ptr_luafun gui_cbitmap(lua_func_misc, "gui.bitmap_new", [](lua_state& L, const std::string& fname)
224 -> int {
225 uint32_t w = L.get_numeric_argument<uint32_t>(1, fname.c_str());
226 uint32_t h = L.get_numeric_argument<uint32_t>(2, fname.c_str());
227 bool d = L.get_bool(3, fname.c_str());
228 if(d) {
229 int64_t c = -1;
230 L.get_numeric_argument<int64_t>(4, c, fname.c_str());
231 lua_dbitmap* b = lua_class<lua_dbitmap>::create(L, w, h);
232 for(size_t i = 0; i < b->width * b->height; i++)
233 b->pixels[i] = framebuffer::color(c);
234 } else {
235 uint16_t c = 0;
236 L.get_numeric_argument<uint16_t>(4, c, fname.c_str());
237 lua_bitmap* b = lua_class<lua_bitmap>::create(L, w, h);
238 for(size_t i = 0; i < b->width * b->height; i++)
239 b->pixels[i] = c;
241 return 1;
244 function_ptr_luafun gui_epalette(lua_func_misc, "gui.palette_set", [](lua_state& L, const std::string& fname)
245 -> int {
246 lua_palette* p = lua_class<lua_palette>::get(L, 1, fname.c_str());
247 uint16_t c = L.get_numeric_argument<uint16_t>(2, fname.c_str());
248 int64_t nval = L.get_numeric_argument<int64_t>(3, fname.c_str());
249 framebuffer::color nc(nval);
250 //The mutex lock protects only the internals of colors array.
251 if(p->colors.size() <= c) {
252 p->palette_mutex.lock();
253 p->colors.resize(static_cast<uint32_t>(c) + 1);
254 p->palette_mutex.unlock();
256 p->colors[c] = nc;
257 return 0;
260 function_ptr_luafun pset_bitmap(lua_func_misc, "gui.bitmap_pset", [](lua_state& L, const std::string& fname)
261 -> int {
262 uint32_t x = L.get_numeric_argument<uint32_t>(2, fname.c_str());
263 uint32_t y = L.get_numeric_argument<uint32_t>(3, fname.c_str());
264 if(lua_class<lua_bitmap>::is(L, 1)) {
265 lua_bitmap* b = lua_class<lua_bitmap>::get(L, 1, fname.c_str());
266 uint16_t c = L.get_numeric_argument<uint16_t>(4, fname.c_str());
267 if(x >= b->width || y >= b->height)
268 return 0;
269 b->pixels[y * b->width + x] = c;
270 } else if(lua_class<lua_dbitmap>::is(L, 1)) {
271 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
272 int64_t c = L.get_numeric_argument<int64_t>(4, fname.c_str());
273 if(x >= b->width || y >= b->height)
274 return 0;
275 b->pixels[y * b->width + x] = framebuffer::color(c);
276 } else
277 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_pset.");
278 return 0;
281 inline int64_t demultiply_color(const framebuffer::color& c)
283 if(!c.origa)
284 return -1;
285 else
286 return c.orig | ((uint32_t)(256 - c.origa) << 24);
289 function_ptr_luafun pget_bitmap(lua_func_misc, "gui.bitmap_pget", [](lua_state& L, const std::string& fname)
290 -> int {
291 uint32_t x = L.get_numeric_argument<uint32_t>(2, fname.c_str());
292 uint32_t y = L.get_numeric_argument<uint32_t>(3, fname.c_str());
293 if(lua_class<lua_bitmap>::is(L, 1)) {
294 lua_bitmap* b = lua_class<lua_bitmap>::get(L, 1, fname.c_str());
295 if(x >= b->width || y >= b->height)
296 return 0;
297 L.pushnumber(b->pixels[y * b->width + x]);
298 } else if(lua_class<lua_dbitmap>::is(L, 1)) {
299 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
300 if(x >= b->width || y >= b->height)
301 return 0;
302 L.pushnumber(demultiply_color(b->pixels[y * b->width + x]));
303 } else
304 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_pget.");
305 return 1;
308 function_ptr_luafun size_bitmap(lua_func_misc, "gui.bitmap_size", [](lua_state& L, const std::string& fname)
309 -> int {
310 if(lua_class<lua_bitmap>::is(L, 1)) {
311 lua_bitmap* b = lua_class<lua_bitmap>::get(L, 1, fname.c_str());
312 L.pushnumber(b->width);
313 L.pushnumber(b->height);
314 } else if(lua_class<lua_dbitmap>::is(L, 1)) {
315 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
316 L.pushnumber(b->width);
317 L.pushnumber(b->height);
318 } else
319 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_size.");
320 return 2;
323 function_ptr_luafun hash_bitmap(lua_func_misc, "gui.bitmap_hash", [](lua_state& L, const std::string& fname)
324 -> int {
325 sha256 h;
326 const int buffersize = 256;
327 int bufferuse = 0;
328 char buf[buffersize];
329 memset(buf, 0, buffersize);
330 if(lua_class<lua_bitmap>::is(L, 1)) {
331 lua_bitmap* b = lua_class<lua_bitmap>::get(L, 1, fname.c_str());
332 serialization::u64b(buf + 0, b->width);
333 serialization::u64b(buf + 8, b->height);
334 bufferuse = 16;
335 for(unsigned i = 0; i < b->width * b->height; i++) {
336 if(bufferuse + 2 > buffersize) {
337 h.write(buf, bufferuse);
338 bufferuse = 0;
340 serialization::u16b(buf + bufferuse + 0, b->pixels[i]);
341 bufferuse += 2;
343 if(bufferuse > 0) h.write(buf, bufferuse);
344 L.pushlstring(h.read());
345 return 1;
346 } else if(lua_class<lua_dbitmap>::is(L, 1)) {
347 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
348 serialization::u64b(buf + 0, b->width);
349 serialization::u64b(buf + 4, b->height);
350 bufferuse = 16;
351 for(unsigned i = 0; i < b->width * b->height; i++) {
352 if(bufferuse + 6 > buffersize) {
353 h.write(buf, bufferuse);
354 bufferuse = 0;
356 serialization::u32b(buf + bufferuse + 0, b->pixels[i].orig);
357 serialization::u16b(buf + bufferuse + 4, b->pixels[i].origa);
358 bufferuse += 6;
360 if(bufferuse > 0) h.write(buf, bufferuse);
361 L.pushlstring(h.read());
362 return 1;
363 } else
364 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_hash.");
367 function_ptr_luafun hash_palette(lua_func_misc, "gui.palette_hash", [](lua_state& L, const std::string& fname)
368 -> int {
369 lua_palette* p = lua_class<lua_palette>::get(L, 1, fname.c_str());
370 sha256 h;
371 const int buffersize = 256;
372 int bufferuse = 0;
373 char buf[buffersize];
374 unsigned realsize = 0;
375 for(unsigned i = 0; i < p->colors.size(); i++)
376 if(p->colors[i].origa) realsize = i + 1;
377 for(unsigned i = 0; i < realsize; i++) {
378 if(bufferuse + 6 > buffersize) {
379 h.write(buf, bufferuse);
380 bufferuse = 0;
382 serialization::u32b(buf + bufferuse + 0, p->colors[i].orig);
383 serialization::u16b(buf + bufferuse + 4, p->colors[i].origa);
384 bufferuse += 6;
386 if(bufferuse > 0) h.write(buf, bufferuse);
387 L.pushlstring(h.read());
388 return 1;
391 struct colorkey_none
393 bool iskey(uint16_t& c) const { return false; }
394 bool iskey(framebuffer::color& c) const { return false; }
397 struct colorkey_direct
399 colorkey_direct(uint64_t _ck)
401 framebuffer::color c(_ck);
402 ck = c.orig;
403 cka = c.origa;
405 bool iskey(framebuffer::color& c) const { return (c.orig == ck && c.origa == cka); }
406 uint32_t ck;
407 uint16_t cka;
410 struct colorkey_palette
412 colorkey_palette(uint64_t _ck) { ck = _ck; }
413 bool iskey(uint16_t& c) const { return (c == ck); }
414 uint16_t ck;
417 template<class colorkey> struct srcdest_direct
419 srcdest_direct(lua_dbitmap& dest, lua_dbitmap& src, const colorkey& _ckey)
420 : ckey(_ckey)
422 darray = &dest.pixels[0];
423 sarray = &src.pixels[0];
424 swidth = src.width;
425 sheight = src.height;
426 dwidth = dest.width;
427 dheight = dest.height;
429 void copy(size_t didx, size_t sidx)
431 framebuffer::color c = sarray[sidx];
432 if(!ckey.iskey(c))
433 darray[didx] = c;
435 size_t swidth, sheight, dwidth, dheight;
436 private:
437 framebuffer::color* sarray;
438 framebuffer::color* darray;
439 const colorkey& ckey;
442 template<class colorkey> struct srcdest_palette
444 srcdest_palette(lua_bitmap& dest, lua_bitmap& src, const colorkey& _ckey)
445 : ckey(_ckey)
447 darray = &dest.pixels[0];
448 sarray = &src.pixels[0];
449 swidth = src.width;
450 sheight = src.height;
451 dwidth = dest.width;
452 dheight = dest.height;
454 void copy(size_t didx, size_t sidx)
456 uint16_t c = sarray[sidx];
457 if(!ckey.iskey(c))
458 darray[didx] = c;
460 size_t swidth, sheight, dwidth, dheight;
461 private:
462 uint16_t* sarray;
463 uint16_t* darray;
464 const colorkey& ckey;
467 template<class colorkey> struct srcdest_paletted
469 typedef framebuffer::color ptype;
470 srcdest_paletted(lua_dbitmap& dest, lua_bitmap& src, lua_palette& palette, const colorkey& _ckey)
471 : ckey(_ckey), transparent(-1)
473 darray = &dest.pixels[0];
474 sarray = &src.pixels[0];
475 limit = palette.colors.size();
476 pal = &palette.colors[0];
477 swidth = src.width;
478 sheight = src.height;
479 dwidth = dest.width;
480 dheight = dest.height;
482 void copy(size_t didx, size_t sidx)
484 uint16_t c = sarray[sidx];
485 if(!ckey.iskey(c))
486 darray[didx] = (c < limit) ? pal[c] : transparent;
488 size_t swidth, sheight, dwidth, dheight;
489 private:
490 uint16_t* sarray;
491 framebuffer::color* darray;
492 framebuffer::color* pal;
493 uint32_t limit;
494 framebuffer::color transparent;
495 const colorkey& ckey;
498 template<class srcdest>
499 void blit(srcdest sd, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h)
501 while((dx + w > sd.dwidth || sx + w > sd.swidth) && w > 0)
502 w--;
503 while((dy + h > sd.dheight || sy + h > sd.sheight) && h > 0)
504 h--;
505 size_t sidx = sy * sd.swidth + sx;
506 size_t didx = dy * sd.dwidth + dx;
507 size_t srskip = sd.swidth - w;
508 size_t drskip = sd.dwidth - w;
509 for(uint32_t j = 0; j < h; j++) {
510 for(uint32_t i = 0; i < w; i++) {
511 sd.copy(didx, sidx);
512 sidx++;
513 didx++;
515 sidx += srskip;
516 didx += drskip;
520 function_ptr_luafun blit_bitmap(lua_func_misc, "gui.bitmap_blit", [](lua_state& L, const std::string& fname)
521 -> int {
522 int slot = 1;
523 int dsts = 0;
524 int srcs = 0;
525 bool dst_d = lua_class<lua_dbitmap>::is(L, dsts = slot);
526 bool dst_p = lua_class<lua_bitmap>::is(L, slot++);
527 if(!dst_d && !dst_p)
528 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_blit");
529 uint32_t dx = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
530 uint32_t dy = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
531 bool src_d = lua_class<lua_dbitmap>::is(L, srcs = slot);
532 bool src_p = lua_class<lua_bitmap>::is(L, slot++);
533 if(!src_d && !src_p)
534 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 4 for gui.bitmap_blit");
535 if(dst_d && src_p)
536 slot++; //Reserve slot 5 for palette.
537 uint32_t sx = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
538 uint32_t sy = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
539 uint32_t w = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
540 uint32_t h = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
541 int64_t ck = 0x100000000ULL;
542 L.get_numeric_argument<int64_t>(slot++, ck, fname.c_str());
544 if(dst_d && src_d) {
545 lua_dbitmap* db = lua_class<lua_dbitmap>::get(L, dsts, fname.c_str());
546 lua_dbitmap* sb = lua_class<lua_dbitmap>::get(L, srcs, fname.c_str());
547 if(ck == 0x100000000ULL)
548 blit(srcdest_direct<colorkey_none>(*db, *sb, colorkey_none()), dx, dy, sx, sy, w, h);
549 else
550 blit(srcdest_direct<colorkey_direct>(*db, *sb, colorkey_direct(ck)), dx, dy, sx, sy, w,
552 } else if(dst_p && src_p) {
553 lua_bitmap* db = lua_class<lua_bitmap>::get(L, dsts, fname.c_str());
554 lua_bitmap* sb = lua_class<lua_bitmap>::get(L, srcs, fname.c_str());
555 if(ck > 65535)
556 blit(srcdest_palette<colorkey_none>(*db, *sb, colorkey_none()), dx, dy, sx, sy, w, h);
557 else
558 blit(srcdest_palette<colorkey_palette>(*db, *sb, colorkey_palette(ck)), dx, dy, sx, sy,
559 w, h);
560 } else if(dst_d && src_p) {
561 lua_dbitmap* db = lua_class<lua_dbitmap>::get(L, dsts, fname.c_str());
562 lua_bitmap* sb = lua_class<lua_bitmap>::get(L, srcs, fname.c_str());
563 lua_palette* pal = lua_class<lua_palette>::get(L, srcs + 1, fname.c_str());
564 if(ck > 65535)
565 blit(srcdest_paletted<colorkey_none>(*db, *sb, *pal, colorkey_none()), dx, dy, sx, sy,
566 w, h);
567 else
568 blit(srcdest_paletted<colorkey_palette>(*db, *sb, *pal, colorkey_palette(ck)), dx, dy,
569 sx, sy, w, h);
570 } else
571 throw std::runtime_error("If parameter 1 to gui.bitmap_blit is paletted, parameter 4 must be "
572 "too");
573 return 0;
576 int bitmap_load_fn(lua_state& L, std::function<lua_loaded_bitmap()> src)
578 uint32_t w, h;
579 auto bitmap = src();
580 if(bitmap.d) {
581 lua_dbitmap* b = lua_class<lua_dbitmap>::create(L, bitmap.w, bitmap.h);
582 for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
583 b->pixels[i] = framebuffer::color(bitmap.bitmap[i]);
584 return 1;
585 } else {
586 lua_bitmap* b = lua_class<lua_bitmap>::create(L, bitmap.w, bitmap.h);
587 lua_palette* p = lua_class<lua_palette>::create(L);
588 for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
589 b->pixels[i] = bitmap.bitmap[i];
590 p->colors.resize(bitmap.palette.size());
591 for(size_t i = 0; i < bitmap.palette.size(); i++)
592 p->colors[i] = framebuffer::color(bitmap.palette[i]);
593 return 2;
597 function_ptr_luafun gui_loadbitmap(lua_func_misc, "gui.bitmap_load", [](lua_state& L,
598 const std::string& fname) -> int {
599 std::string name2;
600 std::string name = L.get_string(1, fname.c_str());
601 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
602 name2 = L.get_string(2, fname.c_str());
603 return bitmap_load_fn(L, [&name, &name2]() -> lua_loaded_bitmap {
604 std::string name3 = zip::resolverel(name, name2);
605 return lua_loaded_bitmap::load(name3);
609 function_ptr_luafun gui_loadbitmap2(lua_func_misc, "gui.bitmap_load_str", [](lua_state& L,
610 const std::string& fname) -> int {
611 std::string contents = L.get_string(1, fname.c_str());
612 return bitmap_load_fn(L, [&contents]() -> lua_loaded_bitmap {
613 std::istringstream strm(contents);
614 return lua_loaded_bitmap::load(strm);
618 inline int64_t mangle_color(uint32_t c)
620 if(c < 0x1000000)
621 return -1;
622 else
623 return ((256 - (c >> 24) - (c >> 31)) << 24) | (c & 0xFFFFFF);
626 int base64val(char ch)
628 if(ch >= 'A' && ch <= 'Z')
629 return ch - 65;
630 if(ch >= 'a' && ch <= 'z')
631 return ch - 97 + 26;
632 if(ch >= '0' && ch <= '9')
633 return ch - 48 + 52;
634 if(ch == '+')
635 return 62;
636 if(ch == '/')
637 return 63;
638 if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
639 return -1;
640 if(ch == '=')
641 return -2;
642 return -3;
645 std::string base64_encode(const std::string& str)
647 std::ostringstream x;
648 unsigned pos = 0;
649 uint32_t mem = 0;
650 for(auto i : str) {
651 mem = (mem << 8) + (unsigned char)i;
652 if(++pos == 3) {
653 uint8_t c1 = (mem >> 18) & 0x3F;
654 uint8_t c2 = (mem >> 12) & 0x3F;
655 uint8_t c3 = (mem >> 6) & 0x3F;
656 uint8_t c4 = mem & 0x3F;
657 x << base64chars[c1];
658 x << base64chars[c2];
659 x << base64chars[c3];
660 x << base64chars[c4];
661 mem = 0;
662 pos = 0;
665 if(pos == 2) {
666 uint8_t c1 = (mem >> 10) & 0x3F;
667 uint8_t c2 = (mem >> 4) & 0x3F;
668 uint8_t c3 = (mem << 2) & 0x3F;
669 x << base64chars[c1];
670 x << base64chars[c2];
671 x << base64chars[c3];
672 x << "=";
674 if(pos == 1) {
675 uint8_t c1 = (mem >> 2) & 0x3F;
676 uint8_t c2 = (mem << 4) & 0x3F;
677 x << base64chars[c1];
678 x << base64chars[c2];
679 x << "==";
681 return x.str();
684 std::string base64_decode(const std::string& str)
686 bool end = 0;
687 uint32_t memory = 0;
688 uint32_t memsize = 1;
689 int posmod = 0;
690 std::ostringstream x;
691 for(auto i : str) {
692 int v = base64val(i);
693 if(v == -1)
694 continue;
695 posmod = (posmod + 1) & 3;
696 if(v == -2 && (posmod == 1 || posmod == 2))
697 throw std::runtime_error("Invalid Base64");
698 if(v == -2) {
699 end = true;
700 continue;
702 if(v == -3 || end)
703 throw std::runtime_error("Invalid Base64");
704 memory = memory * 64 + v;
705 memsize = memsize * 64;
706 if(memsize >= 256) {
707 memsize >>= 8;
708 x << static_cast<uint8_t>(memory / memsize);
709 memory %= memsize;
712 return x.str();
715 template<typename T>
716 int bitmap_load_png_fn(lua_state& L, T& src)
718 png_decoded_image img(src);
719 if(img.has_palette) {
720 lua_bitmap* b = lua_class<lua_bitmap>::create(L, img.width, img.height);
721 lua_palette* p = lua_class<lua_palette>::create(L);
722 for(size_t i = 0; i < img.width * img.height; i++)
723 b->pixels[i] = img.data[i];
724 p->colors.resize(img.palette.size());
725 for(size_t i = 0; i < img.palette.size(); i++)
726 p->colors[i] = framebuffer::color(mangle_color(img.palette[i]));
727 return 2;
728 } else {
729 lua_dbitmap* b = lua_class<lua_dbitmap>::create(L, img.width, img.height);
730 for(size_t i = 0; i < img.width * img.height; i++)
731 b->pixels[i] = framebuffer::color(mangle_color(img.data[i]));
732 return 1;
736 void bitmap_save_png_fn(lua_state& L, std::function<void(const std::vector<char>& buf)> fn, int index,
737 const std::string& fname)
739 std::vector<char> buf;
740 if(lua_class<lua_bitmap>::is(L, index)) {
741 lua_bitmap* b = lua_class<lua_bitmap>::get(L, index, fname.c_str());
742 lua_palette* p = lua_class<lua_palette>::get(L, index + 1, fname.c_str());
743 buf = b->save_png(*p);
744 } else if(lua_class<lua_dbitmap>::is(L, index)) {
745 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, index, fname.c_str());
746 buf = b->save_png();
747 } else
748 (stringfmt() << "Expected BITMAP or DBITMAP as argument " << index
749 << " for gui.bitmap_save_png.").throwex();
750 fn(buf);
754 function_ptr_luafun gui_loadbitmappng(lua_func_misc, "gui.bitmap_load_png", [](lua_state& L,
755 const std::string& fname) -> int {
756 std::string name2;
757 std::string name = L.get_string(1, fname.c_str());
758 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
759 name2 = L.get_string(2, fname.c_str());
760 std::string filename = zip::resolverel(name, name2);
761 return bitmap_load_png_fn(L, filename);
764 function_ptr_luafun gui_loadbitmappng2(lua_func_misc, "gui.bitmap_load_png_str", [](lua_state& L,
765 const std::string& fname) -> int {
766 std::string contents = base64_decode(L.get_string(1, fname.c_str()));
767 std::istringstream strm(contents);
768 return bitmap_load_png_fn(L, strm);
771 function_ptr_luafun gui_savebitmappng(lua_func_misc, "gui.bitmap_save_png", [](lua_state& L,
772 const std::string& fname) -> int {
773 int index = 1;
774 std::string name, name2;
775 if(L.type(index) == LUA_TSTRING) {
776 name = L.get_string(index, fname.c_str());
777 index++;
779 if(L.type(index) == LUA_TSTRING) {
780 name2 = L.get_string(index, fname.c_str());
781 index++;
783 if(index > 1) {
784 std::string filename = zip::resolverel(name, name2);
785 std::ofstream strm(filename, std::ios::binary);
786 if(!strm)
787 throw std::runtime_error("Can't open output file");
788 bitmap_save_png_fn(L, [&strm](const std::vector<char>& x) { strm.write(&x[0], x.size()); },
789 index, fname);
790 if(!strm)
791 throw std::runtime_error("Can't write output file");
792 return 0;
793 } else {
794 std::ostringstream strm;
795 bitmap_save_png_fn(L, [&strm](const std::vector<char>& x) { strm.write(&x[0], x.size()); }, 1,
796 fname);
797 L.pushlstring(base64_encode(strm.str()));
798 return 1;
802 int bitmap_palette_fn(lua_state& L, std::istream& s)
804 lua_palette* p = lua_class<lua_palette>::create(L);
805 while(s) {
806 std::string line;
807 std::getline(s, line);
808 istrip_CR(line);
809 regex_results r;
810 if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
811 int64_t cr, cg, cb, ca;
812 cr = parse_value<uint8_t>(r[1]);
813 cg = parse_value<uint8_t>(r[2]);
814 cb = parse_value<uint8_t>(r[3]);
815 ca = 256 - parse_value<uint16_t>(r[4]);
816 int64_t clr;
817 if(ca == 256)
818 p->colors.push_back(framebuffer::color(-1));
819 else
820 p->colors.push_back(framebuffer::color((ca << 24) | (cr << 16)
821 | (cg << 8) | cb));
822 } else if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
823 int64_t cr, cg, cb;
824 cr = parse_value<uint8_t>(r[1]);
825 cg = parse_value<uint8_t>(r[2]);
826 cb = parse_value<uint8_t>(r[3]);
827 p->colors.push_back(framebuffer::color((cr << 16) | (cg << 8) | cb));
828 } else if(regex_match("[ \t]*transparent[ \t]*", line)) {
829 p->colors.push_back(framebuffer::color(-1));
830 } else if(!regex_match("[ \t]*(#.*)?", line))
831 throw std::runtime_error("Invalid line format (" + line + ")");
833 return 1;
836 function_ptr_luafun gui_loadpalette(lua_func_misc, "gui.bitmap_load_pal", [](lua_state& L,
837 const std::string& fname) -> int {
838 std::string name2;
839 std::string name = L.get_string(1, fname.c_str());
840 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
841 name2 = L.get_string(2, fname.c_str());
842 std::istream& s = zip::openrel(name, name2);
843 try {
844 int r = bitmap_palette_fn(L, s);
845 delete &s;
846 return r;
847 } catch(...) {
848 delete &s;
849 throw;
853 function_ptr_luafun gui_loadpalette2(lua_func_misc, "gui.bitmap_load_pal_str", [](lua_state& L,
854 const std::string& fname) -> int {
855 std::string content = L.get_string(1, fname.c_str());
856 std::istringstream s(content);
857 return bitmap_palette_fn(L, s);
860 function_ptr_luafun gui_dpalette(lua_func_misc, "gui.palette_debug", [](lua_state& L,
861 const std::string& fname) -> int {
862 lua_palette* p = lua_class<lua_palette>::get(L, 1, fname.c_str());
863 size_t i = 0;
864 for(auto c : p->colors)
865 messages << "Color #" << (i++) << ": " << c.orig << ":" << c.origa << std::endl;
866 return 0;
869 inline framebuffer::color tadjust(framebuffer::color c, uint16_t adj)
871 uint32_t rgb = c.orig;
872 uint32_t a = c.origa;
873 a = (a * adj) >> 8;
874 if(a > 256)
875 a = 256;
876 if(a == 0)
877 return framebuffer::color(-1);
878 else
879 return framebuffer::color(rgb | ((uint32_t)(256 - a) << 24));
882 function_ptr_luafun adjust_trans(lua_func_misc, "gui.adjust_transparency", [](lua_state& L,
883 const std::string& fname) -> int {
884 uint16_t tadj = L.get_numeric_argument<uint16_t>(2, fname.c_str());
885 if(lua_class<lua_dbitmap>::is(L, 1)) {
886 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
887 for(auto& c : b->pixels)
888 c = tadjust(c, tadj);
889 } else if(lua_class<lua_palette>::is(L, 1)) {
890 lua_palette* p = lua_class<lua_palette>::get(L, 1, fname.c_str());
891 for(auto& c : p->colors)
892 c = tadjust(c, tadj);
893 } else {
894 throw std::runtime_error("Expected BITMAP or PALETTE as argument 1 for "
895 "gui.adjust_transparency");
897 return 2;
900 lua_class<lua_palette> class_palette("PALETTE");
901 lua_class<lua_bitmap> class_bitmap("BITMAP");
902 lua_class<lua_dbitmap> class_dbitmap("DBITMAP");