Lua (d)bitmap: add hflip and vflip functions
[lsnes.git] / src / lua / gui-bitmap.cpp
blobc93c5ea917c2c17c31ec453ba4fed77d4cd11ccc
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/sha256.hpp"
10 #include "library/serialization.hpp"
11 #include "library/string.hpp"
12 #include "library/zip.hpp"
13 #include "lua/bitmap.hpp"
14 #include "library/threads.hpp"
15 #include <vector>
16 #include <sstream>
18 std::vector<char> lua_dbitmap::save_png() const
20 png::encoder img;
21 img.width = width;
22 img.height = height;
23 img.has_palette = false;
24 img.has_alpha = false;
25 img.data.resize(width * height);
26 for(size_t i = 0; i < width * height; i++) {
27 const framebuffer::color& c = pixels[i];
28 if(c.origa != 256)
29 img.has_alpha = true;
30 img.data[i] = c.orig + ((uint32_t)(c.origa - (c.origa >> 7) + (c.origa >> 8)) << 24);
32 std::ostringstream tmp1;
33 img.encode(tmp1);
34 std::string tmp2 = tmp1.str();
35 return std::vector<char>(tmp2.begin(), tmp2.end());
38 std::vector<char> lua_bitmap::save_png(const lua_palette& pal) const
40 png::encoder img;
41 img.width = width;
42 img.height = height;
43 img.has_palette = true;
44 img.has_alpha = false;
45 img.data.resize(width * height);
46 img.palette.resize(pal.color_count);
47 for(size_t i = 0; i < width * height; i++) {
48 img.data[i] = pixels[i];
50 for(size_t i = 0; i < pal.color_count; i++) {
51 const framebuffer::color& c = pal.colors[i];
52 if(c.origa != 256)
53 img.has_alpha = true;
54 img.palette[i] = c.orig + ((uint32_t)(c.origa - (c.origa >> 7) + (c.origa >> 8)) << 24);
56 std::ostringstream tmp1;
57 img.encode(tmp1);
58 std::string tmp2 = tmp1.str();
59 return std::vector<char>(tmp2.begin(), tmp2.end());
62 namespace
64 const char* CONST_base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
66 struct render_object_bitmap : public framebuffer::object
68 render_object_bitmap(int32_t _x, int32_t _y, lua::objpin<lua_bitmap>& _bitmap,
69 lua::objpin<lua_palette>& _palette, int32_t _x0, int32_t _y0, uint32_t _dw, uint32_t _dh,
70 bool _outside) throw()
72 x = _x;
73 y = _y;
74 b = _bitmap;
75 p = _palette;
76 x0 = _x0;
77 y0 = _y0;
78 dw = _dw;
79 dh = _dh;
80 outside = _outside;
83 render_object_bitmap(int32_t _x, int32_t _y, lua::objpin<lua_dbitmap>& _bitmap, int32_t _x0,
84 int32_t _y0, uint32_t _dw, uint32_t _dh, bool _outside) throw()
86 x = _x;
87 y = _y;
88 b2 = _bitmap;
89 x0 = _x0;
90 y0 = _y0;
91 dw = _dw;
92 dh = _dh;
93 outside = _outside;
96 ~render_object_bitmap() throw()
100 bool kill_request(void* obj) throw()
102 return kill_request_ifeq(p.object(), obj) ||
103 kill_request_ifeq(b.object(), obj) ||
104 kill_request_ifeq(b2.object(), obj);
107 template<bool T> void composite_op(struct framebuffer::fb<T>& scr) throw()
109 uint32_t oX = x + scr.get_origin_x() - x0;
110 uint32_t oY = y + scr.get_origin_y() - y0;
111 size_t w, h;
112 if(b) {
113 w = b->width;
114 h = b->height;
115 } else {
116 w = b2->width;
117 h = b2->height;
120 range bX = ((range::make_w(scr.get_width()) - oX) & range::make_w(w) &
121 range::make_s(x0, dw));
122 range bY = ((range::make_w(scr.get_height()) - oY) & range::make_w(h) &
123 range::make_s(y0, dh));
124 range sX = range::make_s(-x + x0, scr.get_last_blit_width());
125 range sY = range::make_s(-y + y0, scr.get_last_blit_height());
127 if(b)
128 lua_bitmap_composite(scr, oX, oY, bX, bY, sX, sY, outside,
129 lua_bitmap_holder<T>(*b, *p));
130 else
131 lua_bitmap_composite(scr, oX, oY, bX, bY, sX, sY, outside,
132 lua_dbitmap_holder<T>(*b2));
134 void operator()(struct framebuffer::fb<false>& x) throw() { composite_op(x); }
135 void operator()(struct framebuffer::fb<true>& x) throw() { composite_op(x); }
136 void clone(framebuffer::queue& q) const throw(std::bad_alloc) { q.clone_helper(this); }
137 private:
138 int32_t x;
139 int32_t y;
140 lua::objpin<lua_bitmap> b;
141 lua::objpin<lua_dbitmap> b2;
142 lua::objpin<lua_palette> p;
143 int32_t x0;
144 int32_t y0;
145 uint32_t dw;
146 uint32_t dh;
147 bool outside;
150 struct operand_dbitmap
152 typedef framebuffer::color pixel_t;
153 typedef framebuffer::color rpixel_t;
154 operand_dbitmap(lua_dbitmap& _bitmap)
155 : bitmap(_bitmap), _transparent(-1)
157 pixels = bitmap.pixels;
159 size_t get_width() { return bitmap.width; }
160 size_t get_height() { return bitmap.height; }
161 const rpixel_t& read(size_t idx) { return pixels[idx]; }
162 const pixel_t& lookup(const rpixel_t& p) { return p; }
163 void write(size_t idx, const pixel_t& v) { pixels[idx] = v; }
164 bool is_opaque(const rpixel_t& p) { return p.origa > 0; }
165 const pixel_t& transparent() { return _transparent; }
166 private:
167 lua_dbitmap& bitmap;
168 pixel_t* pixels;
169 framebuffer::color _transparent;
172 struct operand_bitmap
174 typedef uint16_t pixel_t;
175 typedef uint16_t rpixel_t;
176 operand_bitmap(lua_bitmap& _bitmap)
177 : bitmap(_bitmap)
179 pixels = bitmap.pixels;
181 size_t get_width() { return bitmap.width; }
182 size_t get_height() { return bitmap.height; }
183 const rpixel_t& read(size_t idx) { return pixels[idx]; }
184 const pixel_t& lookup(const rpixel_t& p) { return p; }
185 void write(size_t idx, const pixel_t& v) { pixels[idx] = v; }
186 bool is_opaque(const rpixel_t& p) { return p > 0; }
187 pixel_t transparent() { return 0; }
188 private:
189 lua_bitmap& bitmap;
190 pixel_t* pixels;
193 struct operand_bitmap_pal
195 typedef framebuffer::color pixel_t;
196 typedef uint16_t rpixel_t;
197 operand_bitmap_pal(lua_bitmap& _bitmap, lua_palette& _palette)
198 : bitmap(_bitmap), palette(_palette), _transparent(-1)
200 pixels = bitmap.pixels;
201 limit = palette.color_count;
202 pal = palette.colors;
204 size_t get_width() { return bitmap.width; }
205 size_t get_height() { return bitmap.height; }
206 const rpixel_t& read(size_t idx) { return pixels[idx]; }
207 const pixel_t& lookup(const rpixel_t& p) { return *((p < limit) ? pal + p : &_transparent); }
208 bool is_opaque(const rpixel_t& p) { return p > 0; }
209 const pixel_t& transparent() { return _transparent; }
210 private:
211 lua_bitmap& bitmap;
212 lua_palette& palette;
213 uint16_t* pixels;
214 framebuffer::color* pal;
215 uint32_t limit;
216 framebuffer::color _transparent;
219 struct colorkey_none
221 bool iskey(uint16_t& c) const { return false; }
222 bool iskey(framebuffer::color& c) const { return false; }
225 struct colorkey_direct
227 colorkey_direct(uint64_t _ck)
229 framebuffer::color c(_ck);
230 ck = c.orig;
231 cka = c.origa;
233 bool iskey(framebuffer::color& c) const { return (c.orig == ck && c.origa == cka); }
234 uint32_t ck;
235 uint16_t cka;
238 struct colorkey_palette
240 colorkey_palette(uint64_t _ck) { ck = _ck; }
241 bool iskey(uint16_t& c) const { return (c == ck); }
242 uint16_t ck;
245 template<class _src, class _dest, class colorkey> struct srcdest
247 srcdest(_dest Xdest, _src Xsrc, const colorkey& _ckey)
248 : dest(Xdest), src(Xsrc), ckey(_ckey)
250 swidth = src.get_width();
251 sheight = src.get_height();
252 dwidth = dest.get_width();
253 dheight = dest.get_height();
255 void copy(size_t didx, size_t sidx)
257 typename _src::rpixel_t c = src.read(sidx);
258 if(!ckey.iskey(c))
259 dest.write(didx, src.lookup(c));
261 size_t swidth, sheight, dwidth, dheight;
262 private:
263 _dest dest;
264 _src src;
265 colorkey ckey;
268 template<class _src, class _dest, class colorkey> srcdest<_src, _dest, colorkey> mk_srcdest(_dest dest,
269 _src src, const colorkey& ckey)
271 return srcdest<_src, _dest, colorkey>(dest, src, ckey);
274 struct srcdest_priority
276 srcdest_priority(lua_bitmap& dest, lua_bitmap& src)
278 darray = dest.pixels;
279 sarray = src.pixels;
280 swidth = src.width;
281 sheight = src.height;
282 dwidth = dest.width;
283 dheight = dest.height;
285 void copy(size_t didx, size_t sidx)
287 uint16_t c = sarray[sidx];
288 if(darray[didx] < c)
289 darray[didx] = c;
291 size_t swidth, sheight, dwidth, dheight;
292 private:
293 uint16_t* sarray;
294 uint16_t* darray;
297 enum porterduff_oper
299 PD_SRC,
300 PD_ATOP,
301 PD_OVER,
302 PD_IN,
303 PD_OUT,
304 PD_DEST,
305 PD_DEST_ATOP,
306 PD_DEST_OVER,
307 PD_DEST_IN,
308 PD_DEST_OUT,
309 PD_CLEAR,
310 PD_XOR
313 porterduff_oper get_pd_oper(const std::string& oper)
315 if(oper == "Src") return PD_SRC;
316 if(oper == "Atop") return PD_ATOP;
317 if(oper == "Over") return PD_OVER;
318 if(oper == "In") return PD_IN;
319 if(oper == "Out") return PD_OUT;
320 if(oper == "Dest") return PD_DEST;
321 if(oper == "DestAtop") return PD_DEST_ATOP;
322 if(oper == "DestOver") return PD_DEST_OVER;
323 if(oper == "DestIn") return PD_DEST_IN;
324 if(oper == "DestOut") return PD_DEST_OUT;
325 if(oper == "Clear") return PD_CLEAR;
326 if(oper == "Xor") return PD_XOR;
327 (stringfmt() << "Bad Porter-Duff operator '" << oper << "'").throwex();
328 return PD_SRC; //NOTREACHED
331 template<porterduff_oper oper, class _src, class _dest> struct srcdest_porterduff
333 srcdest_porterduff(_dest Xdest, _src Xsrc)
334 : dest(Xdest), src(Xsrc)
336 swidth = src.get_width();
337 sheight = src.get_height();
338 dwidth = dest.get_width();
339 dheight = dest.get_height();
341 void copy(size_t didx, size_t sidx)
343 typename _dest::rpixel_t vd = dest.read(didx);
344 typename _src::rpixel_t vs = src.read(sidx);
345 bool od = dest.is_opaque(vd);
346 bool os = src.is_opaque(vs);
347 typename _dest::pixel_t ld = dest.lookup(vd);
348 typename _src::pixel_t ls = src.lookup(vs);
349 typename _dest::pixel_t t = dest.transparent();
350 typename _dest::pixel_t r;
351 switch(oper) {
352 case PD_SRC: r = ls; break;
353 case PD_ATOP: r = od ? (os ? ls : ld) : t; break;
354 case PD_OVER: r = os ? ls : ld; break;
355 case PD_IN: r = (od & os) ? ls : t; break;
356 case PD_OUT: r = (!od && os) ? ls : t; break;
357 case PD_DEST: r = ld; break;
358 case PD_DEST_ATOP: r = os ? (od ? ld : ls) : t; break;
359 case PD_DEST_OVER: r = od ? ld : ls; break;
360 case PD_DEST_IN: r = (od & os) ? ld : t; break;
361 case PD_DEST_OUT: r = (od & !os) ? ld : t; break;
362 case PD_CLEAR: r = t; break;
363 case PD_XOR: r = od ? (os ? t : ld) : ls; break;
365 dest.write(didx, r);
367 size_t swidth, sheight, dwidth, dheight;
368 private:
369 _dest dest;
370 _src src;
373 template<porterduff_oper oper, class _src, class _dest> srcdest_porterduff<oper, _src, _dest>
374 mk_porterduff(_dest dest, _src src)
376 return srcdest_porterduff<oper, _src, _dest>(dest, src);
379 template<class srcdest>
380 void xblit_copy(srcdest sd, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h)
382 while((dx + w > sd.dwidth || sx + w > sd.swidth) && w > 0)
383 w--;
384 while((dy + h > sd.dheight || sy + h > sd.sheight) && h > 0)
385 h--;
386 if(dx + w < w || dy + h < h) return; //Don't do overflowing blits.
387 if(sx + w < w || sy + h < h) return; //Don't do overflowing blits.
388 size_t sidx = sy * sd.swidth + sx;
389 size_t didx = dy * sd.dwidth + dx;
390 size_t srskip = sd.swidth - w;
391 size_t drskip = sd.dwidth - w;
392 for(uint32_t j = 0; j < h; j++) {
393 for(uint32_t i = 0; i < w; i++) {
394 sd.copy(didx, sidx);
395 sidx++;
396 didx++;
398 sidx += srskip;
399 didx += drskip;
403 template<class srcdest>
404 void xblit_scaled(srcdest sd, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h,
405 uint32_t hscl, uint32_t vscl)
407 if(!hscl || !vscl) return;
408 w = max(static_cast<uint32_t>(sd.dwidth / hscl), w);
409 h = max(static_cast<uint32_t>(sd.dheight / vscl), h);
410 while((dx + hscl * w > sd.dwidth || sx + w > sd.swidth) && w > 0)
411 w--;
412 while((dy + vscl * h > sd.dheight || sy + h > sd.sheight) && h > 0)
413 h--;
414 if(dx + hscl * w < dx || dy + vscl * h < dy) return; //Don't do overflowing blits.
415 if(sx + w < w || sy + h < h) return; //Don't do overflowing blits.
416 size_t sidx = sy * sd.swidth + sx;
417 size_t didx = dy * sd.dwidth + dx;
418 size_t drskip = sd.dwidth - hscl * w;
419 uint32_t _w = hscl * w;
420 for(uint32_t j = 0; j < vscl * h; j++) {
421 uint32_t _sidx = sidx;
422 for(uint32_t i = 0; i < _w ; i += hscl) {
423 for(uint32_t k = 0; k < hscl; k++)
424 sd.copy(didx + k, _sidx);
425 _sidx++;
426 didx+=hscl;
428 if((j % vscl) == vscl - 1)
429 sidx += sd.swidth;
430 didx += drskip;
434 template<bool scaled, class srcdest>
435 inline void xblit(srcdest sd, uint32_t dx, uint32_t dy, uint32_t sx, uint32_t sy, uint32_t w, uint32_t h,
436 uint32_t hscl, uint32_t vscl)
438 if(scaled)
439 xblit_scaled(sd, dx, dy, sx, sy, w, h, hscl, vscl);
440 else
441 xblit_copy(sd, dx, dy, sx, sy, w, h);
444 template<bool scaled, class src, class dest>
445 inline void xblit_pal(dest _dest, src _src, uint64_t ck, uint32_t dx, uint32_t dy, uint32_t sx,
446 uint32_t sy, uint32_t w, uint32_t h, uint32_t hscl, uint32_t vscl)
448 if(ck > 65535)
449 xblit<scaled>(mk_srcdest(_dest, _src, colorkey_none()), dx, dy, sx, sy, w, h,
450 hscl, vscl);
451 else
452 xblit<scaled>(mk_srcdest(_dest, _src, colorkey_palette(ck)), dx, dy, sx, sy, w, h,
453 hscl, vscl);
456 template<bool scaled, class src, class dest>
457 inline void xblit_dir(dest _dest, src _src, uint64_t ck, uint32_t dx, uint32_t dy, uint32_t sx,
458 uint32_t sy, uint32_t w, uint32_t h, uint32_t hscl, uint32_t vscl)
460 if(ck == 0x100000000ULL)
461 xblit<scaled>(mk_srcdest(_dest, _src, colorkey_none()), dx, dy, sx, sy, w, h,
462 hscl, vscl);
463 else
464 xblit<scaled>(mk_srcdest(_dest, _src, colorkey_direct(ck)), dx, dy, sx, sy, w, h,
465 hscl, vscl);
468 template<bool scaled, porterduff_oper oper, class src, class dest>
469 inline void xblit_pduff2(dest _dest, src _src, uint32_t dx, uint32_t dy, uint32_t sx,
470 uint32_t sy, uint32_t w, uint32_t h, uint32_t hscl, uint32_t vscl)
472 xblit<scaled>(mk_porterduff<oper>(_dest, _src), dx, dy, sx, sy, w, h, hscl, vscl);
475 template<bool scaled, class src, class dest>
476 inline void xblit_pduff(dest _dest, src _src, uint32_t dx, uint32_t dy, uint32_t sx,
477 uint32_t sy, uint32_t w, uint32_t h, uint32_t hscl, uint32_t vscl, porterduff_oper oper)
479 switch(oper) {
480 case PD_ATOP:
481 xblit_pduff2<scaled, PD_ATOP>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
482 break;
483 case PD_CLEAR:
484 xblit_pduff2<scaled, PD_CLEAR>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
485 break;
486 case PD_DEST:
487 xblit_pduff2<scaled, PD_DEST>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
488 break;
489 case PD_DEST_ATOP:
490 xblit_pduff2<scaled, PD_DEST_ATOP>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
491 break;
492 case PD_DEST_IN:
493 xblit_pduff2<scaled, PD_DEST_IN>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
494 break;
495 case PD_DEST_OUT:
496 xblit_pduff2<scaled, PD_DEST_OUT>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
497 break;
498 case PD_DEST_OVER:
499 xblit_pduff2<scaled, PD_DEST_OVER>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
500 break;
501 case PD_IN:
502 xblit_pduff2<scaled, PD_IN>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
503 break;
504 case PD_OUT:
505 xblit_pduff2<scaled, PD_OUT>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
506 break;
507 case PD_OVER:
508 xblit_pduff2<scaled, PD_OVER>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
509 break;
510 case PD_SRC:
511 xblit_pduff2<scaled, PD_SRC>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
512 break;
513 case PD_XOR:
514 xblit_pduff2<scaled, PD_XOR>(_dest, _src, dx, dy, sx, sy, w, h, hscl, vscl);
515 break;
519 int bitmap_load_fn(lua::state& L, std::function<lua_loaded_bitmap()> src)
521 auto bitmap = src();
522 if(bitmap.d) {
523 lua_dbitmap* b = lua::_class<lua_dbitmap>::create(L, bitmap.w, bitmap.h);
524 for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
525 b->pixels[i] = framebuffer::color(bitmap.bitmap[i]);
526 return 1;
527 } else {
528 lua_bitmap* b = lua::_class<lua_bitmap>::create(L, bitmap.w, bitmap.h);
529 lua_palette* p = lua::_class<lua_palette>::create(L);
530 for(size_t i = 0; i < bitmap.w * bitmap.h; i++)
531 b->pixels[i] = bitmap.bitmap[i];
532 p->adjust_palette_size(bitmap.palette.size());
533 for(size_t i = 0; i < bitmap.palette.size(); i++)
534 p->colors[i] = framebuffer::color(bitmap.palette[i]);
535 return 2;
539 //Similar as _sample_texture(), but for the s=2^n, n integer special case.
540 template<typename pixel>
541 inline void _sample_texture_log2(pixel* dest, uint32_t dwidth, uint32_t dheight, pixel* source,
542 uint32_t swidth, uint32_t sheight, int32_t xx, int32_t xy, int32_t x0, int32_t yx, int32_t yy,
543 int32_t y0, int32_t log2s, bool wrap, pixel transparent)
545 uint32_t th = (1 << log2s) >> 1; //Two shifts, as log2s=0 should give th=0.
546 uint32_t fsmask = (1u << log2s) - 1;
547 uint64_t bias = 1ull << (31 + log2s);
548 size_t didx = 0;
549 for(int32_t i = 0; i < (int32_t)dheight; i++) {
550 uint64_t ssx = (int64_t)xy * i + (int64_t)x0 + bias;
551 uint64_t ssy = (int64_t)yy * i + (int64_t)y0 + bias;
552 for(int32_t j = 0; j < (int32_t)dwidth; j++) {
553 //Compute whole and fractional parts.
554 uint32_t _isx = ssx >> log2s;
555 uint32_t fsx = ssx & fsmask;
556 uint32_t _isy = ssy >> log2s;
557 uint32_t fsy = ssy & fsmask;
558 //The above always give positive fractional parts.
559 //Round the whole parts.
560 _isx += fsx > th;
561 _isy += fsy > th;
562 //Remove biases.
563 int32_t isx = _isx - (1u << 31);
564 int32_t isy = _isy - (1u << 31);
565 //Compute color.
566 pixel p = transparent;
567 if(wrap) {
568 isx = isx % swidth;
569 if(isx < 0) isx += swidth;
570 isy = isy % sheight;
571 if(isy < 0) isy += sheight;
572 p = source[isy * swidth + isx];
573 } else if(isx >= 0 && isx < (int32_t)swidth && isy >= 0 && isy < (int32_t)sheight) {
574 p = source[isy * swidth + isx];
576 dest[didx] = p;
577 //Update values for next pixel.
578 ssx += (int64_t)xx;
579 ssy += (int64_t)yx;
580 didx++;
585 template<typename pixel>
586 inline void _sample_texture(pixel* dest, uint32_t dwidth, uint32_t dheight, pixel* source, uint32_t swidth,
587 uint32_t sheight, int32_t xx, int32_t xy, int32_t x0, int32_t yx, int32_t yy, int32_t y0, uint32_t s,
588 bool wrap, pixel transparent)
590 if((s & (s - 1)) == 0) {
591 //S is power of two. Compute the logarithm.
592 uint32_t log2s = 0;
593 while(s > 1) {
594 log2s++;
595 s >>= 1;
597 return _sample_texture_log2(dest, dwidth, dheight, source, swidth, sheight, xx, xy, x0, yx,
598 yy, y0, log2s, wrap, transparent);
600 int32_t th = s / 2;
601 size_t didx = 0;
602 for(int32_t i = 0; i < (int32_t)dheight; i++) {
603 int64_t ssx = (int64_t)xy * i + (int64_t)x0;
604 int64_t ssy = (int64_t)yy * i + (int64_t)y0;
605 for(int32_t j = 0; j < (int32_t)dwidth; j++) {
606 //Compute whole and fractional parts.
607 int64_t isx = ssx / (int32_t)s;
608 int32_t fsx = ssx % (int32_t)s;
609 int64_t isy = ssy / (int32_t)s;
610 int32_t fsy = ssy % (int32_t)s;
611 //Make the fractional parts always positive.
612 if(fsx < 0) { fsx += s; isx--; }
613 if(fsy < 0) { fsy += s; isy--; }
614 //Round the whole parts.
615 isx += fsx > th;
616 isy += fsy > th;
617 //Compute color.
618 pixel p = transparent;
619 if(wrap) {
620 isx = isx % swidth;
621 if(isx < 0) isx += swidth;
622 isy = isy % sheight;
623 if(isy < 0) isy += sheight;
624 p = source[isy * swidth + isx];
625 } else if(isx >= 0 && isx < (int32_t)swidth && isy >= 0 && isy < (int32_t)sheight) {
626 p = source[isy * swidth + isx];
628 dest[didx] = p;
629 //Update values for next pixel.
630 ssx += (int64_t)xx;
631 ssy += (int64_t)yx;
632 didx++;
637 template<typename pixel>
638 inline int _hflip(pixel* pixels, uint32_t width, uint32_t height)
640 uint32_t w = width, h = height;
642 for (uint32_t x = 0; x < w/2; x++) {
643 for (uint32_t y = 0; y < h; y++) {
644 std::swap(pixels[y * w + x], pixels[(y + 1) * w - x - 1]);
647 return 0;
650 template<typename pixel>
651 inline int _vflip(pixel* pixels, uint32_t width, uint32_t height)
653 uint32_t w = width, h = height;
655 for (uint32_t x = 0; x < w; x++) {
656 for (uint32_t y = 0; y < h/2; y++) {
657 std::swap(pixels[y * w + x], pixels[(h - y - 1) * w + x]);
660 return 0;
664 inline int64_t mangle_color(uint32_t c)
666 if(c < 0x1000000)
667 return -1;
668 else
669 return ((256 - (c >> 24) - (c >> 31)) << 24) | (c & 0xFFFFFF);
672 int base64val(char ch)
674 if(ch >= 'A' && ch <= 'Z')
675 return ch - 65;
676 if(ch >= 'a' && ch <= 'z')
677 return ch - 97 + 26;
678 if(ch >= '0' && ch <= '9')
679 return ch - 48 + 52;
680 if(ch == '+')
681 return 62;
682 if(ch == '/')
683 return 63;
684 if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
685 return -1;
686 if(ch == '=')
687 return -2;
688 return -3;
691 std::string base64_encode(const std::string& str)
693 std::ostringstream x;
694 unsigned pos = 0;
695 uint32_t mem = 0;
696 for(auto i : str) {
697 mem = (mem << 8) + (unsigned char)i;
698 if(++pos == 3) {
699 uint8_t c1 = (mem >> 18) & 0x3F;
700 uint8_t c2 = (mem >> 12) & 0x3F;
701 uint8_t c3 = (mem >> 6) & 0x3F;
702 uint8_t c4 = mem & 0x3F;
703 x << CONST_base64chars[c1];
704 x << CONST_base64chars[c2];
705 x << CONST_base64chars[c3];
706 x << CONST_base64chars[c4];
707 mem = 0;
708 pos = 0;
711 if(pos == 2) {
712 uint8_t c1 = (mem >> 10) & 0x3F;
713 uint8_t c2 = (mem >> 4) & 0x3F;
714 uint8_t c3 = (mem << 2) & 0x3F;
715 x << CONST_base64chars[c1];
716 x << CONST_base64chars[c2];
717 x << CONST_base64chars[c3];
718 x << "=";
720 if(pos == 1) {
721 uint8_t c1 = (mem >> 2) & 0x3F;
722 uint8_t c2 = (mem << 4) & 0x3F;
723 x << CONST_base64chars[c1];
724 x << CONST_base64chars[c2];
725 x << "==";
727 return x.str();
730 std::string base64_decode(const std::string& str)
732 bool end = 0;
733 uint32_t memory = 0;
734 uint32_t memsize = 1;
735 int posmod = 0;
736 std::ostringstream x;
737 for(auto i : str) {
738 int v = base64val(i);
739 if(v == -1)
740 continue;
741 posmod = (posmod + 1) & 3;
742 if(v == -2 && (posmod == 1 || posmod == 2))
743 throw std::runtime_error("Invalid Base64");
744 if(v == -2) {
745 end = true;
746 continue;
748 if(v == -3 || end)
749 throw std::runtime_error("Invalid Base64");
750 memory = memory * 64 + v;
751 memsize = memsize * 64;
752 if(memsize >= 256) {
753 memsize >>= 8;
754 x << static_cast<uint8_t>(memory / memsize);
755 memory %= memsize;
758 return x.str();
761 template<typename T>
762 int bitmap_load_png_fn(lua::state& L, T& src)
764 png::decoder img(src);
765 if(img.has_palette) {
766 lua_bitmap* b = lua::_class<lua_bitmap>::create(L, img.width, img.height);
767 lua_palette* p = lua::_class<lua_palette>::create(L);
768 for(size_t i = 0; i < img.width * img.height; i++)
769 b->pixels[i] = img.data[i];
770 p->adjust_palette_size(img.palette.size());
771 for(size_t i = 0; i < img.palette.size(); i++)
772 p->colors[i] = framebuffer::color(mangle_color(img.palette[i]));
773 return 2;
774 } else {
775 lua_dbitmap* b = lua::_class<lua_dbitmap>::create(L, img.width, img.height);
776 for(size_t i = 0; i < img.width * img.height; i++)
777 b->pixels[i] = framebuffer::color(mangle_color(img.data[i]));
778 return 1;
782 int bitmap_palette_fn(lua::state& L, std::istream& s)
784 lua_palette* p = lua::_class<lua_palette>::create(L);
785 while(s) {
786 std::string line;
787 std::getline(s, line);
788 istrip_CR(line);
789 regex_results r;
790 if(!regex_match("[ \t]*(#.*)?", line)) {
791 //Nothing.
792 } else if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
793 int64_t cr, cg, cb, ca;
794 cr = parse_value<uint8_t>(r[1]);
795 cg = parse_value<uint8_t>(r[2]);
796 cb = parse_value<uint8_t>(r[3]);
797 ca = 256 - parse_value<uint16_t>(r[4]);
798 if(ca == 256)
799 p->push_back(framebuffer::color(-1));
800 else
801 p->push_back(framebuffer::color((ca << 24) | (cr << 16)
802 | (cg << 8) | cb));
803 } else if(r = regex("[ \t]*([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]*", line)) {
804 int64_t cr, cg, cb;
805 cr = parse_value<uint8_t>(r[1]);
806 cg = parse_value<uint8_t>(r[2]);
807 cb = parse_value<uint8_t>(r[3]);
808 p->push_back(framebuffer::color((cr << 16) | (cg << 8) | cb));
809 } else if(r = regex("[ \t]*([^ \t]|[^ \t].*[^ \t])[ \t]*", line)) {
810 p->push_back(framebuffer::color(r[1]));
811 } else
812 throw std::runtime_error("Invalid line format (" + line + ")");
814 return 1;
817 inline framebuffer::color tadjust(framebuffer::color c, uint16_t adj)
819 uint32_t rgb = c.orig;
820 uint32_t a = c.origa;
821 a = (a * adj) >> 8;
822 if(a > 256)
823 a = 256;
824 if(a == 0)
825 return framebuffer::color(-1);
826 else
827 return framebuffer::color(rgb | ((uint32_t)(256 - a) << 24));
830 lua::_class<lua_loaded_bitmap> LUA_class_loaded_bitmap(lua_class_gui, "IMAGELOADER", {
831 {"load", lua_loaded_bitmap::load<false>},
832 {"load_str", lua_loaded_bitmap::load_str<false>},
833 {"load_png", lua_loaded_bitmap::load<true>},
834 {"load_png_str", lua_loaded_bitmap::load_str<true>},
837 lua::_class<lua_palette> LUA_class_palette(lua_class_gui, "PALETTE", {
838 {"new", lua_palette::create},
839 {"load", lua_palette::load},
840 {"load_str", lua_palette::load_str},
841 }, {
842 {"set", &lua_palette::set},
843 {"get", &lua_palette::get},
844 {"hash", &lua_palette::hash},
845 {"debug", &lua_palette::debug},
846 {"adjust_transparency", &lua_palette::adjust_transparency},
847 }, &lua_palette::print);
849 lua::_class<lua_bitmap> LUA_class_bitmap(lua_class_gui, "BITMAP", {
850 {"new", lua_bitmap::create},
851 }, {
852 {"draw", &lua_bitmap::draw<false, false>},
853 {"draw_clip", &lua_bitmap::draw<false, true>},
854 {"draw_outside", &lua_bitmap::draw<true, false>},
855 {"draw_clip_outside", &lua_bitmap::draw<true, true>},
856 {"pset", &lua_bitmap::pset},
857 {"pget", &lua_bitmap::pget},
858 {"size", &lua_bitmap::size},
859 {"hflip", &lua_bitmap::hflip},
860 {"vflip", &lua_bitmap::vflip},
861 {"hash", &lua_bitmap::hash},
862 {"blit", &lua_bitmap::blit<false, false>},
863 {"blit_priority", &lua_bitmap::blit_priority<false>},
864 {"blit_scaled", &lua_bitmap::blit<true, false>},
865 {"blit_scaled_priority", &lua_bitmap::blit_priority<true>},
866 {"blit_porterduff", &lua_bitmap::blit<false, true>},
867 {"blit_scaled_porterduff", &lua_bitmap::blit<true, true>},
868 {"save_png", &lua_bitmap::save_png},
869 {"sample_texture", &lua_bitmap::sample_texture},
870 }, &lua_bitmap::print);
872 lua::_class<lua_dbitmap> LUA_class_dbitmap(lua_class_gui, "DBITMAP", {
873 {"new", lua_dbitmap::create},
874 }, {
875 {"draw", &lua_dbitmap::draw<false, false>},
876 {"draw_clip", &lua_dbitmap::draw<false, true>},
877 {"draw_outside", &lua_dbitmap::draw<true, false>},
878 {"draw_clip_outside", &lua_dbitmap::draw<true, true>},
879 {"pset", &lua_dbitmap::pset},
880 {"pget", &lua_dbitmap::pget},
881 {"size", &lua_dbitmap::size},
882 {"hflip", &lua_dbitmap::hflip},
883 {"vflip", &lua_dbitmap::vflip},
884 {"hash", &lua_dbitmap::hash},
885 {"blit", &lua_dbitmap::blit<false, false>},
886 {"blit_scaled", &lua_dbitmap::blit<true, false>},
887 {"blit_porterduff", &lua_dbitmap::blit<false, true>},
888 {"blit_scaled_porterduff", &lua_dbitmap::blit<true, true>},
889 {"save_png", &lua_dbitmap::save_png},
890 {"adjust_transparency", &lua_dbitmap::adjust_transparency},
891 {"sample_texture", &lua_dbitmap::sample_texture},
892 }, &lua_dbitmap::print);
895 /** Palette **/
896 lua_palette::lua_palette(lua::state& L)
898 color_count = 0;
899 scolors = colors = lua::align_overcommit<lua_palette, framebuffer::color>(this);
900 for(unsigned i = 0; i < reserved_colors; i++)
901 scolors[i] = framebuffer::color(-1);
904 lua_palette::~lua_palette()
906 CORE().fbuf->render_kill_request(this);
909 std::string lua_palette::print()
911 size_t s = color_count;
912 return (stringfmt() << s << " " << ((s != 1) ? "colors" : "color")).str();
915 int lua_palette::create(lua::state& L, lua::parameters& P)
917 lua::_class<lua_palette>::create(L);
918 return 1;
921 int lua_palette::load(lua::state& L, lua::parameters& P)
923 std::string name, name2;
925 P(name, P.optional(name2, ""));
927 std::istream& s = zip::openrel(name, name2);
928 try {
929 int r = bitmap_palette_fn(L, s);
930 delete &s;
931 return r;
932 } catch(...) {
933 delete &s;
934 throw;
938 int lua_palette::load_str(lua::state& L, lua::parameters& P)
940 std::string content;
942 P(content);
944 std::istringstream s(content);
945 return bitmap_palette_fn(L, s);
948 int lua_palette::set(lua::state& L, lua::parameters& P)
950 framebuffer::color nc;
951 uint16_t c;
953 P(P.skipped(), c, nc);
955 if(this->color_count <= c) {
956 this->adjust_palette_size(static_cast<uint32_t>(c) + 1);
958 this->colors[c] = nc;
959 return 0;
962 int lua_palette::get(lua::state& L, lua::parameters& P)
964 framebuffer::color nc;
965 uint16_t c;
966 int64_t _nc;
968 P(P.skipped(), c);
970 if(this->color_count <= c) {
971 return 0;
973 nc = this->colors[c];
974 uint32_t rgb = nc.orig;
975 uint32_t a = nc.origa;
976 if(a == 0)
977 _nc = -1;
978 else
979 _nc = ((256 - a) << 24) | rgb;
980 L.pushnumber(_nc);
981 return 1;
984 int lua_palette::hash(lua::state& L, lua::parameters& P)
986 sha256 h;
987 const int buffersize = 256;
988 int bufferuse = 0;
989 char buf[buffersize];
990 unsigned realsize = 0;
991 for(unsigned i = 0; i < this->color_count; i++)
992 if(this->colors[i].origa) realsize = i + 1;
993 for(unsigned i = 0; i < realsize; i++) {
994 if(bufferuse + 6 > buffersize) {
995 h.write(buf, bufferuse);
996 bufferuse = 0;
998 serialization::u32b(buf + bufferuse + 0, this->colors[i].orig);
999 serialization::u16b(buf + bufferuse + 4, this->colors[i].origa);
1000 bufferuse += 6;
1002 if(bufferuse > 0) h.write(buf, bufferuse);
1003 L.pushlstring(h.read());
1004 return 1;
1007 int lua_palette::debug(lua::state& L, lua::parameters& P)
1009 for(size_t i = 0; i < color_count; i++)
1010 messages << "Color #" << i << ": " << colors[i].orig << ":" << colors[i].origa << std::endl;
1011 return 0;
1014 int lua_palette::adjust_transparency(lua::state& L, lua::parameters& P)
1016 uint16_t tadj;
1018 P(P.skipped(), tadj);
1020 for(size_t i = 0; i < color_count; i++)
1021 colors[i] = tadjust(colors[i], tadj);
1022 return 0;
1025 void lua_palette::adjust_palette_size(size_t newsize)
1027 threads::alock h(palette_mutex);
1028 if(newsize > reserved_colors) {
1029 lcolors.resize(newsize);
1030 if(color_count <= reserved_colors) {
1031 for(unsigned i = 0; i < color_count; i++)
1032 lcolors[i] = colors[i];
1034 colors = &lcolors[0];
1035 } else {
1036 if(color_count > reserved_colors) {
1037 for(unsigned i = 0; i < color_count; i++)
1038 scolors[i] = lcolors[i];
1040 colors = scolors;
1042 color_count = newsize;
1045 void lua_palette::push_back(const framebuffer::color& cx)
1047 size_t c = color_count;
1048 adjust_palette_size(c + 1);
1049 colors[c] = cx;
1052 /** BITMAP **/
1053 lua_bitmap::lua_bitmap(lua::state& L, uint32_t w, uint32_t h)
1055 if(h > 0 && overcommit(w, h) / h / sizeof(uint16_t) < w) throw std::bad_alloc();
1056 width = w;
1057 height = h;
1058 pixels = lua::align_overcommit<lua_bitmap, uint16_t>(this);
1059 memset(pixels, 0, 2 * width * height);
1062 lua_bitmap::~lua_bitmap()
1064 CORE().fbuf->render_kill_request(this);
1067 std::string lua_bitmap::print()
1069 return (stringfmt() << width << "*" << height).str();
1072 int lua_bitmap::create(lua::state& L, lua::parameters& P)
1074 uint32_t w, h;
1075 uint16_t c;
1077 P(w, h, P.optional(c, 0));
1079 lua_bitmap* b = lua::_class<lua_bitmap>::create(L, w, h);
1080 for(size_t i = 0; i < b->width * b->height; i++)
1081 b->pixels[i] = c;
1082 return 1;
1085 template<bool outside, bool clip>
1086 int lua_bitmap::draw(lua::state& L, lua::parameters& P)
1088 auto& core = CORE();
1089 int32_t x, y;
1090 lua::objpin<lua_bitmap> b;
1091 lua::objpin<lua_palette> p;
1093 if(!core.lua2->render_ctx) return 0;
1095 P(b, x, y, p);
1097 int32_t x0 = 0, y0 = 0, dw = b->width, dh = b->height;
1098 if(clip) P(x0, y0, dw, dh);
1100 core.lua2->render_ctx->queue->create_add<render_object_bitmap>(x, y, b, p, x0, y0, dw, dh, outside);
1101 return 0;
1104 int lua_bitmap::pset(lua::state& L, lua::parameters& P)
1106 uint32_t x, y;
1107 uint16_t c;
1109 P(P.skipped(), x, y, c);
1111 if(x >= this->width || y >= this->height)
1112 return 0;
1113 this->pixels[y * this->width + x] = c;
1114 return 0;
1117 int lua_bitmap::pget(lua::state& L, lua::parameters& P)
1119 uint32_t x, y;
1121 P(P.skipped(), x, y);
1123 if(x >= this->width || y >= this->height)
1124 return 0;
1125 L.pushnumber(this->pixels[y * this->width + x]);
1126 return 1;
1129 int lua_bitmap::size(lua::state& L, lua::parameters& P)
1131 L.pushnumber(this->width);
1132 L.pushnumber(this->height);
1133 return 2;
1136 int lua_bitmap::hflip(lua::state& L, lua::parameters& P)
1138 return _hflip(pixels, width, height);
1141 int lua_bitmap::vflip(lua::state& L, lua::parameters& P)
1143 return _vflip(pixels, width, height);
1146 int lua_bitmap::hash(lua::state& L, lua::parameters& P)
1148 sha256 h;
1149 const int buffersize = 256;
1150 int bufferuse = 0;
1151 char buf[buffersize];
1152 memset(buf, 0, buffersize);
1153 serialization::u64b(buf + 0, this->width);
1154 serialization::u64b(buf + 8, this->height);
1155 bufferuse = 16;
1156 for(unsigned i = 0; i < this->width * this->height; i++) {
1157 if(bufferuse + 2 > buffersize) {
1158 h.write(buf, bufferuse);
1159 bufferuse = 0;
1161 serialization::u16b(buf + bufferuse + 0, this->pixels[i]);
1162 bufferuse += 2;
1164 if(bufferuse > 0) h.write(buf, bufferuse);
1165 L.pushlstring(h.read());
1166 return 1;
1169 template<bool scaled, bool porterduff> int lua_bitmap::blit(lua::state& L, lua::parameters& P)
1171 uint32_t dx, dy, sx, sy, w, h, hscl, vscl;
1172 lua_bitmap* src_p;
1174 P(P.skipped(), dx, dy, src_p, sx, sy, w, h);
1175 if(scaled)
1176 P(hscl, P.optional2(vscl, hscl));
1178 if(porterduff) {
1179 porterduff_oper pd_oper = get_pd_oper(P.arg<std::string>());
1180 xblit_pduff<scaled>(operand_bitmap(*this), operand_bitmap(*src_p), dx, dy, sx, sy, w, h, hscl, vscl,
1181 pd_oper);
1182 } else {
1183 int64_t ck = P.arg_opt<uint64_t>(65536);
1184 xblit_pal<scaled>(operand_bitmap(*this), operand_bitmap(*src_p), ck, dx, dy, sx, sy, w, h,
1185 hscl, vscl);
1187 return 0;
1190 template<bool scaled> int lua_bitmap::blit_priority(lua::state& L, lua::parameters& P)
1192 uint32_t dx, dy, sx, sy, w, h, hscl, vscl;
1193 lua_bitmap* src_p;
1195 P(P.skipped(), dx, dy, src_p, sx, sy, w, h);
1196 if(scaled)
1197 P(hscl, P.optional2(vscl, hscl));
1199 xblit<scaled>(srcdest_priority(*this, *src_p), dx, dy, sx, sy, w, h, hscl, vscl);
1200 return 0;
1203 int lua_bitmap::save_png(lua::state& L, lua::parameters& P)
1205 std::string name, name2;
1206 lua_palette* p;
1207 bool was_filename;
1209 P(P.skipped());
1210 if((was_filename = P.is_string())) P(name);
1211 if(P.is_string()) P(name2);
1212 P(p);
1214 auto buf = this->save_png(*p);
1215 if(was_filename) {
1216 std::string filename = zip::resolverel(name, name2);
1217 std::ofstream strm(filename, std::ios::binary);
1218 if(!strm)
1219 throw std::runtime_error("Can't open output file");
1220 strm.write(&buf[0], buf.size());
1221 if(!strm)
1222 throw std::runtime_error("Can't write output file");
1223 return 0;
1224 } else {
1225 std::ostringstream strm;
1226 strm.write(&buf[0], buf.size());
1227 L.pushlstring(base64_encode(strm.str()));
1228 return 1;
1232 int lua_bitmap::sample_texture(lua::state& L, lua::parameters& P)
1234 lua_bitmap* src_p;
1235 int32_t xx, xy, yx, yy, x0, y0;
1236 uint32_t s;
1237 bool wrap;
1238 uint16_t trans = 0;
1240 P(P.skipped(), src_p, xx, xy, x0, yx, yy, y0, s, wrap);
1242 _sample_texture(pixels, width, height, src_p->pixels, src_p->width, src_p->height, xx, xy, x0, yx, yy, y0,
1243 s, wrap, trans);
1244 return 0;
1247 /** DBITMAP **/
1248 lua_dbitmap::lua_dbitmap(lua::state& L, uint32_t w, uint32_t h)
1250 if(h > 0 && overcommit(w, h) / h / sizeof(framebuffer::color) < w) throw std::bad_alloc();
1251 width = w;
1252 height = h;
1253 pixels = lua::align_overcommit<lua_dbitmap, framebuffer::color>(this);
1254 //Initialize the bitmap data.
1255 framebuffer::color transparent(-1);
1256 for(size_t i = 0; i < (size_t)width * height; i++)
1257 new(pixels + i) framebuffer::color(transparent);
1260 lua_dbitmap::~lua_dbitmap()
1262 CORE().fbuf->render_kill_request(this);
1265 std::string lua_dbitmap::print()
1267 return (stringfmt() << width << "*" << height).str();
1270 int lua_dbitmap::create(lua::state& L, lua::parameters& P)
1272 uint32_t w, h;
1273 framebuffer::color c;
1275 P(w, h, P.optional(c, -1));
1277 lua_dbitmap* b = lua::_class<lua_dbitmap>::create(L, w, h);
1278 for(size_t i = 0; i < b->width * b->height; i++)
1279 b->pixels[i] = c;
1280 return 1;
1283 template<bool outside, bool clip>
1284 int lua_dbitmap::draw(lua::state& L, lua::parameters& P)
1286 auto& core = CORE();
1287 int32_t x, y;
1288 lua::objpin<lua_dbitmap> b;
1290 if(!core.lua2->render_ctx) return 0;
1292 P(b, x, y);
1294 int32_t x0 = 0, y0 = 0, dw = b->width, dh = b->height;
1295 if(clip) P(x0, y0, dw, dh);
1297 core.lua2->render_ctx->queue->create_add<render_object_bitmap>(x, y, b, x0, y0, dw, dh, outside);
1298 return 0;
1301 int lua_dbitmap::pset(lua::state& L, lua::parameters& P)
1303 uint32_t x, y;
1304 framebuffer::color c;
1306 P(P.skipped(), x, y, c);
1308 if(x >= this->width || y >= this->height)
1309 return 0;
1310 this->pixels[y * this->width + x] = c;
1311 return 0;
1314 int lua_dbitmap::pget(lua::state& L, lua::parameters& P)
1316 uint32_t x, y;
1318 P(P.skipped(), x, y);
1320 if(x >= this->width || y >= this->height)
1321 return 0;
1322 L.pushnumber((this->pixels[y * this->width + x]).asnumber());
1323 return 1;
1326 int lua_dbitmap::size(lua::state& L, lua::parameters& P)
1328 L.pushnumber(this->width);
1329 L.pushnumber(this->height);
1330 return 2;
1333 int lua_dbitmap::hflip(lua::state& L, lua::parameters& P)
1335 return _hflip(pixels, width, height);
1338 int lua_dbitmap::vflip(lua::state& L, lua::parameters& P)
1340 return _vflip(pixels, width, height);
1343 int lua_dbitmap::hash(lua::state& L, lua::parameters& P)
1345 sha256 h;
1346 const int buffersize = 256;
1347 int bufferuse = 0;
1348 char buf[buffersize];
1349 memset(buf, 0, buffersize);
1350 serialization::u64b(buf + 0, this->width);
1351 serialization::u64b(buf + 4, this->height);
1352 bufferuse = 16;
1353 for(unsigned i = 0; i < this->width * this->height; i++) {
1354 if(bufferuse + 6 > buffersize) {
1355 h.write(buf, bufferuse);
1356 bufferuse = 0;
1358 serialization::u32b(buf + bufferuse + 0, this->pixels[i].orig);
1359 serialization::u16b(buf + bufferuse + 4, this->pixels[i].origa);
1360 bufferuse += 6;
1362 if(bufferuse > 0) h.write(buf, bufferuse);
1363 L.pushlstring(h.read());
1364 return 1;
1367 template<bool scaled, bool porterduff> int lua_dbitmap::blit(lua::state& L, lua::parameters& P)
1369 uint32_t dx, dy, sx, sy, w, h, hscl, vscl;
1371 P(P.skipped(), dx, dy);
1373 //DBitmap or Bitmap+Palette.
1374 bool src_d = P.is<lua_dbitmap>();
1375 bool src_p = P.is<lua_bitmap>();
1376 int sidx = P.skip();
1377 if(!src_d && !src_p)
1378 P.expected("BITMAP or DBITMAP", sidx);
1379 int spal = 0;
1380 if(src_p)
1381 spal = P.skip(); //Reserve for palette.
1383 P(sx, sy, w, h);
1385 if(scaled)
1386 P(hscl, P.optional2(vscl, hscl));
1388 int64_t ckx = 0x100000000ULL;
1389 porterduff_oper pd_oper;
1390 if(porterduff) {
1391 pd_oper = get_pd_oper(P.arg<std::string>());
1392 } else {
1393 //Hack: Direct-color bitmaps should take color spec, with special NONE value.
1394 if(src_p)
1395 ckx = P.arg_opt<int64_t>(0x10000);
1396 else if(P.is_novalue())
1397 ; //Do nothing.
1398 else
1399 ckx = P.arg<framebuffer::color>().asnumber();
1402 operand_dbitmap dest(*this);
1403 if(src_d) {
1404 operand_dbitmap src(*P.arg<lua_dbitmap*>(sidx));
1405 if(porterduff)
1406 xblit_pduff<scaled>(dest, src, dx, dy, sx, sy, w, h, hscl, vscl, pd_oper);
1407 else
1408 xblit_dir<scaled>(dest, src, ckx, dx, dy, sx, sy, w, h, hscl, vscl);
1409 } else if(src_p) {
1410 operand_bitmap_pal src(*P.arg<lua_bitmap*>(sidx), *P.arg<lua_palette*>(spal));
1411 if(porterduff)
1412 xblit_pduff<scaled>(dest, src, dx, dy, sx, sy, w, h, hscl, vscl, pd_oper);
1413 else
1414 xblit_pal<scaled>(dest, src, ckx, dx, dy, sx, sy, w, h, hscl, vscl);
1416 return 0;
1419 int lua_dbitmap::save_png(lua::state& L, lua::parameters& P)
1421 std::string name, name2;
1422 bool was_filename;
1424 P(P.skipped());
1425 if((was_filename = P.is_string())) P(name);
1426 if(P.is_string()) P(name2);
1428 auto buf = this->save_png();
1429 if(was_filename) {
1430 std::string filename = zip::resolverel(name, name2);
1431 std::ofstream strm(filename, std::ios::binary);
1432 if(!strm)
1433 throw std::runtime_error("Can't open output file");
1434 strm.write(&buf[0], buf.size());
1435 if(!strm)
1436 throw std::runtime_error("Can't write output file");
1437 return 0;
1438 } else {
1439 std::ostringstream strm;
1440 strm.write(&buf[0], buf.size());
1441 L.pushlstring(base64_encode(strm.str()));
1442 return 1;
1446 int lua_dbitmap::adjust_transparency(lua::state& L, lua::parameters& P)
1448 uint16_t tadj;
1450 P(P.skipped(), tadj);
1452 for(size_t i = 0; i < width * height; i++)
1453 pixels[i] = tadjust(pixels[i], tadj);
1454 return 0;
1457 int lua_dbitmap::sample_texture(lua::state& L, lua::parameters& P)
1459 lua_dbitmap* src_p;
1460 int32_t xx, xy, yx, yy, x0, y0;
1461 uint32_t s;
1462 bool wrap;
1464 P(P.skipped(), src_p, xx, xy, x0, yx, yy, y0, s, wrap);
1466 _sample_texture(pixels, width, height, src_p->pixels, src_p->width, src_p->height, xx, xy, x0, yx, yy, y0,
1467 s, wrap, framebuffer::color());
1468 return 0;
1471 template<bool png> int lua_loaded_bitmap::load(lua::state& L, lua::parameters& P)
1473 std::string name, name2;
1475 P(name, P.optional(name2, ""));
1477 if(png) {
1478 std::string filename = zip::resolverel(name, name2);
1479 return bitmap_load_png_fn(L, filename);
1480 } else
1481 return bitmap_load_fn(L, [&name, &name2]() -> lua_loaded_bitmap {
1482 std::string name3 = zip::resolverel(name, name2);
1483 return lua_loaded_bitmap::load(name3);
1487 template<bool png> int lua_loaded_bitmap::load_str(lua::state& L, lua::parameters& P)
1489 std::string contents;
1491 P(contents);
1493 if(png) {
1494 contents = base64_decode(contents);
1495 std::istringstream strm(contents);
1496 return bitmap_load_png_fn(L, strm);
1497 } else
1498 return bitmap_load_fn(L, [&contents]() -> lua_loaded_bitmap {
1499 std::istringstream strm(contents);
1500 return lua_loaded_bitmap::load(strm);