mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-19 15:43:56 +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:
committed by
Christian Helmuth
parent
c5066a7317
commit
81e55e8901
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;
|
||||
}
|
Reference in New Issue
Block a user