Use master state for trampolines
[lsnes.git] / src / lua / gui-bitmap.cpp
blob760f1545077c98cde59a37ca94526545447988e5
1 #include "lua/internal.hpp"
2 #include "core/framebuffer.hpp"
3 #include "core/instance.hpp"
4 #include "core/messages.hpp"
5 #include "core/misc.hpp"
6 #include "library/lua-framebuffer.hpp"
7 #include "library/minmax.hpp"
8 #include "library/png.hpp"
9 #include "library/range.hpp"
10 #include "library/sha256.hpp"
11 #include "library/serialization.hpp"
12 #include "library/string.hpp"
13 #include "library/zip.hpp"
14 #include "lua/bitmap.hpp"
15 #include "library/threads.hpp"
16 #include <vector>
17 #include <sstream>
19 std::vector<char> lua_dbitmap::save_png() const
21 png::encoder img;
22 img.width = width;
23 img.height = height;
24 img.has_palette = false;
25 img.has_alpha = false;
26 img.data.resize(width * height);
27 for(size_t i = 0; i < width * height; i++) {
28 const framebuffer::color& c = pixels[i];
29 if(c.origa != 256)
30 img.has_alpha = true;
31 img.data[i] = c.orig + ((uint32_t)(c.origa - (c.origa >> 7) + (c.origa >> 8)) << 24);
33 std::ostringstream tmp1;
34 img.encode(tmp1);
35 std::string tmp2 = tmp1.str();
36 return std::vector<char>(tmp2.begin(), tmp2.end());
39 std::vector<char> lua_bitmap::save_png(const lua_palette& pal) const
41 png::encoder img;
42 img.width = width;
43 img.height = height;
44 img.has_palette = true;
45 img.has_alpha = false;
46 img.data.resize(width * height);
47 img.palette.resize(pal.color_count);
48 for(size_t i = 0; i < width * height; i++) {
49 img.data[i] = pixels[i];
51 for(size_t i = 0; i < pal.color_count; i++) {
52 const framebuffer::color& c = pal.colors[i];
53 if(c.origa != 256)
54 img.has_alpha = true;
55 img.palette[i] = c.orig + ((uint32_t)(c.origa - (c.origa >> 7) + (c.origa >> 8)) << 24);
57 std::ostringstream tmp1;
58 img.encode(tmp1);
59 std::string tmp2 = tmp1.str();
60 return std::vector<char>(tmp2.begin(), tmp2.end());
63 namespace
65 const char* CONST_base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
67 struct render_object_bitmap : public framebuffer::object
69 render_object_bitmap(int32_t _x, int32_t _y, lua::objpin<lua_bitmap>& _bitmap,
70 lua::objpin<lua_palette>& _palette, int32_t _x0, int32_t _y0, uint32_t _dw, uint32_t _dh,
71 bool _outside) throw()
73 x = _x;
74 y = _y;
75 b = _bitmap;
76 p = _palette;
77 x0 = _x0;
78 y0 = _y0;
79 dw = _dw;
80 dh = _dh;
81 outside = _outside;
84 render_object_bitmap(int32_t _x, int32_t _y, lua::objpin<lua_dbitmap>& _bitmap, int32_t _x0,
85 int32_t _y0, uint32_t _dw, uint32_t _dh, bool _outside) throw()
87 x = _x;
88 y = _y;
89 b2 = _bitmap;
90 x0 = _x0;
91 y0 = _y0;
92 dw = _dw;
93 dh = _dh;
94 outside = _outside;
97 ~render_object_bitmap() throw()
101 bool kill_request(void* obj) throw()
103 return kill_request_ifeq(p.object(), obj) ||
104 kill_request_ifeq(b.object(), obj) ||
105 kill_request_ifeq(b2.object(), obj);
108 template<bool T> void composite_op(struct framebuffer::fb<T>& scr) throw()
110 if(p)
111 p->palette_mutex.lock();
112 uint32_t oX = x + scr.get_origin_x() - x0;
113 uint32_t oY = y + scr.get_origin_y() - y0;
114 size_t pallim = 0;
115 size_t w, h;
116 framebuffer::color* palette;
117 if(b) {
118 palette = p->colors;
119 pallim = p->color_count;
120 w = b->width;
121 h = b->height;
122 } else {
123 palette = NULL; //Won't be accessed.
124 w = b2->width;
125 h = b2->height;
128 range bX = ((range::make_w(scr.get_width()) - oX) & range::make_w(w) &
129 range::make_s(x0, dw));
130 range bY = ((range::make_w(scr.get_height()) - oY) & range::make_w(h) &
131 range::make_s(y0, dh));
132 range sX = range::make_s(-x + x0, scr.get_last_blit_width());
133 range sY = range::make_s(-y + y0, scr.get_last_blit_height());
135 for(uint32_t r = bY.low(); r < bY.high(); r++) {
136 typename framebuffer::fb<T>::element_t* rptr = scr.rowptr(oY + r);
137 size_t eptr = oX + bX.low();
138 uint32_t xmin = bX.low();
139 bool cut = outside && sY.in(r);
140 if(cut && sX.in(xmin)) {
141 xmin = sX.high();
142 eptr += (sX.high() - bX.low());
144 if(b)
145 for(uint32_t c = xmin; c < bX.high(); c++, eptr++) {
146 if(__builtin_expect(cut && c == sX.low(), 0)) {
147 c += sX.size();
148 if(c >= bX.high()) break;
149 eptr += sX.size();
151 uint16_t i = b->pixels[r * b->width + c];
152 if(i < pallim)
153 palette[i].apply(rptr[eptr]);
155 else
156 for(uint32_t c = xmin; c < bX.high(); c++, eptr++) {
157 if(__builtin_expect(cut && c == sX.low(), 0)) {
158 c += sX.size();
159 if(c >= bX.high()) break;
160 eptr += sX.size();
162 b2->pixels[r * b2->width + c].apply(rptr[eptr]);
165 if(p)
166 p->palette_mutex.unlock();
168 void operator()(struct framebuffer::fb<false>& x) throw() { composite_op(x); }
169 void operator()(struct framebuffer::fb<true>& x) throw() { composite_op(x); }
170 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
171 private:
172 int32_t x;
173 int32_t y;
174 lua::objpin<lua_bitmap> b;
175 lua::objpin<lua_dbitmap> b2;
176 lua::objpin<lua_palette> p;
177 int32_t x0;
178 int32_t y0;
179 uint32_t dw;
180 uint32_t dh;
181 bool outside;
184 struct operand_dbitmap
186 typedef framebuffer::color pixel_t;
187 typedef framebuffer::color rpixel_t;
188 operand_dbitmap(lua_dbitmap& _bitmap)
189 : bitmap(_bitmap), _transparent(-1)
191 pixels = bitmap.pixels;
193 size_t get_width() { return bitmap.width; }
194 size_t get_height() { return bitmap.height; }
195 const rpixel_t& read(size_t idx) { return pixels[idx]; }
196 const pixel_t& lookup(const rpixel_t& p) { return p; }
197 void write(size_t idx, const pixel_t& v) { pixels[idx] = v; }
198 bool is_opaque(const rpixel_t& p) { return p.origa > 0; }
199 const pixel_t& transparent() { return _transparent; }
200 private:
201 lua_dbitmap& bitmap;
202 pixel_t* pixels;
203 framebuffer::color _transparent;
206 struct operand_bitmap
208 typedef uint16_t pixel_t;
209 typedef uint16_t rpixel_t;
210 operand_bitmap(lua_bitmap& _bitmap)
211 : bitmap(_bitmap)
213 pixels = bitmap.pixels;
215 size_t get_width() { return bitmap.width; }
216 size_t get_height() { return bitmap.height; }
217 const rpixel_t& read(size_t idx) { return pixels[idx]; }
218 const pixel_t& lookup(const rpixel_t& p) { return p; }
219 void write(size_t idx, const pixel_t& v) { pixels[idx] = v; }
220 bool is_opaque(const rpixel_t& p) { return p > 0; }
221 pixel_t transparent() { return 0; }
222 private:
223 lua_bitmap& bitmap;
224 pixel_t* pixels;
227 struct operand_bitmap_pal
229 typedef framebuffer::color pixel_t;
230 typedef uint16_t rpixel_t;
231 operand_bitmap_pal(lua_bitmap& _bitmap, lua_palette& _palette)
232 : bitmap(_bitmap), palette(_palette), _transparent(-1)
234 pixels = bitmap.pixels;
235 limit = palette.color_count;
236 pal = palette.colors;
238 size_t get_width() { return bitmap.width; }
239 size_t get_height() { return bitmap.height; }
240 const rpixel_t& read(size_t idx) { return pixels[idx]; }
241 const pixel_t& lookup(const rpixel_t& p) { return *((p < limit) ? pal + p : &_transparent); }
242 bool is_opaque(const rpixel_t& p) { return p > 0; }
243 const pixel_t& transparent() { return _transparent; }
244 private:
245 lua_bitmap& bitmap;
246 lua_palette& palette;
247 uint16_t* pixels;
248 framebuffer::color* pal;
249 uint32_t limit;
250 framebuffer::color _transparent;
253 struct colorkey_none
255 bool iskey(uint16_t& c) const { return false; }
256 bool iskey(framebuffer::color& c) const { return false; }
259 struct colorkey_direct
261 colorkey_direct(uint64_t _ck)
263 framebuffer::color c(_ck);
264 ck = c.orig;
265 cka = c.origa;
267 bool iskey(framebuffer::color& c) const { return (c.orig == ck && c.origa == cka); }
268 uint32_t ck;
269 uint16_t cka;
272 struct colorkey_palette
274 colorkey_palette(uint64_t _ck) { ck = _ck; }
275 bool iskey(uint16_t& c) const { return (c == ck); }
276 uint16_t ck;
279 template<class _src, class _dest, class colorkey> struct srcdest
281 srcdest(_dest Xdest, _src Xsrc, const colorkey& _ckey)
282 : dest(Xdest), src(Xsrc), ckey(_ckey)
284 swidth = src.get_width();
285 sheight = src.get_height();
286 dwidth = dest.get_width();
287 dheight = dest.get_height();
289 void copy(size_t didx, size_t sidx)
291 typename _src::rpixel_t c = src.read(sidx);
292 if(!ckey.iskey(c))
293 dest.write(didx, src.lookup(c));
295 size_t swidth, sheight, dwidth, dheight;
296 private:
297 _dest dest;
298 _src src;
299 colorkey ckey;
302 template<class _src, class _dest, class colorkey> srcdest<_src, _dest, colorkey> mk_srcdest(_dest dest,
303 _src src, const colorkey& ckey)
305 return srcdest<_src, _dest, colorkey>(dest, src, ckey);
308 struct srcdest_priority
310 srcdest_priority(lua_bitmap& dest, lua_bitmap& src)
312 darray = dest.pixels;
313 sarray = src.pixels;
314 swidth = src.width;
315 sheight = src.height;
316 dwidth = dest.width;
317 dheight = dest.height;
319 void copy(size_t didx, size_t sidx)
321 uint16_t c = sarray[sidx];
322 if(darray[didx] < c)
323 darray[didx] = c;
325 size_t swidth, sheight, dwidth, dheight;
326 private:
327 uint16_t* sarray;
328 uint16_t* darray;
331 enum porterduff_oper
333 PD_SRC,
334 PD_ATOP,
335 PD_OVER,
336 PD_IN,
337 PD_OUT,
338 PD_DEST,
339 PD_DEST_ATOP,
340 PD_DEST_OVER,
341 PD_DEST_IN,
342 PD_DEST_OUT,
343 PD_CLEAR,
344 PD_XOR
347 porterduff_oper get_pd_oper(const std::string& oper)
349 if(oper == "Src") return PD_SRC;
350 if(oper == "Atop") return PD_ATOP;
351 if(oper == "Over") return PD_OVER;
352 if(oper == "In") return PD_IN;
353 if(oper == "Out") return PD_OUT;
354 if(oper == "Dest") return PD_DEST;
355 if(oper == "DestAtop") return PD_DEST_ATOP;
356 if(oper == "DestOver") return PD_DEST_OVER;
357 if(oper == "DestIn") return PD_DEST_IN;
358 if(oper == "DestOut") return PD_DEST_OUT;
359 if(oper == "Clear") return PD_CLEAR;
360 if(oper == "Xor") return PD_XOR;
361 (stringfmt() << "Bad Porter-Duff operator '" << oper << "'").throwex();
362 return PD_SRC; //NOTREACHED
365 template<porterduff_oper oper, class _src, class _dest> struct srcdest_porterduff
367 srcdest_porterduff(_dest Xdest, _src Xsrc)
368 : dest(Xdest), src(Xsrc)
370 swidth = src.get_width();
371 sheight = src.get_height();
372 dwidth = dest.get_width();
373 dheight = dest.get_height();
375 void copy(size_t didx, size_t sidx)
377 typename _dest::rpixel_t vd = dest.read(didx);
378 typename _src::rpixel_t vs = src.read(sidx);
379 bool od = dest.is_opaque(vd);
380 bool os = src.is_opaque(vs);
381 typename _dest::pixel_t ld = dest.lookup(vd);
382 typename _src::pixel_t ls = src.lookup(vs);
383 typename _dest::pixel_t t = dest.transparent();
384 typename _dest::pixel_t r;
385 switch(oper) {
386 case PD_SRC: r = ls; break;
387 case PD_ATOP: r = od ? (os ? ls : ld) : t; break;
388 case PD_OVER: r = os ? ls : ld; break;
389 case PD_IN: r = (od & os) ? ls : t; break;
390 case PD_OUT: r = (!od && os) ? ls : t; break;
391 case PD_DEST: r = ld; break;
392 case PD_DEST_ATOP: r = os ? (od ? ld : ls) : t; break;
393 case PD_DEST_OVER: r = od ? ld : ls; break;
394 case PD_DEST_IN: r = (od & os) ? ld : t; break;
395 case PD_DEST_OUT: r = (od & !os) ? ld : t; break;
396 case PD_CLEAR: r = t; break;
397 case PD_XOR: r = od ? (os ? t : ld) : ls; break;
399 dest.write(didx, r);
401 size_t swidth, sheight, dwidth, dheight;
402 private:
403 _dest dest;
404 _src src;
407 template<porterduff_oper oper, class _src, class _dest> srcdest_porterduff<oper, _src, _dest>
408 mk_porterduff(_dest dest, _src src)
410 return srcdest_porterduff<oper, _src, _dest>(dest, src);
413 template<class srcdest>
414 void xblit_copy(srcdest sd, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h)
416 while((dx + w > sd.dwidth || sx + w > sd.swidth) && w > 0)
417 w--;
418 while((dy + h > sd.dheight || sy + h > sd.sheight) && h > 0)
419 h--;
420 if(dx + w < w || dy + h < h) return; //Don't do overflowing blits.
421 if(sx + w < w || sy + h < h) return; //Don't do overflowing blits.
422 size_t sidx = sy * sd.swidth + sx;
423 size_t didx = dy * sd.dwidth + dx;
424 size_t srskip = sd.swidth - w;
425 size_t drskip = sd.dwidth - w;
426 for(uint32_t j = 0; j < h; j++) {
427 for(uint32_t i = 0; i < w; i++) {
428 sd.copy(didx, sidx);
429 sidx++;
430 didx++;
432 sidx += srskip;
433 didx += drskip;
437 template<class srcdest>
438 void xblit_scaled(srcdest sd, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h,
439 uint32_t hscl, uint32_t vscl)
441 if(!hscl || !vscl) return;
442 w = max(static_cast<uint32_t>(sd.dwidth / hscl), w);
443 h = max(static_cast<uint32_t>(sd.dheight / vscl), h);
444 while((dx + hscl * w > sd.dwidth || sx + w > sd.swidth) && w > 0)
445 w--;
446 while((dy + vscl * h > sd.dheight || sy + h > sd.sheight) && h > 0)
447 h--;
448 if(dx + hscl * w < dx || dy + vscl * h < dy) return; //Don't do overflowing blits.
449 if(sx + w < w || sy + h < h) return; //Don't do overflowing blits.
450 size_t sidx = sy * sd.swidth + sx;
451 size_t didx = dy * sd.dwidth + dx;
452 size_t drskip = sd.dwidth - hscl * w;
453 uint32_t _w = hscl * w;
454 for(uint32_t j = 0; j < vscl * h; j++) {
455 uint32_t _sidx = sidx;
456 for(uint32_t i = 0; i < _w ; i += hscl) {
457 for(uint32_t k = 0; k < hscl; k++)
458 sd.copy(didx + k, _sidx);
459 _sidx++;
460 didx+=hscl;
462 if((j % vscl) == vscl - 1)
463 sidx += sd.swidth;
464 didx += drskip;
468 template<bool scaled, class srcdest>
469 inline void xblit(srcdest sd, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h,
470 uint32_t hscl, uint32_t vscl)
472 if(scaled)
473 xblit_scaled(sd, dx, dy, sx, sy, w, h, hscl, vscl);
474 else
475 xblit_copy(sd, dx, dy, sx, sy, w, h);
478 template<bool scaled, class src, class dest>
479 inline void xblit_pal(dest _dest, src _src, uint64_t ck, uint32_t dx, uint32_t dy, uint32_t sx,
480 uint32_t sy, uint32_t w, uint32_t h, uint32_t hscl, uint32_t vscl)
482 if(ck > 65535)
483 xblit<scaled>(mk_srcdest(_dest, _src, colorkey_none()), dx, dy, sx, sy, w, h,
484 hscl, vscl);
485 else
486 xblit<scaled>(mk_srcdest(_dest, _src, colorkey_palette(ck)), dx, dy, sx, sy, w, h,
487 hscl, vscl);
490 template<bool scaled, class src, class dest>
491 inline void xblit_dir(dest _dest, src _src, uint64_t ck, uint32_t dx, uint32_t dy, uint32_t sx,
492 uint32_t sy, uint32_t w, uint32_t h, uint32_t hscl, uint32_t vscl)
494 if(ck == 0x100000000ULL)
495 xblit<scaled>(mk_srcdest(_dest, _src, colorkey_none()), dx, dy, sx, sy, w, h,
496 hscl, vscl);
497 else
498 xblit<scaled>(mk_srcdest(_dest, _src, colorkey_direct(ck)), dx, dy, sx, sy, w, h,
499 hscl, vscl);
502 template<bool scaled, porterduff_oper oper, class src, class dest>
503 inline void xblit_pduff2(dest _dest, src _src, uint32_t dx, uint32_t dy, uint32_t sx,
504 uint32_t sy, uint32_t w, uint32_t h, uint32_t hscl, uint32_t vscl)
506 xblit<scaled>(mk_porterduff<oper>(_dest, _src), dx, dy, sx, sy, w, h, hscl, vscl);
509 template<bool scaled, class src, class dest>
510 inline void xblit_pduff(dest _dest, src _src, uint32_t dx, uint32_t dy, uint32_t sx,
511 uint32_t sy, uint32_t w, uint32_t h, uint32_t hscl, uint32_t vscl, porterduff_oper oper)
513 switch(oper) {
514 case PD_ATOP:
515 xblit_pduff2<scaled, PD_ATOP>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
516 break;
517 case PD_CLEAR:
518 xblit_pduff2<scaled, PD_CLEAR>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
519 break;
520 case PD_DEST:
521 xblit_pduff2<scaled, PD_DEST>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
522 break;
523 case PD_DEST_ATOP:
524 xblit_pduff2<scaled, PD_DEST_ATOP>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
525 break;
526 case PD_DEST_IN:
527 xblit_pduff2<scaled, PD_DEST_IN>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
528 break;
529 case PD_DEST_OUT:
530 xblit_pduff2<scaled, PD_DEST_OUT>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
531 break;
532 case PD_DEST_OVER:
533 xblit_pduff2<scaled, PD_DEST_OVER>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
534 break;
535 case PD_IN:
536 xblit_pduff2<scaled, PD_IN>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
537 break;
538 case PD_OUT:
539 xblit_pduff2<scaled, PD_OUT>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
540 break;
541 case PD_OVER:
542 xblit_pduff2<scaled, PD_OVER>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
543 break;
544 case PD_SRC:
545 xblit_pduff2<scaled, PD_SRC>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
546 break;
547 case PD_XOR:
548 xblit_pduff2<scaled, PD_XOR>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
549 break;
553 int bitmap_load_fn(lua::state& L, std::function<lua_loaded_bitmap()> src)
555 auto bitmap = src();
556 if(bitmap.d) {
557 lua_dbitmap* b = lua::_class<lua_dbitmap>::create(L, bitmap.w, bitmap.h);
558 for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
559 b->pixels[i] = framebuffer::color(bitmap.bitmap[i]);
560 return 1;
561 } else {
562 lua_bitmap* b = lua::_class<lua_bitmap>::create(L, bitmap.w, bitmap.h);
563 lua_palette* p = lua::_class<lua_palette>::create(L);
564 for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
565 b->pixels[i] = bitmap.bitmap[i];
566 p->adjust_palette_size(bitmap.palette.size());
567 for(size_t i = 0; i < bitmap.palette.size(); i++)
568 p->colors[i] = framebuffer::color(bitmap.palette[i]);
569 return 2;
573 inline int64_t mangle_color(uint32_t c)
575 if(c < 0x1000000)
576 return -1;
577 else
578 return ((256 - (c >> 24) - (c >> 31)) << 24) | (c & 0xFFFFFF);
581 int base64val(char ch)
583 if(ch >= 'A' && ch <= 'Z')
584 return ch - 65;
585 if(ch >= 'a' && ch <= 'z')
586 return ch - 97 + 26;
587 if(ch >= '0' && ch <= '9')
588 return ch - 48 + 52;
589 if(ch == '+')
590 return 62;
591 if(ch == '/')
592 return 63;
593 if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
594 return -1;
595 if(ch == '=')
596 return -2;
597 return -3;
600 std::string base64_encode(const std::string& str)
602 std::ostringstream x;
603 unsigned pos = 0;
604 uint32_t mem = 0;
605 for(auto i : str) {
606 mem = (mem << 8) + (unsigned char)i;
607 if(++pos == 3) {
608 uint8_t c1 = (mem >> 18) & 0x3F;
609 uint8_t c2 = (mem >> 12) & 0x3F;
610 uint8_t c3 = (mem >> 6) & 0x3F;
611 uint8_t c4 = mem & 0x3F;
612 x << CONST_base64chars[c1];
613 x << CONST_base64chars[c2];
614 x << CONST_base64chars[c3];
615 x << CONST_base64chars[c4];
616 mem = 0;
617 pos = 0;
620 if(pos == 2) {
621 uint8_t c1 = (mem >> 10) & 0x3F;
622 uint8_t c2 = (mem >> 4) & 0x3F;
623 uint8_t c3 = (mem << 2) & 0x3F;
624 x << CONST_base64chars[c1];
625 x << CONST_base64chars[c2];
626 x << CONST_base64chars[c3];
627 x << "=";
629 if(pos == 1) {
630 uint8_t c1 = (mem >> 2) & 0x3F;
631 uint8_t c2 = (mem << 4) & 0x3F;
632 x << CONST_base64chars[c1];
633 x << CONST_base64chars[c2];
634 x << "==";
636 return x.str();
639 std::string base64_decode(const std::string& str)
641 bool end = 0;
642 uint32_t memory = 0;
643 uint32_t memsize = 1;
644 int posmod = 0;
645 std::ostringstream x;
646 for(auto i : str) {
647 int v = base64val(i);
648 if(v == -1)
649 continue;
650 posmod = (posmod + 1) & 3;
651 if(v == -2 && (posmod == 1 || posmod == 2))
652 throw std::runtime_error("Invalid Base64");
653 if(v == -2) {
654 end = true;
655 continue;
657 if(v == -3 || end)
658 throw std::runtime_error("Invalid Base64");
659 memory = memory * 64 + v;
660 memsize = memsize * 64;
661 if(memsize >= 256) {
662 memsize >>= 8;
663 x << static_cast<uint8_t>(memory / memsize);
664 memory %= memsize;
667 return x.str();
670 template<typename T>
671 int bitmap_load_png_fn(lua::state& L, T& src)
673 png::decoder img(src);
674 if(img.has_palette) {
675 lua_bitmap* b = lua::_class<lua_bitmap>::create(L, img.width, img.height);
676 lua_palette* p = lua::_class<lua_palette>::create(L);
677 for(size_t i = 0; i < img.width * img.height; i++)
678 b->pixels[i] = img.data[i];
679 p->adjust_palette_size(img.palette.size());
680 for(size_t i = 0; i < img.palette.size(); i++)
681 p->colors[i] = framebuffer::color(mangle_color(img.palette[i]));
682 return 2;
683 } else {
684 lua_dbitmap* b = lua::_class<lua_dbitmap>::create(L, img.width, img.height);
685 for(size_t i = 0; i < img.width * img.height; i++)
686 b->pixels[i] = framebuffer::color(mangle_color(img.data[i]));
687 return 1;
691 int bitmap_palette_fn(lua::state& L, std::istream& s)
693 lua_palette* p = lua::_class<lua_palette>::create(L);
694 while(s) {
695 std::string line;
696 std::getline(s, line);
697 istrip_CR(line);
698 regex_results r;
699 if(!regex_match("[ \t]*(#.*)?", line)) {
700 //Nothing.
701 } else if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
702 int64_t cr, cg, cb, ca;
703 cr = parse_value<uint8_t>(r[1]);
704 cg = parse_value<uint8_t>(r[2]);
705 cb = parse_value<uint8_t>(r[3]);
706 ca = 256 - parse_value<uint16_t>(r[4]);
707 if(ca == 256)
708 p->push_back(framebuffer::color(-1));
709 else
710 p->push_back(framebuffer::color((ca << 24) | (cr << 16)
711 | (cg << 8) | cb));
712 } else if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
713 int64_t cr, cg, cb;
714 cr = parse_value<uint8_t>(r[1]);
715 cg = parse_value<uint8_t>(r[2]);
716 cb = parse_value<uint8_t>(r[3]);
717 p->push_back(framebuffer::color((cr << 16) | (cg << 8) | cb));
718 } else if(r = regex("[ \t]*([^ \t]|[^ \t].*[^ \t])[ \t]*", line)) {
719 p->push_back(framebuffer::color(r[1]));
720 } else
721 throw std::runtime_error("Invalid line format (" + line + ")");
723 return 1;
726 inline framebuffer::color tadjust(framebuffer::color c, uint16_t adj)
728 uint32_t rgb = c.orig;
729 uint32_t a = c.origa;
730 a = (a * adj) >> 8;
731 if(a > 256)
732 a = 256;
733 if(a == 0)
734 return framebuffer::color(-1);
735 else
736 return framebuffer::color(rgb | ((uint32_t)(256 - a) << 24));
739 lua::_class<lua_loaded_bitmap> LUA_class_loaded_bitmap(lua_class_gui, "IMAGELOADER", {
740 {"load", lua_loaded_bitmap::load<false>},
741 {"load_str", lua_loaded_bitmap::load_str<false>},
742 {"load_png", lua_loaded_bitmap::load<true>},
743 {"load_png_str", lua_loaded_bitmap::load_str<true>},
746 lua::_class<lua_palette> LUA_class_palette(lua_class_gui, "PALETTE", {
747 {"new", lua_palette::create},
748 {"load", lua_palette::load},
749 {"load_str", lua_palette::load_str},
750 }, {
751 {"set", &lua_palette::set},
752 {"get", &lua_palette::get},
753 {"hash", &lua_palette::hash},
754 {"debug", &lua_palette::debug},
755 {"adjust_transparency", &lua_palette::adjust_transparency},
756 }, &lua_palette::print);
758 lua::_class<lua_bitmap> LUA_class_bitmap(lua_class_gui, "BITMAP", {
759 {"new", lua_bitmap::create},
760 }, {
761 {"draw", &lua_bitmap::draw<false, false>},
762 {"draw_clip", &lua_bitmap::draw<false, true>},
763 {"draw_outside", &lua_bitmap::draw<true, false>},
764 {"draw_clip_outside", &lua_bitmap::draw<true, true>},
765 {"pset", &lua_bitmap::pset},
766 {"pget", &lua_bitmap::pget},
767 {"size", &lua_bitmap::size},
768 {"hash", &lua_bitmap::hash},
769 {"blit", &lua_bitmap::blit<false, false>},
770 {"blit_priority", &lua_bitmap::blit_priority<false>},
771 {"blit_scaled", &lua_bitmap::blit<true, false>},
772 {"blit_scaled_priority", &lua_bitmap::blit_priority<true>},
773 {"blit_porterduff", &lua_bitmap::blit<false, true>},
774 {"blit_scaled_porterduff", &lua_bitmap::blit<true, true>},
775 {"save_png", &lua_bitmap::save_png},
776 }, &lua_bitmap::print);
778 lua::_class<lua_dbitmap> LUA_class_dbitmap(lua_class_gui, "DBITMAP", {
779 {"new", lua_dbitmap::create},
780 }, {
781 {"draw", &lua_dbitmap::draw<false, false>},
782 {"draw_clip", &lua_dbitmap::draw<false, true>},
783 {"draw_outside", &lua_dbitmap::draw<true, false>},
784 {"draw_clip_outside", &lua_dbitmap::draw<true, true>},
785 {"pset", &lua_dbitmap::pset},
786 {"pget", &lua_dbitmap::pget},
787 {"size", &lua_dbitmap::size},
788 {"hash", &lua_dbitmap::hash},
789 {"blit", &lua_dbitmap::blit<false, false>},
790 {"blit_scaled", &lua_dbitmap::blit<true, false>},
791 {"blit_porterduff", &lua_dbitmap::blit<false, true>},
792 {"blit_scaled_porterduff", &lua_dbitmap::blit<true, true>},
793 {"save_png", &lua_dbitmap::save_png},
794 {"adjust_transparency", &lua_dbitmap::adjust_transparency},
795 }, &lua_dbitmap::print);
798 /** Palette **/
799 lua_palette::lua_palette(lua::state& L)
801 color_count = 0;
802 scolors = colors = lua::align_overcommit<lua_palette, framebuffer::color>(this);
803 for(unsigned i = 0; i < reserved_colors; i++)
804 scolors[i] = framebuffer::color(-1);
807 lua_palette::~lua_palette()
809 CORE().fbuf->render_kill_request(this);
812 std::string lua_palette::print()
814 size_t s = color_count;
815 return (stringfmt() << s << " " << ((s != 1) ? "colors" : "color")).str();
818 int lua_palette::create(lua::state& L, lua::parameters& P)
820 lua::_class<lua_palette>::create(L);
821 return 1;
824 int lua_palette::load(lua::state& L, lua::parameters& P)
826 std::string name, name2;
828 P(name, P.optional(name2, ""));
830 std::istream& s = zip::openrel(name, name2);
831 try {
832 int r = bitmap_palette_fn(L, s);
833 delete &s;
834 return r;
835 } catch(...) {
836 delete &s;
837 throw;
841 int lua_palette::load_str(lua::state& L, lua::parameters& P)
843 std::string content;
845 P(content);
847 std::istringstream s(content);
848 return bitmap_palette_fn(L, s);
851 int lua_palette::set(lua::state& L, lua::parameters& P)
853 framebuffer::color nc;
854 uint16_t c;
856 P(P.skipped(), c, nc);
858 if(this->color_count <= c) {
859 this->adjust_palette_size(static_cast<uint32_t>(c) + 1);
861 this->colors[c] = nc;
862 return 0;
865 int lua_palette::get(lua::state& L, lua::parameters& P)
867 framebuffer::color nc;
868 uint16_t c;
869 int64_t _nc;
871 P(P.skipped(), c);
873 if(this->color_count <= c) {
874 return 0;
876 nc = this->colors[c];
877 uint32_t rgb = nc.orig;
878 uint32_t a = nc.origa;
879 if(a == 0)
880 _nc = -1;
881 else
882 _nc = ((256 - a) << 24) | rgb;
883 L.pushnumber(_nc);
884 return 1;
887 int lua_palette::hash(lua::state& L, lua::parameters& P)
889 sha256 h;
890 const int buffersize = 256;
891 int bufferuse = 0;
892 char buf[buffersize];
893 unsigned realsize = 0;
894 for(unsigned i = 0; i < this->color_count; i++)
895 if(this->colors[i].origa) realsize = i + 1;
896 for(unsigned i = 0; i < realsize; i++) {
897 if(bufferuse + 6 > buffersize) {
898 h.write(buf, bufferuse);
899 bufferuse = 0;
901 serialization::u32b(buf + bufferuse + 0, this->colors[i].orig);
902 serialization::u16b(buf + bufferuse + 4, this->colors[i].origa);
903 bufferuse += 6;
905 if(bufferuse > 0) h.write(buf, bufferuse);
906 L.pushlstring(h.read());
907 return 1;
910 int lua_palette::debug(lua::state& L, lua::parameters& P)
912 for(size_t i = 0; i < color_count; i++)
913 messages << "Color #" << i << ": " << colors[i].orig << ":" << colors[i].origa << std::endl;
914 return 0;
917 int lua_palette::adjust_transparency(lua::state& L, lua::parameters& P)
919 uint16_t tadj;
921 P(P.skipped(), tadj);
923 for(size_t i = 0; i < color_count; i++)
924 colors[i] = tadjust(colors[i], tadj);
925 return 0;
928 void lua_palette::adjust_palette_size(size_t newsize)
930 threads::alock h(palette_mutex);
931 if(newsize > reserved_colors) {
932 lcolors.resize(newsize);
933 if(color_count <= reserved_colors) {
934 for(unsigned i = 0; i < color_count; i++)
935 lcolors[i] = colors[i];
937 colors = &lcolors[0];
938 } else {
939 if(color_count > reserved_colors) {
940 for(unsigned i = 0; i < color_count; i++)
941 scolors[i] = lcolors[i];
943 colors = scolors;
945 color_count = newsize;
948 void lua_palette::push_back(const framebuffer::color& cx)
950 size_t c = color_count;
951 adjust_palette_size(c + 1);
952 colors[c] = cx;
955 /** BITMAP **/
956 lua_bitmap::lua_bitmap(lua::state& L, uint32_t w, uint32_t h)
958 if(overcommit(w, h) / h / sizeof(uint16_t) < w) throw std::bad_alloc();
959 width = w;
960 height = h;
961 pixels = lua::align_overcommit<lua_bitmap, uint16_t>(this);
962 memset(pixels, 0, 2 * width * height);
965 lua_bitmap::~lua_bitmap()
967 CORE().fbuf->render_kill_request(this);
970 std::string lua_bitmap::print()
972 return (stringfmt() << width << "*" << height).str();
975 int lua_bitmap::create(lua::state& L, lua::parameters& P)
977 uint32_t w, h;
978 uint16_t c;
980 P(w, h, P.optional(c, 0));
982 lua_bitmap* b = lua::_class<lua_bitmap>::create(L, w, h);
983 for(size_t i = 0; i < b->width * b->height; i++)
984 b->pixels[i] = c;
985 return 1;
988 template<bool outside, bool clip>
989 int lua_bitmap::draw(lua::state& L, lua::parameters& P)
991 auto& core = CORE();
992 int32_t x, y;
993 lua::objpin<lua_bitmap> b;
994 lua::objpin<lua_palette> p;
996 if(!core.lua2->render_ctx) return 0;
998 P(b, x, y, p);
1000 int32_t x0 = 0, y0 = 0, dw = b->width, dh = b->height;
1001 if(clip) P(x0, y0, dw, dh);
1003 core.lua2->render_ctx->queue->create_add<render_object_bitmap>(x, y, b, p, x0, y0, dw, dh, outside);
1004 return 0;
1007 int lua_bitmap::pset(lua::state& L, lua::parameters& P)
1009 uint32_t x, y;
1010 uint16_t c;
1012 P(P.skipped(), x, y, c);
1014 if(x >= this->width || y >= this->height)
1015 return 0;
1016 this->pixels[y * this->width + x] = c;
1017 return 0;
1020 int lua_bitmap::pget(lua::state& L, lua::parameters& P)
1022 uint32_t x, y;
1024 P(P.skipped(), x, y);
1026 if(x >= this->width || y >= this->height)
1027 return 0;
1028 L.pushnumber(this->pixels[y * this->width + x]);
1029 return 1;
1032 int lua_bitmap::size(lua::state& L, lua::parameters& P)
1034 L.pushnumber(this->width);
1035 L.pushnumber(this->height);
1036 return 2;
1039 int lua_bitmap::hash(lua::state& L, lua::parameters& P)
1041 sha256 h;
1042 const int buffersize = 256;
1043 int bufferuse = 0;
1044 char buf[buffersize];
1045 memset(buf, 0, buffersize);
1046 serialization::u64b(buf + 0, this->width);
1047 serialization::u64b(buf + 8, this->height);
1048 bufferuse = 16;
1049 for(unsigned i = 0; i < this->width * this->height; i++) {
1050 if(bufferuse + 2 > buffersize) {
1051 h.write(buf, bufferuse);
1052 bufferuse = 0;
1054 serialization::u16b(buf + bufferuse + 0, this->pixels[i]);
1055 bufferuse += 2;
1057 if(bufferuse > 0) h.write(buf, bufferuse);
1058 L.pushlstring(h.read());
1059 return 1;
1062 template<bool scaled, bool porterduff> int lua_bitmap::blit(lua::state& L, lua::parameters& P)
1064 uint32_t dx, dy, sx, sy, w, h, hscl, vscl;
1065 lua_bitmap* src_p;
1067 P(P.skipped(), dx, dy, src_p, sx, sy, w, h);
1068 if(scaled)
1069 P(hscl, P.optional2(vscl, hscl));
1071 if(porterduff) {
1072 porterduff_oper pd_oper = get_pd_oper(P.arg<std::string>());
1073 xblit_pduff<scaled>(operand_bitmap(*this), operand_bitmap(*src_p), dx, dy, sx, sy, w, h, hscl, vscl,
1074 pd_oper);
1075 } else {
1076 int64_t ck = P.arg_opt<uint64_t>(65536);
1077 xblit_pal<scaled>(operand_bitmap(*this), operand_bitmap(*src_p), ck, dx, dy, sx, sy, w, h,
1078 hscl, vscl);
1080 return 0;
1083 template<bool scaled> int lua_bitmap::blit_priority(lua::state& L, lua::parameters& P)
1085 uint32_t dx, dy, sx, sy, w, h, hscl, vscl;
1086 lua_bitmap* src_p;
1088 P(P.skipped(), dx, dy, src_p, sx, sy, w, h);
1089 if(scaled)
1090 P(hscl, P.optional2(vscl, hscl));
1092 xblit<scaled>(srcdest_priority(*this, *src_p), dx, dy, sx, sy, w, h, hscl, vscl);
1093 return 0;
1096 int lua_bitmap::save_png(lua::state& L, lua::parameters& P)
1098 std::string name, name2;
1099 lua_palette* p;
1100 bool was_filename;
1102 P(P.skipped());
1103 if((was_filename = P.is_string())) P(name);
1104 if(P.is_string()) P(name2);
1105 P(p);
1107 auto buf = this->save_png(*p);
1108 if(was_filename) {
1109 std::string filename = zip::resolverel(name, name2);
1110 std::ofstream strm(filename, std::ios::binary);
1111 if(!strm)
1112 throw std::runtime_error("Can't open output file");
1113 strm.write(&buf[0], buf.size());
1114 if(!strm)
1115 throw std::runtime_error("Can't write output file");
1116 return 0;
1117 } else {
1118 std::ostringstream strm;
1119 strm.write(&buf[0], buf.size());
1120 L.pushlstring(base64_encode(strm.str()));
1121 return 1;
1125 /** DBITMAP **/
1126 lua_dbitmap::lua_dbitmap(lua::state& L, uint32_t w, uint32_t h)
1128 if(overcommit(w, h) / h / sizeof(framebuffer::color) < w) throw std::bad_alloc();
1129 width = w;
1130 height = h;
1131 pixels = lua::align_overcommit<lua_dbitmap, framebuffer::color>(this);
1132 //Initialize the bitmap data.
1133 framebuffer::color transparent(-1);
1134 for(size_t i = 0; i < (size_t)width * height; i++)
1135 new(pixels + i) framebuffer::color(transparent);
1138 lua_dbitmap::~lua_dbitmap()
1140 CORE().fbuf->render_kill_request(this);
1143 std::string lua_dbitmap::print()
1145 return (stringfmt() << width << "*" << height).str();
1148 int lua_dbitmap::create(lua::state& L, lua::parameters& P)
1150 uint32_t w, h;
1151 framebuffer::color c;
1153 P(w, h, P.optional(c, -1));
1155 lua_dbitmap* b = lua::_class<lua_dbitmap>::create(L, w, h);
1156 for(size_t i = 0; i < b->width * b->height; i++)
1157 b->pixels[i] = c;
1158 return 1;
1161 template<bool outside, bool clip>
1162 int lua_dbitmap::draw(lua::state& L, lua::parameters& P)
1164 auto& core = CORE();
1165 int32_t x, y;
1166 lua::objpin<lua_dbitmap> b;
1168 if(!core.lua2->render_ctx) return 0;
1170 P(b, x, y);
1172 int32_t x0 = 0, y0 = 0, dw = b->width, dh = b->height;
1173 if(clip) P(x0, y0, dw, dh);
1175 core.lua2->render_ctx->queue->create_add<render_object_bitmap>(x, y, b, x0, y0, dw, dh, outside);
1176 return 0;
1179 int lua_dbitmap::pset(lua::state& L, lua::parameters& P)
1181 uint32_t x, y;
1182 framebuffer::color c;
1184 P(P.skipped(), x, y, c);
1186 if(x >= this->width || y >= this->height)
1187 return 0;
1188 this->pixels[y * this->width + x] = c;
1189 return 0;
1192 int lua_dbitmap::pget(lua::state& L, lua::parameters& P)
1194 uint32_t x, y;
1196 P(P.skipped(), x, y);
1198 if(x >= this->width || y >= this->height)
1199 return 0;
1200 L.pushnumber((this->pixels[y * this->width + x]).asnumber());
1201 return 1;
1204 int lua_dbitmap::size(lua::state& L, lua::parameters& P)
1206 L.pushnumber(this->width);
1207 L.pushnumber(this->height);
1208 return 2;
1211 int lua_dbitmap::hash(lua::state& L, lua::parameters& P)
1213 sha256 h;
1214 const int buffersize = 256;
1215 int bufferuse = 0;
1216 char buf[buffersize];
1217 memset(buf, 0, buffersize);
1218 serialization::u64b(buf + 0, this->width);
1219 serialization::u64b(buf + 4, this->height);
1220 bufferuse = 16;
1221 for(unsigned i = 0; i < this->width * this->height; i++) {
1222 if(bufferuse + 6 > buffersize) {
1223 h.write(buf, bufferuse);
1224 bufferuse = 0;
1226 serialization::u32b(buf + bufferuse + 0, this->pixels[i].orig);
1227 serialization::u16b(buf + bufferuse + 4, this->pixels[i].origa);
1228 bufferuse += 6;
1230 if(bufferuse > 0) h.write(buf, bufferuse);
1231 L.pushlstring(h.read());
1232 return 1;
1235 template<bool scaled, bool porterduff> int lua_dbitmap::blit(lua::state& L, lua::parameters& P)
1237 uint32_t dx, dy, sx, sy, w, h, hscl, vscl;
1239 P(P.skipped(), dx, dy);
1241 //DBitmap or Bitmap+Palette.
1242 bool src_d = P.is<lua_dbitmap>();
1243 bool src_p = P.is<lua_bitmap>();
1244 int sidx = P.skip();
1245 if(!src_d && !src_p)
1246 P.expected("BITMAP or DBITMAP", sidx);
1247 int spal = 0;
1248 if(src_p)
1249 spal = P.skip(); //Reserve for palette.
1251 P(sx, sy, w, h);
1253 if(scaled)
1254 P(hscl, P.optional2(vscl, hscl));
1256 int64_t ckx = 0x100000000ULL;
1257 porterduff_oper pd_oper;
1258 if(porterduff) {
1259 pd_oper = get_pd_oper(P.arg<std::string>());
1260 } else {
1261 //Hack: Direct-color bitmaps should take color spec, with special NONE value.
1262 if(src_p)
1263 ckx = P.arg_opt<int64_t>(0x10000);
1264 else if(P.is_novalue())
1265 ; //Do nothing.
1266 else
1267 ckx = P.arg<framebuffer::color>().asnumber();
1270 operand_dbitmap dest(*this);
1271 if(src_d) {
1272 operand_dbitmap src(*P.arg<lua_dbitmap*>(sidx));
1273 if(porterduff)
1274 xblit_pduff<scaled>(dest, src, dx, dy, sx, sy, w, h, hscl, vscl, pd_oper);
1275 else
1276 xblit_dir<scaled>(dest, src, ckx, dx, dy, sx, sy, w, h, hscl, vscl);
1277 } else if(src_p) {
1278 operand_bitmap_pal src(*P.arg<lua_bitmap*>(sidx), *P.arg<lua_palette*>(spal));
1279 if(porterduff)
1280 xblit_pduff<scaled>(dest, src, dx, dy, sx, sy, w, h, hscl, vscl, pd_oper);
1281 else
1282 xblit_pal<scaled>(dest, src, ckx, dx, dy, sx, sy, w, h, hscl, vscl);
1284 return 0;
1287 int lua_dbitmap::save_png(lua::state& L, lua::parameters& P)
1289 std::string name, name2;
1290 bool was_filename;
1292 P(P.skipped());
1293 if((was_filename = P.is_string())) P(name);
1294 if(P.is_string()) P(name2);
1296 auto buf = this->save_png();
1297 if(was_filename) {
1298 std::string filename = zip::resolverel(name, name2);
1299 std::ofstream strm(filename, std::ios::binary);
1300 if(!strm)
1301 throw std::runtime_error("Can't open output file");
1302 strm.write(&buf[0], buf.size());
1303 if(!strm)
1304 throw std::runtime_error("Can't write output file");
1305 return 0;
1306 } else {
1307 std::ostringstream strm;
1308 strm.write(&buf[0], buf.size());
1309 L.pushlstring(base64_encode(strm.str()));
1310 return 1;
1314 int lua_dbitmap::adjust_transparency(lua::state& L, lua::parameters& P)
1316 uint16_t tadj;
1318 P(P.skipped(), tadj);
1320 for(size_t i = 0; i < width * height; i++)
1321 pixels[i] = tadjust(pixels[i], tadj);
1322 return 0;
1326 template<bool png> int lua_loaded_bitmap::load(lua::state& L, lua::parameters& P)
1328 std::string name, name2;
1330 P(name, P.optional(name2, ""));
1332 if(png) {
1333 std::string filename = zip::resolverel(name, name2);
1334 return bitmap_load_png_fn(L, filename);
1335 } else
1336 return bitmap_load_fn(L, [&name, &name2]() -> lua_loaded_bitmap {
1337 std::string name3 = zip::resolverel(name, name2);
1338 return lua_loaded_bitmap::load(name3);
1342 template<bool png> int lua_loaded_bitmap::load_str(lua::state& L, lua::parameters& P)
1344 std::string contents;
1346 P(contents);
1348 if(png) {
1349 contents = base64_decode(contents);
1350 std::istringstream strm(contents);
1351 return bitmap_load_png_fn(L, strm);
1352 } else
1353 return bitmap_load_fn(L, [&contents]() -> lua_loaded_bitmap {
1354 std::istringstream strm(contents);
1355 return lua_loaded_bitmap::load(strm);