Merge branch 'rr1-maint'

Conflicts:
	manual.lyx
	manual.txt
This commit is contained in:
Ilari Liusvaara 2013-02-01 21:54:32 +02:00
commit 531c0d0635
6 changed files with 491 additions and 0 deletions

BIN
data/verysmall.font Normal file

Binary file not shown.

View 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

View file

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

View file

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