Save bitmaps as PNG
[lsnes.git] / src / lua / gui-bitmap.cpp
blobfa7802b0b9815da9ac0feb7e7e3fd3652389802e
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/zip.hpp"
7 #include "lua/bitmap.hpp"
8 #include "library/threadtypes.hpp"
9 #include <vector>
10 #include <sstream>
12 lua_bitmap::lua_bitmap(lua_state& L, uint32_t w, uint32_t h)
14 width = w;
15 height = h;
16 pixels.resize(width * height);
17 memset(&pixels[0], 0, width * height);
20 lua_bitmap::~lua_bitmap()
22 render_kill_request(this);
25 std::string lua_bitmap::print()
27 return (stringfmt() << width << "*" << height).str();
30 lua_dbitmap::lua_dbitmap(lua_state& L, uint32_t w, uint32_t h)
32 width = w;
33 height = h;
34 pixels.resize(width * height);
37 lua_dbitmap::~lua_dbitmap()
39 render_kill_request(this);
42 std::string lua_dbitmap::print()
44 return (stringfmt() << width << "*" << height).str();
47 lua_palette::lua_palette(lua_state& L)
51 lua_palette::~lua_palette()
55 std::string lua_palette::print()
57 size_t s = colors.size();
58 return (stringfmt() << s << " " << ((s != 1) ? "colors" : "color")).str();
61 std::vector<char> lua_dbitmap::save_png() const
63 png_encodedable_image img;
64 img.width = width;
65 img.height = height;
66 img.has_palette = false;
67 img.has_alpha = false;
68 img.data.resize(width * height);
69 for(size_t i = 0; i < width * height; i++) {
70 const premultiplied_color& c = pixels[i];
71 if(c.origa != 256)
72 img.has_alpha = true;
73 img.data[i] = c.orig + ((uint32_t)(c.origa - (c.origa >> 7) + (c.origa >> 8)) << 24);
75 std::ostringstream tmp1;
76 img.encode(tmp1);
77 std::string tmp2 = tmp1.str();
78 return std::vector<char>(tmp2.begin(), tmp2.end());
81 std::vector<char> lua_bitmap::save_png(const lua_palette& pal) const
83 png_encodedable_image img;
84 img.width = width;
85 img.height = height;
86 img.has_palette = true;
87 img.has_alpha = false;
88 img.data.resize(width * height);
89 img.palette.resize(pal.colors.size());
90 for(size_t i = 0; i < width * height; i++) {
91 img.data[i] = pixels[i];
93 for(size_t i = 0; i < pal.colors.size(); i++) {
94 const premultiplied_color& c = pal.colors[i];
95 if(c.origa != 256)
96 img.has_alpha = true;
97 img.palette[i] = c.orig + ((uint32_t)(c.origa - (c.origa >> 7) + (c.origa >> 8)) << 24);
99 std::ostringstream tmp1;
100 img.encode(tmp1);
101 std::string tmp2 = tmp1.str();
102 return std::vector<char>(tmp2.begin(), tmp2.end());
105 namespace
107 const char* base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
109 struct render_object_bitmap : public render_object
111 render_object_bitmap(int32_t _x, int32_t _y, lua_obj_pin<lua_bitmap> _bitmap,
112 lua_obj_pin<lua_palette> _palette) throw()
114 x = _x;
115 y = _y;
116 b = _bitmap;
117 p = _palette;
120 render_object_bitmap(int32_t _x, int32_t _y, lua_obj_pin<lua_dbitmap> _bitmap) throw()
122 x = _x;
123 y = _y;
124 b2 = _bitmap;
127 ~render_object_bitmap() throw()
131 bool kill_request(void* obj) throw()
133 return kill_request_ifeq(p.object(), obj) ||
134 kill_request_ifeq(b.object(), obj) ||
135 kill_request_ifeq(b2.object(), obj);
138 template<bool T> void composite_op(struct framebuffer<T>& scr) throw()
140 if(p)
141 p->palette_mutex.lock();
142 uint32_t originx = scr.get_origin_x();
143 uint32_t originy = scr.get_origin_y();
144 size_t pallim = 0;
145 size_t w, h;
146 premultiplied_color* palette;
147 if(b) {
148 palette = &p->colors[0];
149 for(auto& c : p->colors)
150 c.set_palette(scr);
151 pallim = p->colors.size();
152 w = b->width;
153 h = b->height;
154 } else {
155 for(auto& c : b2->pixels)
156 c.set_palette(scr);
157 w = b2->width;
158 h = b2->height;
161 int32_t xmin = 0;
162 int32_t xmax = w;
163 int32_t ymin = 0;
164 int32_t ymax = h;
165 clip_range(originx, scr.get_width(), x, xmin, xmax);
166 clip_range(originy, scr.get_height(), y, ymin, ymax);
167 for(int32_t r = ymin; r < ymax; r++) {
168 typename framebuffer<T>::element_t* rptr = scr.rowptr(y + r + originy);
169 size_t eptr = x + xmin + originx;
170 if(b)
171 for(int32_t c = xmin; c < xmax; c++, eptr++) {
172 uint16_t i = b->pixels[r * b->width + c];
173 if(i < pallim)
174 palette[i].apply(rptr[eptr]);
176 else
177 for(int32_t c = xmin; c < xmax; c++, eptr++)
178 b2->pixels[r * b2->width + c].apply(rptr[eptr]);
180 if(p)
181 p->palette_mutex.unlock();
183 void operator()(struct framebuffer<false>& x) throw() { composite_op(x); }
184 void operator()(struct framebuffer<true>& x) throw() { composite_op(x); }
185 void clone(render_queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
186 private:
187 int32_t x;
188 int32_t y;
189 lua_obj_pin<lua_bitmap> b;
190 lua_obj_pin<lua_dbitmap> b2;
191 lua_obj_pin<lua_palette> p;
194 function_ptr_luafun gui_bitmap(lua_func_misc, "gui.bitmap_draw", [](lua_state& L, const std::string& fname)
195 -> int {
196 if(!lua_render_ctx)
197 return 0;
198 int32_t x = L.get_numeric_argument<int32_t>(1, fname.c_str());
199 int32_t y = L.get_numeric_argument<int32_t>(2, fname.c_str());
200 if(lua_class<lua_bitmap>::is(L, 3)) {
201 lua_class<lua_bitmap>::get(L, 3, fname.c_str());
202 lua_class<lua_palette>::get(L, 4, fname.c_str());
203 auto b = lua_class<lua_bitmap>::pin(L, 3, fname.c_str());
204 auto p = lua_class<lua_palette>::pin(L, 4, fname.c_str());
205 lua_render_ctx->queue->create_add<render_object_bitmap>(x, y, b, p);
206 } else if(lua_class<lua_dbitmap>::is(L, 3)) {
207 lua_class<lua_dbitmap>::get(L, 3, fname.c_str());
208 auto b = lua_class<lua_dbitmap>::pin(L, 3, fname.c_str());
209 lua_render_ctx->queue->create_add<render_object_bitmap>(x, y, b);
210 } else
211 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 3 for gui.bitmap_draw.");
212 return 0;
215 function_ptr_luafun gui_cpalette(lua_func_misc, "gui.palette_new", [](lua_state& L, const std::string& fname)
216 -> int {
217 lua_class<lua_palette>::create(L);
218 return 1;
221 function_ptr_luafun gui_cbitmap(lua_func_misc, "gui.bitmap_new", [](lua_state& L, const std::string& fname)
222 -> int {
223 uint32_t w = L.get_numeric_argument<uint32_t>(1, fname.c_str());
224 uint32_t h = L.get_numeric_argument<uint32_t>(2, fname.c_str());
225 bool d = L.get_bool(3, fname.c_str());
226 if(d) {
227 int64_t c = -1;
228 L.get_numeric_argument<int64_t>(4, c, fname.c_str());
229 lua_dbitmap* b = lua_class<lua_dbitmap>::create(L, w, h);
230 for(size_t i = 0; i < b->width * b->height; i++)
231 b->pixels[i] = premultiplied_color(c);
232 } else {
233 uint16_t c = 0;
234 L.get_numeric_argument<uint16_t>(4, c, fname.c_str());
235 lua_bitmap* b = lua_class<lua_bitmap>::create(L, w, h);
236 for(size_t i = 0; i < b->width * b->height; i++)
237 b->pixels[i] = c;
239 return 1;
242 function_ptr_luafun gui_epalette(lua_func_misc, "gui.palette_set", [](lua_state& L, const std::string& fname)
243 -> int {
244 lua_palette* p = lua_class<lua_palette>::get(L, 1, fname.c_str());
245 uint16_t c = L.get_numeric_argument<uint16_t>(2, fname.c_str());
246 int64_t nval = L.get_numeric_argument<int64_t>(3, fname.c_str());
247 premultiplied_color nc(nval);
248 //The mutex lock protects only the internals of colors array.
249 if(p->colors.size() <= c) {
250 p->palette_mutex.lock();
251 p->colors.resize(static_cast<uint32_t>(c) + 1);
252 p->palette_mutex.unlock();
254 p->colors[c] = nc;
255 return 0;
258 function_ptr_luafun pset_bitmap(lua_func_misc, "gui.bitmap_pset", [](lua_state& L, const std::string& fname)
259 -> int {
260 uint32_t x = L.get_numeric_argument<uint32_t>(2, fname.c_str());
261 uint32_t y = L.get_numeric_argument<uint32_t>(3, fname.c_str());
262 if(lua_class<lua_bitmap>::is(L, 1)) {
263 lua_bitmap* b = lua_class<lua_bitmap>::get(L, 1, fname.c_str());
264 uint16_t c = L.get_numeric_argument<uint16_t>(4, fname.c_str());
265 if(x >= b->width || y >= b->height)
266 return 0;
267 b->pixels[y * b->width + x] = c;
268 } else if(lua_class<lua_dbitmap>::is(L, 1)) {
269 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
270 int64_t c = L.get_numeric_argument<int64_t>(4, fname.c_str());
271 if(x >= b->width || y >= b->height)
272 return 0;
273 b->pixels[y * b->width + x] = premultiplied_color(c);
274 } else
275 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_pset.");
276 return 0;
279 inline int64_t demultiply_color(const premultiplied_color& c)
281 if(!c.origa)
282 return -1;
283 else
284 return c.orig | ((uint32_t)(256 - c.origa) << 24);
287 function_ptr_luafun pget_bitmap(lua_func_misc, "gui.bitmap_pget", [](lua_state& L, const std::string& fname)
288 -> int {
289 uint32_t x = L.get_numeric_argument<uint32_t>(2, fname.c_str());
290 uint32_t y = L.get_numeric_argument<uint32_t>(3, fname.c_str());
291 if(lua_class<lua_bitmap>::is(L, 1)) {
292 lua_bitmap* b = lua_class<lua_bitmap>::get(L, 1, fname.c_str());
293 if(x >= b->width || y >= b->height)
294 return 0;
295 L.pushnumber(b->pixels[y * b->width + x]);
296 } else if(lua_class<lua_dbitmap>::is(L, 1)) {
297 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
298 if(x >= b->width || y >= b->height)
299 return 0;
300 L.pushnumber(demultiply_color(b->pixels[y * b->width + x]));
301 } else
302 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_pget.");
303 return 1;
306 function_ptr_luafun size_bitmap(lua_func_misc, "gui.bitmap_size", [](lua_state& L, const std::string& fname)
307 -> int {
308 if(lua_class<lua_bitmap>::is(L, 1)) {
309 lua_bitmap* b = lua_class<lua_bitmap>::get(L, 1, fname.c_str());
310 L.pushnumber(b->width);
311 L.pushnumber(b->height);
312 } else if(lua_class<lua_dbitmap>::is(L, 1)) {
313 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
314 L.pushnumber(b->width);
315 L.pushnumber(b->height);
316 } else
317 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_size.");
318 return 2;
321 struct colorkey_none
323 bool iskey(uint16_t& c) const { return false; }
324 bool iskey(premultiplied_color& c) const { return false; }
327 struct colorkey_direct
329 colorkey_direct(uint64_t _ck)
331 premultiplied_color c(_ck);
332 ck = c.orig;
333 cka = c.origa;
335 bool iskey(premultiplied_color& c) const { return (c.orig == ck && c.origa == cka); }
336 uint32_t ck;
337 uint16_t cka;
340 struct colorkey_palette
342 colorkey_palette(uint64_t _ck) { ck = _ck; }
343 bool iskey(uint16_t& c) const { return (c == ck); }
344 uint16_t ck;
347 template<class colorkey> struct srcdest_direct
349 srcdest_direct(lua_dbitmap& dest, lua_dbitmap& src, const colorkey& _ckey)
350 : ckey(_ckey)
352 darray = &dest.pixels[0];
353 sarray = &src.pixels[0];
354 swidth = src.width;
355 sheight = src.height;
356 dwidth = dest.width;
357 dheight = dest.height;
359 void copy(size_t didx, size_t sidx)
361 premultiplied_color c = sarray[sidx];
362 if(!ckey.iskey(c))
363 darray[didx] = c;
365 size_t swidth, sheight, dwidth, dheight;
366 private:
367 premultiplied_color* sarray;
368 premultiplied_color* darray;
369 const colorkey& ckey;
372 template<class colorkey> struct srcdest_palette
374 srcdest_palette(lua_bitmap& dest, lua_bitmap& src, const colorkey& _ckey)
375 : ckey(_ckey)
377 darray = &dest.pixels[0];
378 sarray = &src.pixels[0];
379 swidth = src.width;
380 sheight = src.height;
381 dwidth = dest.width;
382 dheight = dest.height;
384 void copy(size_t didx, size_t sidx)
386 uint16_t c = sarray[sidx];
387 if(!ckey.iskey(c))
388 darray[didx] = c;
390 size_t swidth, sheight, dwidth, dheight;
391 private:
392 uint16_t* sarray;
393 uint16_t* darray;
394 const colorkey& ckey;
397 template<class colorkey> struct srcdest_paletted
399 typedef premultiplied_color ptype;
400 srcdest_paletted(lua_dbitmap& dest, lua_bitmap& src, lua_palette& palette, const colorkey& _ckey)
401 : ckey(_ckey), transparent(-1)
403 darray = &dest.pixels[0];
404 sarray = &src.pixels[0];
405 limit = palette.colors.size();
406 pal = &palette.colors[0];
407 swidth = src.width;
408 sheight = src.height;
409 dwidth = dest.width;
410 dheight = dest.height;
412 void copy(size_t didx, size_t sidx)
414 uint16_t c = sarray[sidx];
415 if(!ckey.iskey(c))
416 darray[didx] = (c < limit) ? pal[c] : transparent;
418 size_t swidth, sheight, dwidth, dheight;
419 private:
420 uint16_t* sarray;
421 premultiplied_color* darray;
422 premultiplied_color* pal;
423 uint32_t limit;
424 premultiplied_color transparent;
425 const colorkey& ckey;
428 template<class srcdest>
429 void blit(srcdest sd, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h)
431 while((dx + w > sd.dwidth || sx + w > sd.swidth) && w > 0)
432 w--;
433 while((dy + h > sd.dheight || sy + h > sd.sheight) && h > 0)
434 h--;
435 size_t sidx = sy * sd.swidth + sx;
436 size_t didx = dy * sd.dwidth + dx;
437 size_t srskip = sd.swidth - w;
438 size_t drskip = sd.dwidth - w;
439 for(uint32_t j = 0; j < h; j++) {
440 for(uint32_t i = 0; i < w; i++) {
441 sd.copy(didx, sidx);
442 sidx++;
443 didx++;
445 sidx += srskip;
446 didx += drskip;
450 function_ptr_luafun blit_bitmap(lua_func_misc, "gui.bitmap_blit", [](lua_state& L, const std::string& fname)
451 -> int {
452 int slot = 1;
453 int dsts = 0;
454 int srcs = 0;
455 bool dst_d = lua_class<lua_dbitmap>::is(L, dsts = slot);
456 bool dst_p = lua_class<lua_bitmap>::is(L, slot++);
457 if(!dst_d && !dst_p)
458 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 1 for gui.bitmap_blit");
459 uint32_t dx = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
460 uint32_t dy = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
461 bool src_d = lua_class<lua_dbitmap>::is(L, srcs = slot);
462 bool src_p = lua_class<lua_bitmap>::is(L, slot++);
463 if(!src_d && !src_p)
464 throw std::runtime_error("Expected BITMAP or DBITMAP as argument 4 for gui.bitmap_blit");
465 if(dst_d && src_p)
466 slot++; //Reserve slot 5 for palette.
467 uint32_t sx = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
468 uint32_t sy = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
469 uint32_t w = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
470 uint32_t h = L.get_numeric_argument<uint32_t>(slot++, fname.c_str());
471 int64_t ck = 0x100000000ULL;
472 L.get_numeric_argument<int64_t>(slot++, ck, fname.c_str());
474 if(dst_d && src_d) {
475 lua_dbitmap* db = lua_class<lua_dbitmap>::get(L, dsts, fname.c_str());
476 lua_dbitmap* sb = lua_class<lua_dbitmap>::get(L, srcs, fname.c_str());
477 if(ck == 0x100000000ULL)
478 blit(srcdest_direct<colorkey_none>(*db, *sb, colorkey_none()), dx, dy, sx, sy, w, h);
479 else
480 blit(srcdest_direct<colorkey_direct>(*db, *sb, colorkey_direct(ck)), dx, dy, sx, sy, w,
482 } else if(dst_p && src_p) {
483 lua_bitmap* db = lua_class<lua_bitmap>::get(L, dsts, fname.c_str());
484 lua_bitmap* sb = lua_class<lua_bitmap>::get(L, srcs, fname.c_str());
485 if(ck > 65535)
486 blit(srcdest_palette<colorkey_none>(*db, *sb, colorkey_none()), dx, dy, sx, sy, w, h);
487 else
488 blit(srcdest_palette<colorkey_palette>(*db, *sb, colorkey_palette(ck)), dx, dy, sx, sy,
489 w, h);
490 } else if(dst_d && src_p) {
491 lua_dbitmap* db = lua_class<lua_dbitmap>::get(L, dsts, fname.c_str());
492 lua_bitmap* sb = lua_class<lua_bitmap>::get(L, srcs, fname.c_str());
493 lua_palette* pal = lua_class<lua_palette>::get(L, srcs + 1, fname.c_str());
494 if(ck > 65535)
495 blit(srcdest_paletted<colorkey_none>(*db, *sb, *pal, colorkey_none()), dx, dy, sx, sy,
496 w, h);
497 else
498 blit(srcdest_paletted<colorkey_palette>(*db, *sb, *pal, colorkey_palette(ck)), dx, dy,
499 sx, sy, w, h);
500 } else
501 throw std::runtime_error("If parameter 1 to gui.bitmap_blit is paletted, parameter 4 must be "
502 "too");
503 return 0;
506 int bitmap_load_fn(lua_state& L, std::function<lua_loaded_bitmap()> src)
508 uint32_t w, h;
509 auto bitmap = src();
510 if(bitmap.d) {
511 lua_dbitmap* b = lua_class<lua_dbitmap>::create(L, bitmap.w, bitmap.h);
512 for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
513 b->pixels[i] = premultiplied_color(bitmap.bitmap[i]);
514 return 1;
515 } else {
516 lua_bitmap* b = lua_class<lua_bitmap>::create(L, bitmap.w, bitmap.h);
517 lua_palette* p = lua_class<lua_palette>::create(L);
518 for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
519 b->pixels[i] = bitmap.bitmap[i];
520 p->colors.resize(bitmap.palette.size());
521 for(size_t i = 0; i < bitmap.palette.size(); i++)
522 p->colors[i] = premultiplied_color(bitmap.palette[i]);
523 return 2;
527 function_ptr_luafun gui_loadbitmap(lua_func_misc, "gui.bitmap_load", [](lua_state& L,
528 const std::string& fname) -> int {
529 std::string name2;
530 std::string name = L.get_string(1, fname.c_str());
531 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
532 name2 = L.get_string(2, fname.c_str());
533 return bitmap_load_fn(L, [&name, &name2]() -> lua_loaded_bitmap {
534 std::string name3 = resolve_file_relative(name, name2);
535 return lua_loaded_bitmap::load(name3);
539 function_ptr_luafun gui_loadbitmap2(lua_func_misc, "gui.bitmap_load_str", [](lua_state& L,
540 const std::string& fname) -> int {
541 std::string contents = L.get_string(1, fname.c_str());
542 return bitmap_load_fn(L, [&contents]() -> lua_loaded_bitmap {
543 std::istringstream strm(contents);
544 return lua_loaded_bitmap::load(strm);
548 inline int64_t mangle_color(uint32_t c)
550 if(c < 0x1000000)
551 return -1;
552 else
553 return ((256 - (c >> 24) - (c >> 31)) << 24) | (c & 0xFFFFFF);
556 int base64val(char ch)
558 if(ch >= 'A' && ch <= 'Z')
559 return ch - 65;
560 if(ch >= 'a' && ch <= 'z')
561 return ch - 97 + 26;
562 if(ch >= '0' && ch <= '9')
563 return ch - 48 + 52;
564 if(ch == '+')
565 return 62;
566 if(ch == '/')
567 return 63;
568 if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
569 return -1;
570 if(ch == '=')
571 return -2;
572 return -3;
575 std::string base64_encode(const std::string& str)
577 std::ostringstream x;
578 unsigned pos = 0;
579 uint32_t mem = 0;
580 for(auto i : str) {
581 mem = (mem << 8) + (unsigned char)i;
582 if(++pos == 3) {
583 uint8_t c1 = (mem >> 18) & 0x3F;
584 uint8_t c2 = (mem >> 12) & 0x3F;
585 uint8_t c3 = (mem >> 6) & 0x3F;
586 uint8_t c4 = mem & 0x3F;
587 x << base64chars[c1];
588 x << base64chars[c2];
589 x << base64chars[c3];
590 x << base64chars[c4];
591 mem = 0;
592 pos = 0;
595 if(pos == 2) {
596 uint8_t c1 = (mem >> 10) & 0x3F;
597 uint8_t c2 = (mem >> 4) & 0x3F;
598 uint8_t c3 = (mem << 2) & 0x3F;
599 x << base64chars[c1];
600 x << base64chars[c2];
601 x << base64chars[c3];
602 x << "=";
604 if(pos == 1) {
605 uint8_t c1 = (mem >> 2) & 0x3F;
606 uint8_t c2 = (mem << 4) & 0x3F;
607 x << base64chars[c1];
608 x << base64chars[c2];
609 x << "==";
611 return x.str();
614 std::string base64_decode(const std::string& str)
616 bool end = 0;
617 uint32_t memory = 0;
618 uint32_t memsize = 1;
619 int posmod = 0;
620 std::ostringstream x;
621 for(auto i : str) {
622 int v = base64val(i);
623 if(v == -1)
624 continue;
625 posmod = (posmod + 1) & 3;
626 if(v == -2 && (posmod == 1 || posmod == 2))
627 throw std::runtime_error("Invalid Base64");
628 if(v == -2) {
629 end = true;
630 continue;
632 if(v == -3 || end)
633 throw std::runtime_error("Invalid Base64");
634 memory = memory * 64 + v;
635 memsize = memsize * 64;
636 if(memsize >= 256) {
637 memsize >>= 8;
638 x << static_cast<uint8_t>(memory / memsize);
639 memory %= memsize;
642 return x.str();
645 template<typename T>
646 int bitmap_load_png_fn(lua_state& L, T& src)
648 png_decoded_image img(src);
649 if(img.has_palette) {
650 lua_bitmap* b = lua_class<lua_bitmap>::create(L, img.width, img.height);
651 lua_palette* p = lua_class<lua_palette>::create(L);
652 for(size_t i = 0; i < img.width * img.height; i++)
653 b->pixels[i] = img.data[i];
654 p->colors.resize(img.palette.size());
655 for(size_t i = 0; i < img.palette.size(); i++)
656 p->colors[i] = premultiplied_color(mangle_color(img.palette[i]));
657 return 2;
658 } else {
659 lua_dbitmap* b = lua_class<lua_dbitmap>::create(L, img.width, img.height);
660 for(size_t i = 0; i < img.width * img.height; i++)
661 b->pixels[i] = premultiplied_color(mangle_color(img.data[i]));
662 return 1;
666 void bitmap_save_png_fn(lua_state& L, std::function<void(const std::vector<char>& buf)> fn, int index,
667 const std::string& fname)
669 std::vector<char> buf;
670 if(lua_class<lua_bitmap>::is(L, index)) {
671 lua_bitmap* b = lua_class<lua_bitmap>::get(L, index, fname.c_str());
672 lua_palette* p = lua_class<lua_palette>::get(L, index + 1, fname.c_str());
673 buf = b->save_png(*p);
674 } else if(lua_class<lua_dbitmap>::is(L, index)) {
675 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, index, fname.c_str());
676 buf = b->save_png();
677 } else
678 (stringfmt() << "Expected BITMAP or DBITMAP as argument " << index
679 << " for gui.bitmap_save_png.").throwex();
680 fn(buf);
684 function_ptr_luafun gui_loadbitmappng(lua_func_misc, "gui.bitmap_load_png", [](lua_state& L,
685 const std::string& fname) -> int {
686 std::string name2;
687 std::string name = L.get_string(1, fname.c_str());
688 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
689 name2 = L.get_string(2, fname.c_str());
690 std::string filename = resolve_file_relative(name, name2);
691 return bitmap_load_png_fn(L, filename);
694 function_ptr_luafun gui_loadbitmappng2(lua_func_misc, "gui.bitmap_load_png_str", [](lua_state& L,
695 const std::string& fname) -> int {
696 std::string contents = base64_decode(L.get_string(1, fname.c_str()));
697 std::istringstream strm(contents);
698 return bitmap_load_png_fn(L, strm);
701 function_ptr_luafun gui_savebitmappng(lua_func_misc, "gui.bitmap_save_png", [](lua_state& L,
702 const std::string& fname) -> int {
703 int index = 1;
704 std::string name, name2;
705 if(L.type(index) == LUA_TSTRING) {
706 name = L.get_string(index, fname.c_str());
707 index++;
709 if(L.type(index) == LUA_TSTRING) {
710 name2 = L.get_string(index, fname.c_str());
711 index++;
713 if(index > 1) {
714 std::string filename = resolve_file_relative(name, name2);
715 std::ofstream strm(filename, std::ios::binary);
716 if(!strm)
717 throw std::runtime_error("Can't open output file");
718 bitmap_save_png_fn(L, [&strm](const std::vector<char>& x) { strm.write(&x[0], x.size()); },
719 index, fname);
720 if(!strm)
721 throw std::runtime_error("Can't write output file");
722 return 0;
723 } else {
724 std::ostringstream strm;
725 bitmap_save_png_fn(L, [&strm](const std::vector<char>& x) { strm.write(&x[0], x.size()); }, 1,
726 fname);
727 L.pushlstring(base64_encode(strm.str()));
728 return 1;
732 int bitmap_palette_fn(lua_state& L, std::istream& s)
734 lua_palette* p = lua_class<lua_palette>::create(L);
735 while(s) {
736 std::string line;
737 std::getline(s, line);
738 istrip_CR(line);
739 regex_results r;
740 if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
741 int64_t cr, cg, cb, ca;
742 cr = parse_value<uint8_t>(r[1]);
743 cg = parse_value<uint8_t>(r[2]);
744 cb = parse_value<uint8_t>(r[3]);
745 ca = 256 - parse_value<uint16_t>(r[4]);
746 int64_t clr;
747 if(ca == 256)
748 p->colors.push_back(premultiplied_color(-1));
749 else
750 p->colors.push_back(premultiplied_color((ca << 24) | (cr << 16)
751 | (cg << 8) | cb));
752 } else if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
753 int64_t cr, cg, cb;
754 cr = parse_value<uint8_t>(r[1]);
755 cg = parse_value<uint8_t>(r[2]);
756 cb = parse_value<uint8_t>(r[3]);
757 p->colors.push_back(premultiplied_color((cr << 16) | (cg << 8) | cb));
758 } else if(regex_match("[ \t]*transparent[ \t]*", line)) {
759 p->colors.push_back(premultiplied_color(-1));
760 } else if(!regex_match("[ \t]*(#.*)?", line))
761 throw std::runtime_error("Invalid line format (" + line + ")");
763 return 1;
766 function_ptr_luafun gui_loadpalette(lua_func_misc, "gui.bitmap_load_pal", [](lua_state& L,
767 const std::string& fname) -> int {
768 std::string name2;
769 std::string name = L.get_string(1, fname.c_str());
770 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
771 name2 = L.get_string(2, fname.c_str());
772 std::istream& s = open_file_relative(name, name2);
773 try {
774 int r = bitmap_palette_fn(L, s);
775 delete &s;
776 return r;
777 } catch(...) {
778 delete &s;
779 throw;
783 function_ptr_luafun gui_loadpalette2(lua_func_misc, "gui.bitmap_load_pal_str", [](lua_state& L,
784 const std::string& fname) -> int {
785 std::string content = L.get_string(1, fname.c_str());
786 std::istringstream s(content);
787 return bitmap_palette_fn(L, s);
790 function_ptr_luafun gui_dpalette(lua_func_misc, "gui.palette_debug", [](lua_state& L,
791 const std::string& fname) -> int {
792 lua_palette* p = lua_class<lua_palette>::get(L, 1, fname.c_str());
793 size_t i = 0;
794 for(auto c : p->colors)
795 messages << "Color #" << (i++) << ": " << c.orig << ":" << c.origa << std::endl;
796 return 0;
799 inline premultiplied_color tadjust(premultiplied_color c, uint16_t adj)
801 uint32_t rgb = c.orig;
802 uint32_t a = c.origa;
803 a = (a * adj) >> 8;
804 if(a > 256)
805 a = 256;
806 if(a == 0)
807 return premultiplied_color(-1);
808 else
809 return premultiplied_color(rgb | ((uint32_t)(256 - a) << 24));
812 function_ptr_luafun adjust_trans(lua_func_misc, "gui.adjust_transparency", [](lua_state& L,
813 const std::string& fname) -> int {
814 uint16_t tadj = L.get_numeric_argument<uint16_t>(2, fname.c_str());
815 if(lua_class<lua_dbitmap>::is(L, 1)) {
816 lua_dbitmap* b = lua_class<lua_dbitmap>::get(L, 1, fname.c_str());
817 for(auto& c : b->pixels)
818 c = tadjust(c, tadj);
819 } else if(lua_class<lua_palette>::is(L, 1)) {
820 lua_palette* p = lua_class<lua_palette>::get(L, 1, fname.c_str());
821 for(auto& c : p->colors)
822 c = tadjust(c, tadj);
823 } else {
824 throw std::runtime_error("Expected BITMAP or PALETTE as argument 1 for "
825 "gui.adjust_transparency");
827 return 2;
831 DECLARE_LUACLASS(lua_palette, "PALETTE");
832 DECLARE_LUACLASS(lua_bitmap, "BITMAP");
833 DECLARE_LUACLASS(lua_dbitmap, "DBITMAP");