Merge branch 'rr1-maint'
Conflicts: manual.lyx manual.txt
This commit is contained in:
commit
531c0d0635
6 changed files with 491 additions and 0 deletions
BIN
data/verysmall.font
Normal file
BIN
data/verysmall.font
Normal file
Binary file not shown.
56
include/library/customfont.hpp
Normal file
56
include/library/customfont.hpp
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef _customfont__hpp__included__
|
||||
#define _customfont__hpp__included__
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <map>
|
||||
#include "framebuffer.hpp"
|
||||
|
||||
class ligature_key
|
||||
{
|
||||
public:
|
||||
ligature_key(const std::vector<uint32_t>& key) throw(std::bad_alloc);
|
||||
const std::vector<uint32_t>& get() const throw() { return ikey; }
|
||||
size_t length() const throw() { return ikey.size(); }
|
||||
bool operator<(const ligature_key& key) const throw();
|
||||
bool operator<=(const ligature_key& key) const throw() { return !(key < *this); }
|
||||
bool operator==(const ligature_key& key) const throw();
|
||||
bool operator!=(const ligature_key& key) const throw() { return !(key == *this); }
|
||||
bool operator>=(const ligature_key& key) const throw() { return !(*this < key); }
|
||||
bool operator>(const ligature_key& key) const throw() { return key < *this; }
|
||||
private:
|
||||
std::vector<uint32_t> ikey;
|
||||
};
|
||||
|
||||
struct font_glyph_data
|
||||
{
|
||||
font_glyph_data();
|
||||
font_glyph_data(std::istream& s);
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned stride;
|
||||
std::vector<uint32_t> glyph; //Bitpacked, element breaks between rows.
|
||||
void render(framebuffer<false>& fb, int32_t x, int32_t y, premultiplied_color fg, premultiplied_color bg)
|
||||
const;
|
||||
void render(framebuffer<true>& fb, int32_t x, int32_t y, premultiplied_color fg, premultiplied_color bg) const;
|
||||
};
|
||||
|
||||
struct custom_font
|
||||
{
|
||||
public:
|
||||
custom_font();
|
||||
custom_font(const std::string& file);
|
||||
void add(const ligature_key& key, const font_glyph_data& glyph) throw(std::bad_alloc);
|
||||
ligature_key best_ligature_match(const std::vector<uint32_t>& codepoints, size_t start) const
|
||||
throw(std::bad_alloc);
|
||||
const font_glyph_data& lookup_glyph(const ligature_key& key) const throw();
|
||||
unsigned get_rowadvance() const throw() { return rowadvance; }
|
||||
private:
|
||||
std::map<ligature_key, font_glyph_data> glyphs;
|
||||
unsigned rowadvance;
|
||||
};
|
||||
|
||||
#endif
|
17
manual.lyx
17
manual.lyx
|
@ -2099,6 +2099,23 @@ Run specified render queue, copying the objects to current render queue.
|
|||
Warning: Don't try to run the current render queue.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
gui.loadfont(string filename)
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Loads font from specified file (CUSTOMFONT object).
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsubsection
|
||||
CUSTOMFONT(number x, number y, string text[, number fgc[, number bgc]])
|
||||
\end_layout
|
||||
|
||||
\begin_layout Standard
|
||||
Draw string with custom font to screen.
|
||||
The parameters are the same as in gui.text.
|
||||
\end_layout
|
||||
|
||||
\begin_layout Subsection
|
||||
table input
|
||||
\end_layout
|
||||
|
|
10
manual.txt
10
manual.txt
|
@ -1051,6 +1051,16 @@ queue.
|
|||
|
||||
• Warning: Don't try to run the current render queue.
|
||||
|
||||
8.3.33 gui.loadfont(string filename)
|
||||
|
||||
Loads font from specified file (CUSTOMFONT object).
|
||||
|
||||
8.3.34 CUSTOMFONT(number x, number y, string text[, number fgc[,
|
||||
number bgc]])
|
||||
|
||||
Draw string with custom font to screen. The parameters are the
|
||||
same as in gui.text.
|
||||
|
||||
8.4 table input
|
||||
|
||||
Input handling. Only available in on_input callback.
|
||||
|
|
278
src/library/customfont.cpp
Normal file
278
src/library/customfont.cpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
#include "customfont.hpp"
|
||||
#include "serialization.hpp"
|
||||
#include <cstring>
|
||||
#include "zip.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
ligature_key::ligature_key(const std::vector<uint32_t>& key) throw(std::bad_alloc)
|
||||
{
|
||||
ikey = key;
|
||||
}
|
||||
|
||||
bool ligature_key::operator<(const ligature_key& key) const throw()
|
||||
{
|
||||
for(size_t i = 0; i < ikey.size() && i < key.ikey.size(); i++)
|
||||
if(ikey[i] < key.ikey[i])
|
||||
return true;
|
||||
else if(ikey[i] > key.ikey[i])
|
||||
return false;
|
||||
return (ikey.size() < key.ikey.size());
|
||||
}
|
||||
|
||||
bool ligature_key::operator==(const ligature_key& key) const throw()
|
||||
{
|
||||
for(size_t i = 0; i < ikey.size() && i < key.ikey.size(); i++)
|
||||
if(ikey[i] != key.ikey[i])
|
||||
return false;
|
||||
return (ikey.size() == key.ikey.size());
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void bound(int32_t c, uint32_t odim, uint32_t dim, uint32_t& dc, uint32_t& off, uint32_t& size)
|
||||
{
|
||||
if(c >= dim || c + odim <= 0) {
|
||||
//Outside the screen.
|
||||
dc = 0;
|
||||
off = 0;
|
||||
size = 0;
|
||||
} else if(c >= 0) {
|
||||
dc = c;
|
||||
off = 0;
|
||||
size = odim;
|
||||
} else {
|
||||
dc = 0;
|
||||
off = -c;
|
||||
size = odim + c;
|
||||
}
|
||||
if(dc + size > dim)
|
||||
size = dim - dc;
|
||||
}
|
||||
|
||||
template<bool T> void _render(const font_glyph_data& glyph, framebuffer<T>& fb, int32_t x, int32_t y,
|
||||
premultiplied_color fg, premultiplied_color bg)
|
||||
{
|
||||
uint32_t xdc, xoff, xsize;
|
||||
uint32_t ydc, yoff, ysize;
|
||||
bound(x, glyph.width, fb.get_width(), xdc, xoff, xsize);
|
||||
bound(y, glyph.height, fb.get_height(), ydc, yoff, ysize);
|
||||
if(!xsize || !ysize)
|
||||
return;
|
||||
for(unsigned i = 0; i < ysize; i++) {
|
||||
auto p = fb.rowptr(i + ydc);
|
||||
for(unsigned j = 0; j < xsize; j++) {
|
||||
size_t ge = (i + yoff) * glyph.stride + ((j + xoff) / 32);
|
||||
size_t gb = 31 - (j + xoff) % 32;
|
||||
if((glyph.glyph[ge] >> gb) & 1)
|
||||
fg.apply(p[j + xdc]);
|
||||
else
|
||||
bg.apply(p[j + xdc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
font_glyph_data::font_glyph_data()
|
||||
{
|
||||
stride = width = height = 0;
|
||||
}
|
||||
|
||||
font_glyph_data::font_glyph_data(std::istream& s)
|
||||
{
|
||||
char header[40];
|
||||
bool old = true;
|
||||
bool upside_down = true;
|
||||
size_t rcount = 26;
|
||||
s.read(header, 26);
|
||||
if(!s)
|
||||
throw std::runtime_error("Can't read glyph bitmap header");
|
||||
if(read16ule(header + 0) != 0x4D42)
|
||||
throw std::runtime_error("Bad glyph BMP magic");
|
||||
if(read16ule(header + 14) != 12) {
|
||||
//Not OS/2 format.
|
||||
old = false;
|
||||
rcount = 40;
|
||||
s.read(header + 26, 14);
|
||||
if(!s)
|
||||
throw std::runtime_error("Can't read glyph bitmap header");
|
||||
}
|
||||
|
||||
uint32_t startoff = read32ule(header + 10);
|
||||
if(old) {
|
||||
width = read16ule(header + 18);
|
||||
height = read16ule(header + 20);
|
||||
if(read16ule(header + 22) != 1)
|
||||
throw std::runtime_error("Bad glyph BMP planecount");
|
||||
if(read16ule(header + 24) != 1)
|
||||
throw std::runtime_error("Bad glyph BMP bitdepth");
|
||||
if(startoff < 26)
|
||||
throw std::runtime_error("Glyph BMP data can't overlap header");
|
||||
} else {
|
||||
long _width = read32sle(header + 18);
|
||||
long _height = read32sle(header + 22);
|
||||
if(_width < 0)
|
||||
throw std::runtime_error("Bad glyph BMP size");
|
||||
if(_height < 0)
|
||||
upside_down = false;
|
||||
width = _width;
|
||||
height = (_height >= 0) ? height : -height;
|
||||
|
||||
if(read16ule(header + 26) != 1)
|
||||
throw std::runtime_error("Bad glyph BMP planecount");
|
||||
if(read16ule(header + 28) != 1)
|
||||
throw std::runtime_error("Bad glyph BMP bitdepth");
|
||||
if(read32ule(header + 30) != 0)
|
||||
throw std::runtime_error("Bad glyph BMP compression method");
|
||||
if(startoff < 40)
|
||||
throw std::runtime_error("Glyph BMP data can't overlap header");
|
||||
}
|
||||
//Discard data until start of bitmap.
|
||||
while(rcount < startoff) {
|
||||
s.get();
|
||||
if(!s)
|
||||
throw std::runtime_error("EOF while skipping to BMP data");
|
||||
rcount++;
|
||||
}
|
||||
stride = (width + 31) / 32;
|
||||
glyph.resize(stride * height);
|
||||
memset(&glyph[0], 0, sizeof(uint32_t) * glyph.size());
|
||||
size_t toskip = (4 - ((width + 7) / 8) % 4) % 4;
|
||||
for(size_t i = 0; i < height; i++) {
|
||||
size_t y = upside_down ? (height - i - 1) : i;
|
||||
size_t bpos = y * stride * 32;
|
||||
for(size_t j = 0; j < width; j += 8) {
|
||||
size_t e = (bpos + j) / 32;
|
||||
size_t b = (bpos + j) % 32;
|
||||
int c = s.get();
|
||||
if(!s)
|
||||
throw std::runtime_error("EOF while reading BMP data");
|
||||
glyph[e] |= ((uint32_t)c << (24 - b));
|
||||
}
|
||||
for(size_t j = 0; j < toskip; j++) {
|
||||
s.get();
|
||||
if(!s)
|
||||
throw std::runtime_error("EOF while reading BMP data");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void font_glyph_data::render(framebuffer<false>& fb, int32_t x, int32_t y, premultiplied_color fg,
|
||||
premultiplied_color bg) const
|
||||
{
|
||||
_render(*this, fb, x, y, fg, bg);
|
||||
}
|
||||
|
||||
void font_glyph_data::render(framebuffer<true>& fb, int32_t x, int32_t y, premultiplied_color fg,
|
||||
premultiplied_color bg) const
|
||||
{
|
||||
_render(*this, fb, x, y, fg, bg);
|
||||
}
|
||||
|
||||
|
||||
custom_font::custom_font()
|
||||
{
|
||||
rowadvance = 0;
|
||||
}
|
||||
|
||||
custom_font::custom_font(const std::string& file)
|
||||
{
|
||||
std::istream* toclose = NULL;
|
||||
rowadvance = 0;
|
||||
try {
|
||||
zip_reader r(file);
|
||||
for(auto member : r) {
|
||||
//Parse the key out of filename.
|
||||
std::vector<uint32_t> k;
|
||||
std::string tname = member;
|
||||
std::string tmp;
|
||||
if(tname == "bad") {
|
||||
//Special, no key.
|
||||
} else if(regex_match("[0-9]+(-[0-9]+)*", tname))
|
||||
while(tname != "") {
|
||||
extract_token(tname, tmp, "-");
|
||||
k.push_back(parse_value<uint32_t>(tmp));
|
||||
}
|
||||
else {
|
||||
delete toclose;
|
||||
toclose = NULL;
|
||||
continue;
|
||||
}
|
||||
ligature_key key(k);
|
||||
std::istream& s = r[member];
|
||||
toclose = &s;
|
||||
try {
|
||||
add(key, font_glyph_data(s));
|
||||
} catch(std::bad_alloc& e) {
|
||||
throw;
|
||||
} catch(std::exception& e) {
|
||||
throw std::runtime_error(tname + std::string(": ") + e.what());
|
||||
}
|
||||
delete toclose;
|
||||
toclose = NULL;
|
||||
}
|
||||
} catch(std::bad_alloc& e) {
|
||||
if(toclose)
|
||||
delete toclose;
|
||||
throw;
|
||||
} catch(std::exception& e) {
|
||||
if(toclose)
|
||||
delete toclose;
|
||||
throw std::runtime_error(std::string("Error reading font: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const ligature_key& lkey)
|
||||
{
|
||||
if(!lkey.length())
|
||||
return (os << "bad");
|
||||
for(size_t i = 0; i < lkey.length(); i++) {
|
||||
if(i)
|
||||
os << "-";
|
||||
os << lkey.get()[i];
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
void custom_font::add(const ligature_key& key, const font_glyph_data& glyph) throw(std::bad_alloc)
|
||||
{
|
||||
glyphs[key] = glyph;
|
||||
if(glyph.height > rowadvance)
|
||||
rowadvance = glyph.height;
|
||||
}
|
||||
|
||||
ligature_key custom_font::best_ligature_match(const std::vector<uint32_t>& codepoints, size_t start) const
|
||||
throw(std::bad_alloc)
|
||||
{
|
||||
std::vector<uint32_t> tmp;
|
||||
if(start >= codepoints.size())
|
||||
return ligature_key(tmp); //Bad.
|
||||
ligature_key best(tmp);
|
||||
for(size_t i = 1; i <= codepoints.size() - start; i++) {
|
||||
tmp.push_back(codepoints[start + i - 1]);
|
||||
ligature_key lkey(tmp);
|
||||
if(glyphs.count(lkey))
|
||||
best = lkey;
|
||||
auto j = glyphs.lower_bound(lkey);
|
||||
//If lower_bound is greater than equivalent length of string, there can be no better match.
|
||||
if(j == glyphs.end())
|
||||
break;
|
||||
const std::vector<uint32_t>& tmp2 = j->first.get();
|
||||
bool best_found = false;
|
||||
for(size_t k = 0; k < tmp2.size() && start + k < codepoints.size(); k++)
|
||||
if(tmp2[k] > codepoints[start + k]) {
|
||||
best_found = true;
|
||||
break;
|
||||
} else if(tmp2[k] < codepoints[start + k])
|
||||
break;
|
||||
if(best_found)
|
||||
break;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
const font_glyph_data& custom_font::lookup_glyph(const ligature_key& key) const throw()
|
||||
{
|
||||
static font_glyph_data empty_glyph;
|
||||
auto i = glyphs.find(key);
|
||||
return (i == glyphs.end()) ? empty_glyph : i->second;
|
||||
}
|
130
src/lua/gui-text-cf.cpp
Normal file
130
src/lua/gui-text-cf.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
#include "lua/internal.hpp"
|
||||
#include "fonts/wrapper.hpp"
|
||||
#include "library/framebuffer.hpp"
|
||||
#include "library/customfont.hpp"
|
||||
#include "library/utf8.hpp"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
struct lua_customfont
|
||||
{
|
||||
public:
|
||||
lua_customfont(lua_State* LS, const std::string& filename);
|
||||
~lua_customfont() throw();
|
||||
int draw(lua_State* LS);
|
||||
const custom_font& get_font() { return font; }
|
||||
private:
|
||||
custom_font font;
|
||||
};
|
||||
}
|
||||
|
||||
DECLARE_LUACLASS(lua_customfont, "CUSTOMFONT");
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<uint32_t> decode_utf8(const std::string& t)
|
||||
{
|
||||
std::vector<uint32_t> x;
|
||||
size_t ts = t.length();
|
||||
uint16_t s = utf8_initial_state;
|
||||
for(size_t i = 0; i <= ts; i++) {
|
||||
int ch = (i < ts) ? (int)(unsigned char)t[i] : -1;
|
||||
int32_t cp = utf8_parse_byte(ch, s);
|
||||
if(cp >= 0)
|
||||
x.push_back(cp);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
struct render_object_text_cf : public render_object
|
||||
{
|
||||
render_object_text_cf(int32_t _x, int32_t _y, const std::string& _text, premultiplied_color _fg,
|
||||
premultiplied_color _bg, lua_obj_pin<lua_customfont>* _font) throw()
|
||||
: x(_x), y(_y), text(_text), fg(_fg), bg(_bg), font(_font) {}
|
||||
~render_object_text_cf() throw() {}
|
||||
template<bool X> void op(struct framebuffer<X>& scr) throw()
|
||||
{
|
||||
fg.set_palette(scr);
|
||||
bg.set_palette(scr);
|
||||
const custom_font& fdata = font->object()->get_font();
|
||||
std::vector<uint32_t> _text = decode_utf8(text);
|
||||
int32_t orig_x = x;
|
||||
int32_t drawx = x;
|
||||
int32_t drawy = y;
|
||||
for(size_t i = 0; i < _text.size();) {
|
||||
uint32_t cp = _text[i];
|
||||
ligature_key k = fdata.best_ligature_match(_text, i);
|
||||
const font_glyph_data& 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);
|
||||
drawx += glyph.width;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
void operator()(struct framebuffer<true>& scr) throw() { op(scr); }
|
||||
void operator()(struct framebuffer<false>& scr) throw() { op(scr); }
|
||||
private:
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
premultiplied_color fg;
|
||||
premultiplied_color bg;
|
||||
std::string text;
|
||||
lua_obj_pin<lua_customfont>* font;
|
||||
};
|
||||
|
||||
lua_customfont::lua_customfont(lua_State* LS, const std::string& filename)
|
||||
: font(filename)
|
||||
{
|
||||
static bool done = false;
|
||||
if(!done) {
|
||||
objclass<lua_customfont>().bind(LS, "__call", &lua_customfont::draw);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
lua_customfont::~lua_customfont() throw() {}
|
||||
|
||||
int lua_customfont::draw(lua_State* LS)
|
||||
{
|
||||
std::string fname = "CUSTOMFONT::__call";
|
||||
if(!lua_render_ctx)
|
||||
return 0;
|
||||
int64_t fgc = 0xFFFFFFU;
|
||||
int64_t bgc = -1;
|
||||
int32_t _x = get_numeric_argument<int32_t>(LS, 2, fname.c_str());
|
||||
int32_t _y = get_numeric_argument<int32_t>(LS, 3, fname.c_str());
|
||||
get_numeric_argument<int64_t>(LS, 5, fgc, fname.c_str());
|
||||
get_numeric_argument<int64_t>(LS, 6, bgc, fname.c_str());
|
||||
std::string text = get_string_argument(LS, 4, fname.c_str());
|
||||
auto f = lua_class<lua_customfont>::pin(LS, 1, fname.c_str());
|
||||
premultiplied_color fg(fgc);
|
||||
premultiplied_color bg(bgc);
|
||||
lua_render_ctx->queue->create_add<render_object_text_cf>(_x, _y, text, fg, bg, f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
function_ptr_luafun gui_text_cf("gui.loadfont", [](lua_State* LS, const std::string& fname) -> int {
|
||||
std::string filename = get_string_argument(LS, 1, fname.c_str());
|
||||
try {
|
||||
lua_customfont* b = lua_class<lua_customfont>::create(LS, LS, filename);
|
||||
return 1;
|
||||
} catch(std::exception& e) {
|
||||
lua_pushstring(LS, e.what());
|
||||
lua_error(LS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
Loading…
Add table
Reference in a new issue