diff --git a/include/library/framebuffer-font2.hpp b/include/library/framebuffer-font2.hpp index b08e6d01..0f55943a 100644 --- a/include/library/framebuffer-font2.hpp +++ b/include/library/framebuffer-font2.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,7 @@ public: std::vector fglyph; //Bitpacked, element breaks between rows. void render(fb& fb, int32_t x, int32_t y, color fg, color bg, color hl) const; void render(fb& fb, int32_t x, int32_t y, color fg, color bg, color hl) const; + void render(uint8_t* buf, size_t stride, uint32_t u, uint32_t v, uint32_t w, uint32_t h) const; }; font2(); font2(const std::string& file); @@ -33,6 +35,9 @@ public: throw(std::bad_alloc); const glyph& lookup_glyph(const std::u32string& key) const throw(); unsigned get_rowadvance() const throw() { return rowadvance; } + std::pair get_metrics(const std::u32string& str, uint32_t xalign) const; + void for_each_glyph(const std::u32string& str, uint32_t xalign, std::function cb) const; private: std::map glyphs; unsigned rowadvance; diff --git a/include/library/framebuffer.hpp b/include/library/framebuffer.hpp index 3656f0ab..b01db50e 100644 --- a/include/library/framebuffer.hpp +++ b/include/library/framebuffer.hpp @@ -528,7 +528,8 @@ struct font * Parameter string: The string to get metrics of. * Returns: A pair. First element is width of string, the second is height of string. */ - std::pair get_metrics(const std::string& string) throw(); + std::pair get_metrics(const std::string& string, uint32_t xalign, bool xdbl, bool ydbl) + throw(); /** * Layout a string. * @@ -565,6 +566,26 @@ struct font */ template void render(struct fb& scr, int32_t x, int32_t y, const std::string& text, color fg, color bg, bool hdbl, bool vdbl) throw(); +/** + * Call function on every glyph. + * + * Parameter str: The string to call on. + * Parameter alignx: The x alignment. + * Parameter cb: The callback to call. + */ + void for_each_glyph(const std::string& str, uint32_t alignx, bool xdbl, bool ydbl, + std::function cb); +/** + * Render to bitmap. + * + * Parameter buf: The bufer to render on. + * Parameter stride: The stride on buffer. + * Parameter str: The string to render. + * Parameter alignx: The x alignment. + * Parameter hdbl: If set, double width horizontally. + * Parameter vdbl: If set, double height vertically. + */ + void render(uint8_t* buf, size_t stride, const std::string& str, uint32_t alignx, bool hdbl, bool vdbl); private: glyph bad_glyph; uint32_t bad_glyph_data[4]; diff --git a/include/lua/halo.hpp b/include/lua/halo.hpp new file mode 100644 index 00000000..7834920f --- /dev/null +++ b/include/lua/halo.hpp @@ -0,0 +1,23 @@ +#ifndef _lua__halo__hpp__included__ +#define _lua__halo__hpp__included__ + +#include +#include "library/framebuffer.hpp" + +/** + * Render a 1px wide halo around monochrome image. + * + * Parameter pixmap: The pixmap to render halo on. Must be aligned to 32 bytes. + * Parameter width: Width of the pixmap. Must be multiple of 32 bytes. + * Parameter height: Height of the pixmap. + */ +void render_halo(unsigned char* pixmap, size_t width, size_t height); + +/** + * Blit a bitmap to screen. + */ +template void halo_blit(struct framebuffer::fb& scr, unsigned char* pixmap, size_t width, + size_t height, uint32_t x, uint32_t y, framebuffer::color& bg, framebuffer::color& fg, framebuffer::color& hl) + throw(); + +#endif \ No newline at end of file diff --git a/lua.lyx b/lua.lyx index 55b61ff8..2e3ffd49 100644 --- a/lua.lyx +++ b/lua.lyx @@ -4344,7 +4344,21 @@ Get names of all classes available. \end_layout \begin_layout Subsection +lsnes_features: Read feature flags +\end_layout +\begin_layout Itemize +Syntax: boolean lsnes_features(string feature) +\end_layout + +\begin_layout Standard +Checks if feature is available, if it is, returns true, otherwise + returns false. + The following features are known: +\end_layout + +\begin_layout Itemize +text-halos: gui.text supports halos (takes halo color argument). \end_layout \begin_layout Subsection @@ -4980,22 +4994,22 @@ gui.text/gui.textH/gui.textV,gui.textHV: Draw text \begin_layout Itemize Syntax: none gui.text(number x, number y, string text[, number fgc[, number - bgc]]) + bgc[, number hlc]]]) \end_layout \begin_layout Itemize Syntax: none gui.textH(number x, number y, string text[, number fgc[, number - bgc]]) + bgc[, number hlc]]]) \end_layout \begin_layout Itemize Syntax: none gui.textV(number x, number y, string text[, number fgc[, number - bgc]]) + bgc[, number hlc]]]) \end_layout \begin_layout Itemize Syntax: none gui.textHV(number x, number y, string text[, number fgc[, number - bgc]]) + bgc[, number hlc]]]) \end_layout \begin_layout Standard @@ -5025,9 +5039,14 @@ fgc: Text color (default is 0xFFFFFF (white)) bgc: Background color (default is -1 (transparent)) \end_layout +\begin_layout Itemize +hlc: Halo color (default is -1 (transparent)) +\end_layout + \begin_layout Standard Note: The H variants draw at double width and V variants draw at double height. + Halo thickness is always 1 and is not doubled. \end_layout \begin_layout Subsection diff --git a/lua.pdf b/lua.pdf index d02bff03..31126931 100644 Binary files a/lua.pdf and b/lua.pdf differ diff --git a/src/interface/c-interface.cpp b/src/interface/c-interface.cpp index 7c324e3b..78f7d562 100644 --- a/src/interface/c-interface.cpp +++ b/src/interface/c-interface.cpp @@ -950,8 +950,7 @@ failed: template void callback_render_text2(struct lsnes_core_fontrender_req& req, const std::string& str) { - auto size = main_font.get_metrics(str); - auto layout = main_font.dolayout(str); + auto size = main_font.get_metrics(str, 0, false, false); size_t memreq = size.first * size.second * sizeof(T); if(!memreq) memreq = 1; if(size.first && memreq / size.first / sizeof(T) < size.second) @@ -963,9 +962,9 @@ failed: T bg = (T)req.bg_color; T* bmp = (T*)req.bitmap; - for(auto i : layout) { - auto& g = *i.dglyph; - T* _bmp = bmp + (i.y * size.first + i.x); + main_font.for_each_glyph(str, 0, false, false, [size, &fg, &bg, bmp](uint32_t ix, uint32_t iy, + const framebuffer::font::glyph& g, bool hdbl, bool vdbl) { + T* _bmp = bmp + (iy * size.first + ix); size_t w = g.wide ? 16 : 8; size_t skip = size.first - w; for(size_t _y = 0; _y < 16; _y++) { @@ -980,7 +979,7 @@ failed: } _bmp = _bmp + skip; } - } + }); req.width = size.first; req.height = size.second; } diff --git a/src/library/framebuffer-font2.cpp b/src/library/framebuffer-font2.cpp index 307e93b2..5d48d4fb 100644 --- a/src/library/framebuffer-font2.cpp +++ b/src/library/framebuffer-font2.cpp @@ -165,6 +165,30 @@ void font2::glyph::render(fb& fb, int32_t x, int32_t y, color fg, _render(*this, fb, x, y, fg, bg, hl); } +void font2::glyph::render(uint8_t* buf, size_t _stride, uint32_t u, uint32_t v, uint32_t w, uint32_t h) const +{ + //Clip the bounding box to valid range. + u = std::min(u, (uint32_t)width); + v = std::min(v, (uint32_t)height); + w = std::min(w, (uint32_t)width); + h = std::min(h, (uint32_t)height); + if(u + w > width) w = width - u; + if(v + h > height) h = height - v; + if(!w || !h) return; + //Do the actual render. + size_t ge = v * stride; + for(unsigned j = 0; j < h; j++) { + buf += _stride; + ge += stride; + for(unsigned i = 0; i < w; i++) { + unsigned dx = u + i; + size_t gb = 31 - (dx & 31); + buf[i] = (fglyph[ge + (dx >> 5)] >> gb) & 1; + + + } + } +} font2::font2() { @@ -300,4 +324,41 @@ const font2::glyph& font2::lookup_glyph(const std::u32string& key) const throw() auto i = glyphs.find(key); return (i == glyphs.end()) ? empty_glyph : i->second; } -} \ No newline at end of file + +std::pair font2::get_metrics(const std::u32string& str, uint32_t xalign) const +{ + uint32_t w = 0; + uint32_t h = 0; + for_each_glyph(str, xalign, [&w, &h](uint32_t x, uint32_t y, const glyph& g) { + w = std::max(w, x + (uint32_t)g.width); + h = std::max(h, y + (uint32_t)g.height); + }); + return std::make_pair(w, h); +} + +void font2::for_each_glyph(const std::u32string& str, uint32_t xalign, std::function cb) const +{ + uint32_t drawx = 0; + uint32_t orig_x = 0; + uint32_t drawy = 0; + for(size_t i = 0; i < str.size();) { + uint32_t cp = str[i]; + std::u32string k = best_ligature_match(str, i); + const glyph& g = lookup_glyph(k); + if(k.length()) + i += k.length(); + else + i++; + if(cp == 9) { + drawx = (((drawx + xalign) + 64) >> 6 << 6) - xalign; + } else if(cp == 10) { + drawx = orig_x; + drawy += get_rowadvance(); + } else { + cb(drawx, drawy, g); + drawx += g.width; + } + } +} +} diff --git a/src/library/framebuffer.cpp b/src/library/framebuffer.cpp index fa45e3a5..f98776af 100644 --- a/src/library/framebuffer.cpp +++ b/src/library/framebuffer.cpp @@ -727,28 +727,17 @@ const font::glyph& font::get_bad_glyph() throw() return bad_glyph; } -std::pair font::get_metrics(const std::string& string) throw() +std::pair font::get_metrics(const std::string& string, uint32_t xalign, bool xdbl, bool ydbl) throw() { size_t commit_width = 0; size_t commit_height = 0; - size_t linelength = 0; - utf8::to32i(string.begin(), string.end(), lambda_output_iterator([this, &linelength, &commit_width, - &commit_height](const int32_t& cp) -> void { - const glyph& g = get_glyph(cp); - switch(cp) { - case 9: - linelength = (linelength + TABSTOPS) / TABSTOPS * TABSTOPS; - commit_width = max(commit_width, linelength); - break; - case 10: - commit_height += 16; - break; - default: - linelength = linelength + (g.wide ? 16 : 8); - commit_width = max(commit_width, linelength); - break; - }; - })); + for_each_glyph(string, xalign, xdbl, ydbl, [&commit_width, &commit_height](uint32_t x, uint32_t y, + const glyph& g, bool xdbl, bool ydbl) { + size_t w = 1 << ((g.wide ? 4 : 3) + (xdbl ? 1 : 0)); + size_t h = 1 << (4 + (ydbl ? 1 : 0)); + commit_width = std::max(commit_width, (size_t)(x + w)); + commit_height = std::max(commit_height, (size_t)(y + h)); + }); return std::make_pair(commit_width, commit_height); } @@ -756,35 +745,20 @@ std::vector font::dolayout(const std::string& string) throw(std::b { //First, calculate the number of glyphs to draw. size_t chars = 0; - utf8::to32i(string.begin(), string.end(), lambda_output_iterator([&chars](const int32_t& cp) - -> void { - if(cp != 9 && cp != 10) - chars++; - })); + for_each_glyph(string, 0, false, false, [&chars](uint32_t x, uint32_t y, const glyph& g, bool xdbl, + bool ydbl) { + chars++; + }); //Allocate space. std::vector l; l.resize(chars); size_t gtr = 0; - size_t layout_x = 0; - size_t layout_y = 0; - utf8::to32i(string.begin(), string.end(), lambda_output_iterator([this, &layout_x, &layout_y, - &l, >r](const int32_t cp) { - const glyph& g = get_glyph(cp); - switch(cp) { - case 9: - layout_x = (layout_x + TABSTOPS) / TABSTOPS * TABSTOPS; - break; - case 10: - layout_x = 0; - layout_y = layout_y + 16; - break; - default: - l[gtr].x = layout_x; - l[gtr].y = layout_y; - l[gtr++].dglyph = &g; - layout_x = layout_x + (g.wide ? 16 : 8);; - } - })); + for_each_glyph(string, 0, false, false, [&l, >r](uint32_t x, uint32_t y, const glyph& g, bool xdbl, + bool ydbl) { + l[gtr].x = x; + l[gtr].y = y; + l[gtr++].dglyph = &g; + }); return l; } @@ -812,12 +786,109 @@ template void font::render(struct fb& scr, int32_t x, int32_t y, cons { x += scr.get_origin_x(); y += scr.get_origin_y(); - size_t layout_x = 0; - size_t layout_y = 0; size_t swidth = scr.get_width(); size_t sheight = scr.get_height(); - utf8::to32i(text.begin(), text.end(), lambda_output_iterator([this, x, y, &scr, &layout_x, - &layout_y, swidth, sheight, hdbl, vdbl, &fg, &bg](const int32_t& cp) { + + for_each_glyph(text, x, hdbl, vdbl, [this, x, y, &scr, swidth, sheight, &fg, &bg](uint32_t lx, uint32_t ly, + const glyph& g, bool hdbl, bool vdbl) { + //Render this glyph at x + lx, y + ly. + int32_t gx = x + lx; + int32_t gy = y + ly; + //Don't draw characters completely off-screen. + if(gy <= (vdbl ? -32 : -16) || gy >= (ssize_t)sheight) + return; + if(gx <= -(hdbl ? 2 : 1) * (g.wide ? 16 : 8) || gx >= (ssize_t)swidth) + return; + //Compute the bounding box. + uint32_t xstart = 0; + uint32_t ystart = 0; + uint32_t xlength = (hdbl ? 2 : 1) * (g.wide ? 16 : 8); + uint32_t ylength = (vdbl ? 32 : 16); + if(gx < 0) xstart = -gx; + if(gy < 0) ystart = -gy; + xlength -= xstart; + ylength -= ystart; + if(gx + xstart + xlength > swidth) xlength = swidth - (gx + xstart); + if(gy + ystart + ylength > sheight) ylength = sheight - (gy + ystart); + if(g.data) + for(size_t i = 0; i < ylength; i++) { + typename fb::element_t* r = scr.rowptr(gy + ystart + i) + + (gx + xstart); + uint32_t _y = (i + ystart) >> (vdbl ? 1 : 0); + uint32_t d = g.data[_y >> (g.wide ? 1 : 2)]; + if(g.wide) + d >>= 16 - ((_y & 1) << 4); + else + d >>= 24 - ((_y & 3) << 3); + if(hdbl) + for(size_t j = 0; j < xlength; j++) { + uint32_t b = (g.wide ? 15 : 7) - ((j + xstart) >> 1); + if(((d >> b) & 1) != 0) + fg.apply(r[j]); + else + bg.apply(r[j]); + } + else + for(size_t j = 0; j < xlength; j++) { + uint32_t b = (g.wide ? 15 : 7) - (j + xstart); + if(((d >> b) & 1) != 0) + fg.apply(r[j]); + else + bg.apply(r[j]); + } + } + else + for(size_t i = 0; i < ylength; i++) { + typename fb::element_t* r = scr.rowptr(gy + ystart + i) + (gx + xstart); + for(size_t j = 0; j < xlength; j++) + bg.apply(r[j]); + } + }); +} + +void font::render(uint8_t* buf, size_t stride, const std::string& str, uint32_t alignx, bool hdbl, bool vdbl) +{ + for_each_glyph(str, alignx, hdbl, vdbl, [this, buf, stride] + (uint32_t lx, uint32_t ly, const glyph& g, bool hdbl, bool vdbl) { + uint8_t* ptr = buf + (ly * stride + lx); + size_t xlength = (g.wide ? 16 : 8) << (hdbl ? 1 : 0); + size_t height = 16 << (vdbl ? 1 : 0); + + if(g.data) + for(size_t i = 0; i < height; i++) { + ptr += stride; + uint32_t _y = i >> (vdbl ? 1 : 0); + uint32_t d = g.data[_y >> (g.wide ? 1 : 2)]; + if(g.wide) + d >>= 16 - ((_y & 1) << 4); + else + d >>= 24 - ((_y & 3) << 3); + if(hdbl) + for(size_t j = 0; j < xlength; j++) { + uint32_t b = (g.wide ? 15 : 7) - (j >> 1); + ptr[j] = ((d >> b) & 1); + } + else + for(size_t j = 0; j < xlength; j++) { + uint32_t b = (g.wide ? 15 : 7) - j; + ptr[j] = ((d >> b) & 1); + } + } + }); +} + + +void font::for_each_glyph(const std::string& str, uint32_t alignx, bool xdbl, bool ydbl, + std::function cb) +{ + size_t layout_x = alignx; + size_t layout_y = 0; + size_t offset = alignx; + unsigned _xdbl = xdbl; + unsigned _ydbl = ydbl; + auto _cb = &cb; + utf8::to32i(str.begin(), str.end(), lambda_output_iterator([this, &layout_x, &layout_y, _xdbl, + _ydbl, offset, _cb](const int32_t cp) { const glyph& g = get_glyph(cp); switch(cp) { case 9: @@ -825,67 +896,16 @@ template void font::render(struct fb& scr, int32_t x, int32_t y, cons break; case 10: layout_x = 0; - layout_y = layout_y + (vdbl ? 32 : 16); + layout_y = layout_y + 16; break; default: - //Render this glyph at x + layout_x, y + layout_y. - int32_t gx = x + layout_x; - int32_t gy = y + layout_y; - //Don't draw characters completely off-screen. - if(gy <= (vdbl ? -32 : -16) || gy >= (ssize_t)sheight) - break; - if(gx <= -(hdbl ? 2 : 1) * (g.wide ? 16 : 8) || gx >= (ssize_t)swidth) - break; - //Compute the bounding box. - uint32_t xstart = 0; - uint32_t ystart = 0; - uint32_t xlength = (hdbl ? 2 : 1) * (g.wide ? 16 : 8); - uint32_t ylength = (vdbl ? 32 : 16); - if(gx < 0) xstart = -gx; - if(gy < 0) ystart = -gy; - xlength -= xstart; - ylength -= ystart; - if(gx + xstart + xlength > swidth) xlength = swidth - (gx + xstart); - if(gy + ystart + ylength > sheight) ylength = sheight - (gy + ystart); - if(g.data) - for(size_t i = 0; i < ylength; i++) { - typename fb::element_t* r = scr.rowptr(gy + ystart + i) + - (gx + xstart); - uint32_t _y = (i + ystart) >> (vdbl ? 1 : 0); - uint32_t d = g.data[_y >> (g.wide ? 1 : 2)]; - if(g.wide) - d >>= 16 - ((_y & 1) << 4); - else - d >>= 24 - ((_y & 3) << 3); - if(hdbl) - for(size_t j = 0; j < xlength; j++) { - uint32_t b = (g.wide ? 15 : 7) - ((j + xstart) >> 1); - if(((d >> b) & 1) != 0) - fg.apply(r[j]); - else - bg.apply(r[j]); - } - else - for(size_t j = 0; j < xlength; j++) { - uint32_t b = (g.wide ? 15 : 7) - (j + xstart); - if(((d >> b) & 1) != 0) - fg.apply(r[j]); - else - bg.apply(r[j]); - } - } - else - for(size_t i = 0; i < ylength; i++) { - typename fb::element_t* r = scr.rowptr(gy + ystart + i) + - (gx + xstart); - for(size_t j = 0; j < xlength; j++) - bg.apply(r[j]); - } - layout_x += (hdbl ? 2 : 1) * (g.wide ? 16 : 8); + (*_cb)((layout_x - offset) << _xdbl, layout_y << _ydbl, g, _xdbl, _ydbl); + layout_x = layout_x + (g.wide ? 16 : 8); } })); } + color::color(const std::string& clr) throw(std::bad_alloc, std::runtime_error) { int64_t col = -1; diff --git a/src/lua/core.cpp b/src/lua/core.cpp index 7e766530..9993d62f 100644 --- a/src/lua/core.cpp +++ b/src/lua/core.cpp @@ -230,6 +230,16 @@ namespace return 1; } + int lsnes_features(lua::state& L, lua::parameters& P) + { + bool ok = false; + std::string arg; + P(arg); + if(arg == "text-halos") ok = true; + L.pushboolean(ok); + return 1; + } + lua::functions LUA_misc_fns(lua_func_misc, "", { {"print2", print2}, {"exec", exec}, @@ -239,6 +249,7 @@ namespace {"set_timer_timeout", set_timer_timeout}, {"bus_address", bus_address}, {"get_lua_memory_use", get_lua_memory_use}, + {"lsnes_features", lsnes_features}, {"memory.get_lag_flag", get_lag_flag}, {"memory.set_lag_flag", set_lag_flag}, {"gui.get_runmode", get_runmode}, diff --git a/src/lua/gui-text-cf.cpp b/src/lua/gui-text-cf.cpp index 67ff99a0..da056e2c 100644 --- a/src/lua/gui-text-cf.cpp +++ b/src/lua/gui-text-cf.cpp @@ -1,5 +1,6 @@ #include "core/instance.hpp" #include "lua/internal.hpp" +#include "lua/halo.hpp" #include "lua/bitmap.hpp" #include "fonts/wrapper.hpp" #include "core/framebuffer.hpp" @@ -58,34 +59,38 @@ namespace { const framebuffer::font2& fdata = font->get_font(); std::u32string _text = utf8::to32(text); - int32_t orig_x = x + scr.get_origin_x(); - int32_t drawx = x + scr.get_origin_x(); - int32_t drawy = y + scr.get_origin_y(); - if(hl) { - //Adjust for halo. - drawx++; - orig_x++; - drawy++; - } - for(size_t i = 0; i < _text.size();) { - uint32_t cp = _text[i]; - std::u32string k = fdata.best_ligature_match(_text, i); - const framebuffer::font2::glyph& glyph = fdata.lookup_glyph(k); - if(k.length()) - i += k.length(); - else - i++; - if(cp == 9) { - drawx = (drawx + 64) >> 6 << 6; - } else if(cp == 10) { - drawx = orig_x; - drawy += fdata.get_rowadvance(); - } else { - glyph.render(scr, drawx, drawy, fg, bg, hl); - drawx += glyph.width; - } + + auto size = fdata.get_metrics(_text, x); + //Enlarge size by 2 in each dimension, in order to accomodiate halo, if any. + //Round up width to multiple of 32. + size.first = (size.first + 33) >> 5 << 5; + size.second += 2; + size_t allocsize = size.first * size.second + 32; + + if(allocsize > 32768) { + std::vector memory; + memory.resize(allocsize); + op_with(scr, &memory[0], size, _text, fdata); + } else { + uint8_t memory[allocsize]; + op_with(scr, memory, size, _text, fdata); } } + template void op_with(struct framebuffer::fb& scr, unsigned char* mem, + std::pair size, const std::u32string& _text, const framebuffer::font2& fdata) + throw() + { + memset(mem, 0, size.first * size.second); + int32_t rx = x + scr.get_origin_x(); + int32_t ry = y + scr.get_origin_y(); + fdata.for_each_glyph(_text, x, [mem, size](uint32_t x, uint32_t y, + const framebuffer::font2::glyph& glyph) { + x++; + y++; + glyph.render(mem + (y * size.first + x), size.first, 0, 0, glyph.width, glyph.height); + }); + halo_blit(scr, mem, size.first, size.second, rx, ry, bg, fg, hl); + } bool kill_request(void* obj) throw() { return kill_request_ifeq(font.object(), obj); diff --git a/src/lua/gui-text.cpp b/src/lua/gui-text.cpp index e217b4b0..7394ee53 100644 --- a/src/lua/gui-text.cpp +++ b/src/lua/gui-text.cpp @@ -1,5 +1,6 @@ #include "core/instance.hpp" #include "lua/internal.hpp" +#include "lua/halo.hpp" #include "fonts/wrapper.hpp" #include "library/framebuffer.hpp" #include "library/lua-framebuffer.hpp" @@ -9,12 +10,38 @@ namespace struct render_object_text : public framebuffer::object { render_object_text(int32_t _x, int32_t _y, const std::string& _text, framebuffer::color _fg, - framebuffer::color _bg, bool _hdbl = false, bool _vdbl = false) throw() - : x(_x), y(_y), text(_text), fg(_fg), bg(_bg), hdbl(_hdbl), vdbl(_vdbl) {} + framebuffer::color _bg, framebuffer::color _hl, bool _hdbl = false, bool _vdbl = false) + throw() + : x(_x), y(_y), text(_text), fg(_fg), bg(_bg), hl(_hl), hdbl(_hdbl), vdbl(_vdbl) {} ~render_object_text() throw() {} template void op(struct framebuffer::fb& scr) throw() { - main_font.render(scr, x, y, text, fg, bg, hdbl, vdbl); + auto size = main_font.get_metrics(text, x, hdbl, vdbl); + //Enlarge size by 2 in each dimension, in order to accomodiate halo, if any. + //Round up width to multiple of 32. + size.first = (size.first + 33) >> 5 << 5; + size.second += 2; + //The -1 is to accomodiate halo. + size_t allocsize = size.first * size.second + 32; + + if(allocsize > 32768) { + std::vector memory; + memory.resize(allocsize); + op_with(scr, &memory[0], size); + } else { + uint8_t memory[allocsize]; + op_with(scr, memory, size); + } + } + template void op_with(struct framebuffer::fb& scr, unsigned char* mem, + std::pair size) throw() + { + uint32_t rx = x + (int32_t)scr.get_origin_x() - 1; + uint32_t ry = y + (int32_t)scr.get_origin_y() - 1; + mem += (32 - ((size_t)mem & 31)) & 31; //Align. + memset(mem, 0, size.first * size.second); + main_font.render(mem + size.first + 1, size.first, text, x, hdbl, vdbl); + halo_blit(scr, mem, size.first, size.second, rx, ry, bg, fg, hl); } void operator()(struct framebuffer::fb& scr) throw() { op(scr); } void operator()(struct framebuffer::fb& scr) throw() { op(scr); } @@ -25,6 +52,7 @@ namespace std::string text; framebuffer::color fg; framebuffer::color bg; + framebuffer::color hl; bool hdbl; bool vdbl; }; @@ -35,13 +63,13 @@ namespace auto& core = CORE(); int32_t x, y; std::string text; - framebuffer::color fg, bg; + framebuffer::color fg, bg, hl; if(!core.lua2->render_ctx) return 0; - P(x, y, text, P.optional(fg, 0xFFFFFFU), P.optional(bg, -1)); + P(x, y, text, P.optional(fg, 0xFFFFFFU), P.optional(bg, -1), P.optional(hl, -1)); - core.lua2->render_ctx->queue->create_add(x, y, text, fg, bg, hdbl, vdbl); + core.lua2->render_ctx->queue->create_add(x, y, text, fg, bg, hl, hdbl, vdbl); return 0; } diff --git a/src/lua/halo.cpp b/src/lua/halo.cpp new file mode 100644 index 00000000..735065d9 --- /dev/null +++ b/src/lua/halo.cpp @@ -0,0 +1,88 @@ +#include "lua/halo.hpp" +#include "library/range.hpp" +#include "library/eatarg.hpp" +#include + +namespace +{ + void render_halo1(unsigned char* prow, size_t width) + { + uint32_t x = prow[0]; + for(unsigned i = 0; i < width - 1; i++) { + x = (x << 10) | prow[i + 1]; + prow[i] = (x >> 19) | (x >> 10) | (x << 1); + } + prow[width - 1] = (x >> 10) | (x << 1); + } + void render_halo2(unsigned char* prow, const unsigned char* srow, size_t width) + { + uint32_t x = prow[0]; + uint32_t y = srow[0]; + uint32_t z = 0; + for(unsigned i = 0; i < width - 1; i++) { + x = (x << 10) | prow[i + 1]; + y = (y << 10) | srow[i + 1]; + z = x | y; + prow[i] = (z >> 19) | (x >> 10) | (z << 1) | (y >> 9); + } + prow[width - 1] = (x >> 10) | ((x | y) << 1) | (y >> 9); + } + void render_halo3(unsigned char* prow, const unsigned char* arow, const unsigned char* brow, size_t width) + { + uint32_t x = prow[0]; + uint32_t y = arow[0] | brow[0]; + uint32_t z = 0; + for(unsigned i = 0; i < width - 1; i++) { + x = (x << 10) | prow[i + 1]; + y = (y << 10) | arow[i + 1] | brow[i + 1]; + z = x | y; + prow[i] = (z >> 19) | (x >> 10) | (z << 1) | (y >> 9); + } + prow[width - 1] = (x >> 10) | (z << 1) | (y >> 9); + } + void mask_halo(unsigned char* pixmap, size_t pixels) + { + for(size_t i = 0; i < pixels; i++) + pixmap[i] &= 0x03; + } +} + +void render_halo(unsigned char* pixmap, size_t width, size_t height) +{ + if(!height || !width) return; + if(height == 1) { + render_halo1(pixmap, width); + } else { + render_halo2(pixmap, pixmap + width, width); + size_t off = width; + for(size_t i = 1; i < height - 1; i++, off += width) + render_halo3(pixmap + off, pixmap + off - width, pixmap + off + width, width); + render_halo2(pixmap + off, pixmap + off - width, width); + } + mask_halo(pixmap, width * height); +} + +template void halo_blit(struct framebuffer::fb& scr, unsigned char* pixmap, size_t width, + size_t height, uint32_t x, uint32_t y, framebuffer::color& bg, framebuffer::color& fg, framebuffer::color& hl) + throw() +{ + framebuffer::color cmap4[4] = {bg, fg, hl, fg}; + if(hl) + render_halo(pixmap, width, height); + range bX = (range::make_w(scr.get_width()) - x) & range::make_w(width); + range bY = (range::make_w(scr.get_height()) - y) & range::make_w(height); + for(uint32_t r = bY.low(); r < bY.high(); r++) { + typename framebuffer::fb::element_t* rptr = scr.rowptr(y + r); + size_t eptr = x + bX.low(); + for(uint32_t c = bX.low(); c < bX.high(); c++, eptr++) { + uint16_t i = pixmap[r * width + c]; + cmap4[i].apply(rptr[eptr]); + } + } +} + +void _pull_fn_68269328963289632986296386936() +{ + eat_argument(halo_blit); + eat_argument(halo_blit); +} \ No newline at end of file diff --git a/src/platform/wxwidgets/textrender.cpp b/src/platform/wxwidgets/textrender.cpp index fc13e1bb..6a5994d0 100644 --- a/src/platform/wxwidgets/textrender.cpp +++ b/src/platform/wxwidgets/textrender.cpp @@ -135,7 +135,7 @@ void text_framebuffer::render(char* tbuffer) size_t text_framebuffer::text_width(const std::string& text) { - auto x = main_font.get_metrics(text); + auto x = main_font.get_metrics(text, 0, false, false); return x.first / 8; }