mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-30 10:38:55 +00:00
gems: TrueType VFS plugin
This commit introduces a VFS plugin that exposes the glyphs and metadata of a TrueType font as a pseudo file system. The TTF font data is obtained from the VFS. The resulting pseudo file system is a directory that contains the files 'glyphs', 'baseline', 'max_width', and 'max_height'. The counter part of the plugin is the 'Vfs_font' class that implements the 'Text_painter::Font' interface by accessing the pseudo file system as provided by the TTF VFS plugin. Fixes #2740
This commit is contained in:
parent
c5066a7317
commit
81e55e8901
270
repos/gems/include/gems/cached_font.h
Normal file
270
repos/gems/include/gems/cached_font.h
Normal file
@ -0,0 +1,270 @@
|
||||
/*
|
||||
* \brief Glyph cache
|
||||
* \author Norman Feske
|
||||
* \date 2018-03-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__GEMS__CACHED_FONT_T_
|
||||
#define _INCLUDE__GEMS__CACHED_FONT_T_
|
||||
|
||||
#include <base/output.h>
|
||||
#include <base/allocator.h>
|
||||
#include <nitpicker_gfx/text_painter.h>
|
||||
|
||||
namespace Genode { class Cached_font; }
|
||||
|
||||
|
||||
class Genode::Cached_font : public Text_painter::Font
|
||||
{
|
||||
public:
|
||||
|
||||
struct Stats
|
||||
{
|
||||
unsigned misses;
|
||||
unsigned hits;
|
||||
unsigned consumed_bytes;
|
||||
|
||||
void print(Output &out) const
|
||||
{
|
||||
Genode::print(out, "used: ", consumed_bytes/1024, " KiB, "
|
||||
"hits: ", hits, ", misses: ", misses);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
typedef Text_painter::Area Area;
|
||||
typedef Text_painter::Font Font;
|
||||
typedef Text_painter::Glyph Glyph;
|
||||
|
||||
struct Time { unsigned value; };
|
||||
|
||||
Allocator &_alloc;
|
||||
Font const &_font;
|
||||
size_t const _limit;
|
||||
Time mutable _now { 0 };
|
||||
Stats mutable _stats { };
|
||||
|
||||
class Cached_glyph : Avl_node<Cached_glyph>
|
||||
{
|
||||
private:
|
||||
|
||||
friend class Avl_node<Cached_glyph>;
|
||||
friend class Avl_tree<Cached_glyph>;
|
||||
friend class Cached_font;
|
||||
|
||||
Codepoint const _codepoint;
|
||||
Glyph const _glyph;
|
||||
Time _last_used;
|
||||
|
||||
Glyph::Opacity _values[];
|
||||
|
||||
bool _higher(Codepoint const other) const
|
||||
{
|
||||
return _codepoint.value > other.value;
|
||||
}
|
||||
|
||||
unsigned _importance(Time now) const
|
||||
{
|
||||
return now.value - _last_used.value;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Cached_glyph(Codepoint c, Glyph const &glyph, Time now)
|
||||
:
|
||||
_codepoint(c),
|
||||
_glyph({ .width = glyph.width,
|
||||
.height = glyph.height,
|
||||
.vpos = glyph.vpos,
|
||||
.advance = glyph.advance,
|
||||
.values = _values }),
|
||||
_last_used(now)
|
||||
{
|
||||
for (unsigned i = 0; i < glyph.num_values(); i++)
|
||||
_values[i] = glyph.values[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Avl_node interface
|
||||
*/
|
||||
bool higher(Cached_glyph *c) { return _higher(c->_codepoint); }
|
||||
|
||||
Cached_glyph *find_by_codepoint(Codepoint requested)
|
||||
{
|
||||
if (_codepoint.value == requested.value) return this;
|
||||
|
||||
Cached_glyph *c = Avl_node<Cached_glyph>::child(_higher(requested));
|
||||
|
||||
return c ? c->find_by_codepoint(requested) : nullptr;
|
||||
}
|
||||
|
||||
Cached_glyph *find_least_recently_used(Time now)
|
||||
{
|
||||
Cached_glyph *result = this;
|
||||
|
||||
for (unsigned i = 0; i < 2; i++) {
|
||||
Cached_glyph *c = Avl_node<Cached_glyph>::child(i);
|
||||
if (c && c->_importance(now) > result->_importance(now))
|
||||
result = c;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void mark_as_used(Time now) { _last_used = now; }
|
||||
|
||||
void apply(Font::Apply_fn const &fn) const { fn.apply(_glyph); }
|
||||
};
|
||||
|
||||
Avl_tree<Cached_glyph> mutable _avl_tree { };
|
||||
|
||||
/**
|
||||
* Size of one cache entry in bytes
|
||||
*/
|
||||
size_t const _alloc_size = sizeof(Cached_glyph)
|
||||
+ 4*_font.bounding_box().count();
|
||||
|
||||
/**
|
||||
* Add cache entry for the given glyph
|
||||
*
|
||||
* \throw Out_of_ram
|
||||
* \throw Out_of_caps
|
||||
*/
|
||||
void _insert(Codepoint codepoint, Glyph const &glyph)
|
||||
{
|
||||
auto const cached_glyph_ptr = (Cached_glyph *)_alloc.alloc(_alloc_size);
|
||||
|
||||
_stats.consumed_bytes += _alloc_size;
|
||||
|
||||
memset(cached_glyph_ptr, 0, _alloc_size);
|
||||
|
||||
construct_at<Cached_glyph>(cached_glyph_ptr, codepoint, glyph, _now);
|
||||
|
||||
_avl_tree.insert(cached_glyph_ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evict glyph from cache
|
||||
*/
|
||||
void _remove(Cached_glyph &glyph)
|
||||
{
|
||||
_avl_tree.remove(&glyph);
|
||||
|
||||
glyph.~Cached_glyph();
|
||||
|
||||
_alloc.free(&glyph, _alloc_size);
|
||||
|
||||
_stats.consumed_bytes -= _alloc_size;
|
||||
}
|
||||
|
||||
Cached_glyph *_find_by_codepoint(Codepoint codepoint)
|
||||
{
|
||||
if (!_avl_tree.first())
|
||||
return nullptr;
|
||||
|
||||
return _avl_tree.first()->find_by_codepoint(codepoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evice least recently used glyph from cache
|
||||
*
|
||||
* \return true if a glyph was released
|
||||
*/
|
||||
bool _remove_least_recently_used()
|
||||
{
|
||||
if (!_avl_tree.first())
|
||||
return false;
|
||||
|
||||
Cached_glyph *glyph = _avl_tree.first()->find_least_recently_used(_now);
|
||||
if (!glyph)
|
||||
return false; /* this should never happen */
|
||||
|
||||
_remove(*glyph);
|
||||
_stats.misses++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void _remove_all()
|
||||
{
|
||||
while (Cached_glyph *glyph_ptr = _avl_tree.first())
|
||||
_remove(*glyph_ptr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
struct Limit { size_t value; };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc backing store for cached glyphs
|
||||
* \param font original (uncached) font
|
||||
* \param limit maximum cache size in bytes
|
||||
*/
|
||||
Cached_font(Allocator &alloc, Font const &font, Limit limit)
|
||||
:
|
||||
_alloc(alloc), _font(font), _limit(limit.value)
|
||||
{ }
|
||||
|
||||
~Cached_font() { _remove_all(); }
|
||||
|
||||
Stats stats() const { return _stats; }
|
||||
|
||||
void _apply_glyph(Codepoint c, Apply_fn const &fn) const override
|
||||
{
|
||||
_now.value++;
|
||||
|
||||
/*
|
||||
* Try to lookup glyph from the cache. If it is missing, fill cache
|
||||
* with requested glyph and repeat the lookup. When under memory
|
||||
* pressure, flush least recently used glyphs from cache.
|
||||
*
|
||||
* Even though '_apply_glyph' is a const method, the internal cache
|
||||
* and stats must of course be mutated. Hence the 'const_cast'.
|
||||
*/
|
||||
Cached_font &mutable_this = const_cast<Cached_font &>(*this);
|
||||
|
||||
/* retry once after handling a cache miss */
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
if (Cached_glyph *glyph_ptr = mutable_this._find_by_codepoint(c)) {
|
||||
glyph_ptr->apply(fn);
|
||||
glyph_ptr->mark_as_used(_now);
|
||||
_stats.hits += (i == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
while (_stats.consumed_bytes + _alloc_size > _limit)
|
||||
if (!mutable_this._remove_least_recently_used())
|
||||
break;
|
||||
|
||||
_font.apply_glyph(c, [&] (Glyph const &glyph) {
|
||||
mutable_this._insert(c, glyph); });
|
||||
}
|
||||
}
|
||||
|
||||
Advance_info advance_info(Codepoint c) const override
|
||||
{
|
||||
unsigned width = 0;
|
||||
Text_painter::Fixpoint_number advance { 0 };
|
||||
|
||||
/* go through the '_apply_glyph' cache-fill mechanism */
|
||||
Font::apply_glyph(c, [&] (Glyph const &glyph) {
|
||||
width = glyph.width, advance = glyph.advance; });
|
||||
|
||||
return Advance_info { .width = width, .advance = advance };
|
||||
}
|
||||
|
||||
unsigned baseline() const override { return _font.baseline(); }
|
||||
|
||||
Area bounding_box() const override { return _font.bounding_box(); }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__GEMS__CACHED_FONT_T_ */
|
169
repos/gems/include/gems/vfs_font.h
Normal file
169
repos/gems/include/gems/vfs_font.h
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* \brief Implementation of 'Text_painter::Font' for VFS-mounted fonts
|
||||
* \author Norman Feske
|
||||
* \date 2018-03-26
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE__GEMS__VFS_FONT_T_
|
||||
#define _INCLUDE__GEMS__VFS_FONT_T_
|
||||
|
||||
#include <gems/vfs.h>
|
||||
#include <nitpicker_gfx/text_painter.h>
|
||||
|
||||
namespace Genode { class Vfs_font; }
|
||||
|
||||
|
||||
class Genode::Vfs_font : public Text_painter::Font
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Glyph_painter::Glyph Glyph;
|
||||
|
||||
static constexpr Vfs::file_size GLYPH_SLOT_BYTES = 64*1024;
|
||||
|
||||
class Glyph_header
|
||||
{
|
||||
private:
|
||||
|
||||
uint8_t _width = 0;
|
||||
uint8_t _height = 0;
|
||||
uint8_t _vpos = 0;
|
||||
int8_t _advance_decimal = 0;
|
||||
uint8_t _advance_fractional = 0;
|
||||
uint8_t _reserved[3] { };
|
||||
|
||||
Glyph::Opacity _values[];
|
||||
|
||||
float _advance() const
|
||||
{
|
||||
float value = 256.0*_advance_decimal + _advance_fractional;
|
||||
return value/256;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Glyph_header(Glyph const &glyph)
|
||||
:
|
||||
_width ((uint8_t)min(255U, glyph.width)),
|
||||
_height((uint8_t)min(255U, glyph.height)),
|
||||
_vpos ((uint8_t)min(255U, glyph.vpos)),
|
||||
_advance_decimal((int8_t)max(-127, min(127, glyph.advance.decimal()))),
|
||||
_advance_fractional((uint8_t)glyph.advance.value & 0xff)
|
||||
{ }
|
||||
|
||||
Glyph_header() { }
|
||||
|
||||
Glyph glyph() const { return Glyph { .width = _width,
|
||||
.height = _height,
|
||||
.vpos = _vpos,
|
||||
.advance = _advance(),
|
||||
.values = _values }; }
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
private:
|
||||
|
||||
typedef Text_painter::Codepoint Codepoint;
|
||||
typedef Text_painter::Area Area;
|
||||
typedef Directory::Path Path;
|
||||
|
||||
Directory const _font_dir;
|
||||
unsigned const _baseline;
|
||||
Area const _bounding_box;
|
||||
|
||||
struct Glyph_buffer
|
||||
{
|
||||
Allocator &_alloc;
|
||||
|
||||
unsigned const num_bytes;
|
||||
|
||||
Glyph_header &header;
|
||||
|
||||
Glyph_buffer(Allocator &alloc, Area size)
|
||||
:
|
||||
_alloc(alloc), num_bytes(sizeof(Glyph_header) + size.count()*4),
|
||||
header(*(Glyph_header *)alloc.alloc(num_bytes))
|
||||
{ }
|
||||
|
||||
~Glyph_buffer() { _alloc.free(&header, num_bytes); }
|
||||
|
||||
char *ptr() { return (char *)&header; }
|
||||
};
|
||||
|
||||
Glyph_buffer mutable _buffer;
|
||||
|
||||
Readonly_file _glyphs_file;
|
||||
|
||||
template <typename T, unsigned MAX_LEN = 128>
|
||||
static T _value_from_file(Directory const &dir, Path const &path,
|
||||
T const &default_value)
|
||||
{
|
||||
T result = default_value;
|
||||
try {
|
||||
Readonly_file const file(dir, path);
|
||||
char buf[MAX_LEN + 1] { };
|
||||
if (file.read(buf, sizeof(buf)) <= MAX_LEN)
|
||||
if (ascii_to(buf, result))
|
||||
return result;
|
||||
}
|
||||
catch (Readonly_file::Open_failed) { }
|
||||
return default_value;
|
||||
}
|
||||
|
||||
static Readonly_file::At _file_pos(Codepoint c)
|
||||
{
|
||||
return Readonly_file::At{(Vfs::file_size)c.value*GLYPH_SLOT_BYTES};
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
struct Unavailable : Exception { };
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param alloc allocator for glyph buffer
|
||||
* \param dir directory
|
||||
* \param path path to font
|
||||
*
|
||||
* \throw Unavailable unable to obtain font data
|
||||
*/
|
||||
Vfs_font(Allocator &alloc, Directory const &dir, Path const &path)
|
||||
:
|
||||
_font_dir(dir, path),
|
||||
_baseline(_value_from_file(_font_dir, "baseline", 0U)),
|
||||
_bounding_box(_value_from_file(_font_dir, "max_width", 0U),
|
||||
_value_from_file(_font_dir, "max_height", 0U)),
|
||||
_buffer(alloc, _bounding_box),
|
||||
_glyphs_file(_font_dir, "glyphs")
|
||||
{ }
|
||||
|
||||
void _apply_glyph(Codepoint c, Apply_fn const &fn) const override
|
||||
{
|
||||
_glyphs_file.read(_file_pos(c), _buffer.ptr(), _buffer.num_bytes);
|
||||
|
||||
fn.apply(_buffer.header.glyph());
|
||||
}
|
||||
|
||||
Advance_info advance_info(Codepoint c) const override
|
||||
{
|
||||
_glyphs_file.read(_file_pos(c), _buffer.ptr(), sizeof(Glyph_header));
|
||||
|
||||
Glyph const glyph = _buffer.header.glyph();
|
||||
|
||||
return Advance_info { .width = glyph.width, .advance = glyph.advance };
|
||||
}
|
||||
|
||||
unsigned baseline() const override { return _baseline; }
|
||||
|
||||
Area bounding_box() const override { return _bounding_box; }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__GEMS__TTF_FONT_T_ */
|
9
repos/gems/lib/mk/vfs_ttf.mk
Normal file
9
repos/gems/lib/mk/vfs_ttf.mk
Normal file
@ -0,0 +1,9 @@
|
||||
SRC_CC = vfs.cc
|
||||
|
||||
INC_DIR += $(REP_DIR)/src/lib/vfs/ttf
|
||||
|
||||
LIBS += ttf_font
|
||||
|
||||
vpath %.cc $(REP_DIR)/src/lib/vfs/ttf
|
||||
|
||||
SHARED_LIB = yes
|
16
repos/gems/recipes/src/vfs_ttf/content.mk
Normal file
16
repos/gems/recipes/src/vfs_ttf/content.mk
Normal file
@ -0,0 +1,16 @@
|
||||
MIRROR_FROM_REP_DIR := lib/mk/vfs_ttf.mk lib/mk/ttf_font.mk \
|
||||
src/lib/vfs/ttf src/lib/ttf_font
|
||||
|
||||
STB_PORT_DIR := $(call port_dir,$(GENODE_DIR)/repos/libports/ports/stb)
|
||||
|
||||
content: $(MIRROR_FROM_REP_DIR) include/stb_truetype.h LICENSE
|
||||
|
||||
include/stb_truetype.h:
|
||||
mkdir -p $(dir $@)
|
||||
cp -r $(STB_PORT_DIR)/include/stb_truetype.h $@
|
||||
|
||||
$(MIRROR_FROM_REP_DIR):
|
||||
$(mirror_from_rep_dir)
|
||||
|
||||
LICENSE:
|
||||
cp $(GENODE_DIR)/LICENSE $@
|
1
repos/gems/recipes/src/vfs_ttf/hash
Normal file
1
repos/gems/recipes/src/vfs_ttf/hash
Normal file
@ -0,0 +1 @@
|
||||
2018-03-30 89b39a8703dbb8b098068d2ec4f73b4705debb44
|
7
repos/gems/recipes/src/vfs_ttf/used_apis
Normal file
7
repos/gems/recipes/src/vfs_ttf/used_apis
Normal file
@ -0,0 +1,7 @@
|
||||
base
|
||||
os
|
||||
so
|
||||
libc
|
||||
vfs
|
||||
gems
|
||||
nitpicker_gfx
|
154
repos/gems/src/lib/vfs/ttf/glyphs_file_system.h
Normal file
154
repos/gems/src/lib/vfs/ttf/glyphs_file_system.h
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* \brief Glyphs file system
|
||||
* \author Norman Feske
|
||||
* \date 2018-03-26
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _GLYPHS_FILE_SYSTEM_H_
|
||||
#define _GLYPHS_FILE_SYSTEM_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <vfs/single_file_system.h>
|
||||
#include <nitpicker_gfx/text_painter.h>
|
||||
|
||||
/* gems includes */
|
||||
#include <gems/vfs_font.h>
|
||||
|
||||
namespace Vfs {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
class Glyphs_file_system;
|
||||
|
||||
typedef Text_painter::Font Font;
|
||||
typedef Vfs_font::Glyph_header Glyph_header;
|
||||
}
|
||||
|
||||
|
||||
class Vfs::Glyphs_file_system : public Vfs::Single_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
static constexpr unsigned UNICODE_MAX = 0x10ffff;
|
||||
|
||||
static constexpr file_size FILE_SIZE = Vfs_font::GLYPH_SLOT_BYTES*(UNICODE_MAX + 1);
|
||||
|
||||
Font const &_font;
|
||||
|
||||
struct Vfs_handle : Single_vfs_handle
|
||||
{
|
||||
Font const &_font;
|
||||
|
||||
Vfs_handle(Directory_service &ds,
|
||||
File_io_service &fs,
|
||||
Allocator &alloc,
|
||||
Font const &font)
|
||||
:
|
||||
Single_vfs_handle(ds, fs, alloc, 0), _font(font)
|
||||
{ }
|
||||
|
||||
Read_result read(char *dst, file_size count,
|
||||
file_size &out_count) override
|
||||
{
|
||||
out_count = 0;
|
||||
|
||||
if (seek() > FILE_SIZE)
|
||||
return READ_ERR_INVALID;
|
||||
|
||||
Codepoint const codepoint { (uint32_t)(seek() / Vfs_font::GLYPH_SLOT_BYTES) };
|
||||
|
||||
file_size byte_offset = seek() % Vfs_font::GLYPH_SLOT_BYTES;
|
||||
|
||||
_font.apply_glyph(codepoint, [&] (Glyph_painter::Glyph const &glyph) {
|
||||
|
||||
if (byte_offset < sizeof(Glyph_header)) {
|
||||
|
||||
Glyph_header const header(glyph);
|
||||
|
||||
char const * const src = (char const *)&header + byte_offset;
|
||||
size_t const len = min(sizeof(header) - byte_offset, count);
|
||||
memcpy(dst, src, len);
|
||||
|
||||
dst += len;
|
||||
byte_offset += len;
|
||||
count -= len;
|
||||
out_count += len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given that 'byte_offset' is at least 'sizeof(header)',
|
||||
* continue working with 'alpha_offset', which is the first
|
||||
* offset of interest within the array of alpha values.
|
||||
*/
|
||||
size_t const alpha_offset = byte_offset - sizeof(Glyph_header);
|
||||
size_t const alpha_values_len = 4*glyph.width*glyph.height;
|
||||
|
||||
if (alpha_offset < alpha_values_len) {
|
||||
char const * const src = (char const *)glyph.values + alpha_offset;
|
||||
size_t const len = min(alpha_values_len - alpha_offset, count);
|
||||
memcpy(dst, src, len);
|
||||
out_count += len;
|
||||
}
|
||||
});
|
||||
|
||||
return READ_OK;
|
||||
}
|
||||
|
||||
Write_result write(char const *, file_size, file_size &) override
|
||||
{
|
||||
return WRITE_ERR_IO;
|
||||
}
|
||||
|
||||
bool read_ready() override { return true; }
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
Glyphs_file_system(Font const &font)
|
||||
:
|
||||
Single_file_system(NODE_TYPE_CHAR_DEVICE, type(), Xml_node("<glyphs/>")),
|
||||
_font(font)
|
||||
{ }
|
||||
|
||||
static char const *type_name() { return "glyphs"; }
|
||||
|
||||
char const *type() override { return type_name(); }
|
||||
|
||||
|
||||
/*********************************
|
||||
** Directory-service interface **
|
||||
*********************************/
|
||||
|
||||
Open_result open(char const *path, unsigned,
|
||||
Vfs::Vfs_handle **out_handle,
|
||||
Allocator &alloc) override
|
||||
{
|
||||
if (!_single_file(path))
|
||||
return OPEN_ERR_UNACCESSIBLE;
|
||||
|
||||
try {
|
||||
*out_handle = new (alloc)
|
||||
Vfs_handle(*this, *this, alloc, _font);
|
||||
return OPEN_OK;
|
||||
}
|
||||
catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; }
|
||||
catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; }
|
||||
}
|
||||
|
||||
Stat_result stat(char const *path, Stat &out) override
|
||||
{
|
||||
Stat_result result = Single_file_system::stat(path, out);
|
||||
out.mode |= 0444;
|
||||
out.size = FILE_SIZE;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _GLYPHS_FILE_SYSTEM_H_ */
|
2
repos/gems/src/lib/vfs/ttf/target.mk
Normal file
2
repos/gems/src/lib/vfs/ttf/target.mk
Normal file
@ -0,0 +1,2 @@
|
||||
TARGET = dummy-vfs_ttf
|
||||
LIBS = vfs_ttf
|
173
repos/gems/src/lib/vfs/ttf/vfs.cc
Normal file
173
repos/gems/src/lib/vfs/ttf/vfs.cc
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* \brief Truetype font file system
|
||||
* \author Norman Feske
|
||||
* \date 2018-03-07
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <vfs/dir_file_system.h>
|
||||
#include <vfs/readonly_value_file_system.h>
|
||||
|
||||
/* gems includes */
|
||||
#include <gems/ttf_font.h>
|
||||
#include <gems/vfs.h>
|
||||
#include <gems/cached_font.h>
|
||||
|
||||
/* local includes */
|
||||
#include <glyphs_file_system.h>
|
||||
|
||||
namespace Vfs_ttf {
|
||||
|
||||
using namespace Vfs;
|
||||
using namespace Genode;
|
||||
|
||||
class Font_from_file;
|
||||
class Local_factory;
|
||||
class File_system;
|
||||
|
||||
struct Dummy_io_response_handler : Vfs::Io_response_handler
|
||||
{
|
||||
void handle_io_response(Vfs::Vfs_handle::Context *) override { };
|
||||
};
|
||||
|
||||
typedef Text_painter::Font Font;
|
||||
}
|
||||
|
||||
|
||||
struct Vfs_ttf::Font_from_file
|
||||
{
|
||||
typedef Directory::Path Path;
|
||||
|
||||
Directory const _dir;
|
||||
File_content const _content;
|
||||
|
||||
Constructible<Ttf_font const> _font { };
|
||||
|
||||
/*
|
||||
* Each slot of the glyphs file is 64 KiB, which limits the maximum glyph
|
||||
* size to 128x128. We cap the size at 100px to prevent cut-off glyphs.
|
||||
*/
|
||||
static constexpr float MAX_SIZE_PX = 100.0;
|
||||
|
||||
Font_from_file(Vfs::File_system &root, Entrypoint &ep, Allocator &alloc,
|
||||
Path const &file_path, float px)
|
||||
:
|
||||
_dir(root, ep, alloc),
|
||||
_content(alloc, _dir, file_path, File_content::Limit{10*1024*1024})
|
||||
{
|
||||
_content.bytes([&] (char const *ptr, size_t) {
|
||||
_font.construct(alloc, ptr, min(px, MAX_SIZE_PX)); });
|
||||
}
|
||||
|
||||
Font const &font() const { return *_font; }
|
||||
};
|
||||
|
||||
|
||||
struct Vfs_ttf::Local_factory : File_system_factory
|
||||
{
|
||||
Font_from_file _font;
|
||||
Cached_font::Limit _cache_limit;
|
||||
Cached_font _cached_font;
|
||||
Glyphs_file_system _glyphs_fs;
|
||||
Readonly_value_file_system<unsigned> _baseline_fs;
|
||||
Readonly_value_file_system<unsigned> _max_width_fs;
|
||||
Readonly_value_file_system<unsigned> _max_height_fs;
|
||||
|
||||
Local_factory(Env &env, Allocator &alloc, Xml_node node,
|
||||
Vfs::File_system &root_dir)
|
||||
:
|
||||
_font(root_dir, env.ep(), alloc,
|
||||
node.attribute_value("path", Directory::Path()),
|
||||
node.attribute_value("size_px", 16.0)),
|
||||
_cache_limit({node.attribute_value("cache", Number_of_bytes())}),
|
||||
_cached_font(alloc, _font.font(), _cache_limit),
|
||||
_glyphs_fs (_cached_font),
|
||||
_baseline_fs ("baseline", _font.font().baseline()),
|
||||
_max_width_fs ("max_width", _font.font().bounding_box().w()),
|
||||
_max_height_fs("max_height", _font.font().bounding_box().h())
|
||||
{ }
|
||||
|
||||
Vfs::File_system *create(Env &, Allocator &, Xml_node node,
|
||||
Io_response_handler &, Vfs::File_system &) override
|
||||
{
|
||||
if (node.has_type(Glyphs_file_system::type_name()))
|
||||
return &_glyphs_fs;
|
||||
|
||||
if (node.has_type(Readonly_value_file_system<unsigned>::type_name()))
|
||||
return _baseline_fs.matches(node) ? &_baseline_fs
|
||||
: _max_width_fs.matches(node) ? &_max_width_fs
|
||||
: _max_height_fs.matches(node) ? &_max_height_fs
|
||||
: nullptr;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Vfs_ttf::File_system : private Local_factory,
|
||||
private Dummy_io_response_handler,
|
||||
public Vfs::Dir_file_system
|
||||
{
|
||||
private:
|
||||
|
||||
typedef String<200> Config;
|
||||
static Config _config(Xml_node node)
|
||||
{
|
||||
char buf[Config::capacity()] { };
|
||||
|
||||
Xml_generator xml(buf, sizeof(buf), "dir", [&] () {
|
||||
typedef String<64> Name;
|
||||
xml.attribute("name", node.attribute_value("name", Name()));
|
||||
xml.node("glyphs", [&] () { });
|
||||
xml.node("readonly_value", [&] () { xml.attribute("name", "baseline"); });
|
||||
xml.node("readonly_value", [&] () { xml.attribute("name", "max_width"); });
|
||||
xml.node("readonly_value", [&] () { xml.attribute("name", "max_height"); });
|
||||
});
|
||||
return Config(Cstring(buf));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
File_system(Env &env, Allocator &alloc, Xml_node node,
|
||||
Vfs::File_system &root_dir)
|
||||
:
|
||||
Local_factory(env, alloc, node, root_dir),
|
||||
Vfs::Dir_file_system(env, alloc,
|
||||
Xml_node(_config(node).string()),
|
||||
*this, *this, root_dir)
|
||||
{ }
|
||||
|
||||
char const *type() override { return "ttf"; }
|
||||
};
|
||||
|
||||
|
||||
/**************************
|
||||
** VFS plugin interface **
|
||||
**************************/
|
||||
|
||||
extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
|
||||
{
|
||||
struct Factory : Vfs::File_system_factory
|
||||
{
|
||||
Vfs::File_system *create(Genode::Env &env, Genode::Allocator &alloc,
|
||||
Genode::Xml_node node,
|
||||
Vfs::Io_response_handler &,
|
||||
Vfs::File_system &root_dir) override
|
||||
{
|
||||
try {
|
||||
return new (alloc) Vfs_ttf::File_system(env, alloc, node, root_dir); }
|
||||
catch (...) { }
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
static Factory factory;
|
||||
return &factory;
|
||||
}
|
Loading…
Reference in New Issue
Block a user