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:
parent
56deafcd4a
commit
0c0156647b
13 changed files with 428 additions and 148 deletions
|
@ -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;
|
||||
|
|
|
@ -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
23
include/lua/halo.hpp
Normal 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
27
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 <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
BIN
lua.pdf
Binary file not shown.
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, >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<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;
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
88
src/lua/halo.cpp
Normal 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>);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue