Redo text rendering (adds halo support to gui.text())

Now gui.text() and friends support halos. Also fixes a bug with partially
opaque halos in CUSTOMFONT rendering.
This commit is contained in:
Ilari Liusvaara 2015-05-27 10:44:50 +03:00
parent 56deafcd4a
commit 0c0156647b
13 changed files with 428 additions and 148 deletions

View file

@ -3,6 +3,7 @@
#include <vector>
#include <cstdint>
#include <functional>
#include <cstdlib>
#include <iostream>
#include <stdexcept>
@ -24,6 +25,7 @@ public:
std::vector<uint32_t> fglyph; //Bitpacked, element breaks between rows.
void render(fb<false>& fb, int32_t x, int32_t y, color fg, color bg, color hl) const;
void render(fb<true>& 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<uint32_t, uint32_t> get_metrics(const std::u32string& str, uint32_t xalign) const;
void for_each_glyph(const std::u32string& str, uint32_t xalign, std::function<void(uint32_t x, uint32_t y,
const glyph& g)> cb) const;
private:
std::map<std::u32string, glyph> glyphs;
unsigned rowadvance;

View file

@ -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<size_t, size_t> get_metrics(const std::string& string) throw();
std::pair<size_t, size_t> get_metrics(const std::string& string, uint32_t xalign, bool xdbl, bool ydbl)
throw();
/**
* Layout a string.
*
@ -565,6 +566,26 @@ struct font
*/
template<bool X> void render(struct fb<X>& 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<void(uint32_t x, uint32_t y, const glyph& g, bool xdbl, bool ydbl)> 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];

23
include/lua/halo.hpp Normal file
View file

@ -0,0 +1,23 @@
#ifndef _lua__halo__hpp__included__
#define _lua__halo__hpp__included__
#include <cstdlib>
#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<bool X> void halo_blit(struct framebuffer::fb<X>& 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

27
lua.lyx
View file

@ -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 <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

BIN
lua.pdf

Binary file not shown.

View file

@ -950,8 +950,7 @@ failed:
template<typename T>
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;
}

View file

@ -165,6 +165,30 @@ void font2::glyph::render(fb<true>& 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;
}
}
std::pair<uint32_t, uint32_t> 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<void(uint32_t x, uint32_t y,
const glyph& g)> 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;
}
}
}
}

View file

@ -727,28 +727,17 @@ const font::glyph& font::get_bad_glyph() throw()
return bad_glyph;
}
std::pair<size_t, size_t> font::get_metrics(const std::string& string) throw()
std::pair<size_t, size_t> 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<int32_t>([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::layout> 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<int32_t>([&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<layout> 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<int32_t>([this, &layout_x, &layout_y,
&l, &gtr](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, &gtr](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<bool X> void font::render(struct fb<X>& 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<int32_t>([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<X>::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<X>::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<void(uint32_t x, uint32_t y, const glyph& g, bool xdbl, bool ydbl)> 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<int32_t>([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<bool X> void font::render(struct fb<X>& 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<X>::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<X>::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;

View file

@ -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},

View file

@ -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<uint8_t> 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<bool X> void op_with(struct framebuffer::fb<X>& scr, unsigned char* mem,
std::pair<size_t, size_t> 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);

View file

@ -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<bool X> void op(struct framebuffer::fb<X>& 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<uint8_t> memory;
memory.resize(allocsize);
op_with(scr, &memory[0], size);
} else {
uint8_t memory[allocsize];
op_with(scr, memory, size);
}
}
template<bool X> void op_with(struct framebuffer::fb<X>& scr, unsigned char* mem,
std::pair<size_t, size_t> 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<true>& scr) throw() { op(scr); }
void operator()(struct framebuffer::fb<false>& 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<render_object_text>(x, y, text, fg, bg, hdbl, vdbl);
core.lua2->render_ctx->queue->create_add<render_object_text>(x, y, text, fg, bg, hl, hdbl, vdbl);
return 0;
}

88
src/lua/halo.cpp Normal file
View file

@ -0,0 +1,88 @@
#include "lua/halo.hpp"
#include "library/range.hpp"
#include "library/eatarg.hpp"
#include <cstdint>
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<bool X> void halo_blit(struct framebuffer::fb<X>& 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<X>::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<false>);
eat_argument(halo_blit<true>);
}

View file

@ -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;
}