90 lines
2.6 KiB
C++
90 lines
2.6 KiB
C++
#include <psemek/fonts/monospace_font.hpp>
|
|
|
|
#include <psemek/util/unicode.hpp>
|
|
#include <psemek/util/exception.hpp>
|
|
|
|
namespace psemek::fonts
|
|
{
|
|
|
|
monospace_font::monospace_font(character_range range, std::string_view name, geom::vector<int, 2> size, gfx::texture_2d atlas, std::vector<geom::box<float, 2>> texcoords)
|
|
: range_{range}
|
|
, name_{name}
|
|
, size_{size}
|
|
, atlas_{std::move(atlas)}
|
|
, texcoords_{std::move(texcoords)}
|
|
{
|
|
if (range_.end - range_.begin != texcoords_.size())
|
|
throw util::exception("Wrong number of texture coordinates");
|
|
|
|
if (!supports_character('?'))
|
|
throw util::exception("Monospace font must support '?' character");
|
|
}
|
|
|
|
static geom::vector<float, 2> advance_dir(shape_options::direction_t direction)
|
|
{
|
|
switch (direction)
|
|
{
|
|
case shape_options::left_to_right: return {1.f, 0.f};
|
|
case shape_options::right_to_left: return {-1.f, 0.f};
|
|
case shape_options::top_to_bottom: return {0.f, 1.f};
|
|
case shape_options::bottom_to_top: return {0.f, -1.f};
|
|
}
|
|
|
|
return {1.f, 0.f};
|
|
}
|
|
|
|
bool monospace_font::supports_character(char32_t c) const
|
|
{
|
|
return std::isspace(c) || (c >= range_.begin && c < range_.end);
|
|
}
|
|
|
|
std::vector<glyph> monospace_font::shape(std::string_view str, shape_options const & options, geom::point<float, 2> & pen) const
|
|
{
|
|
return shape_impl(util::utf8_range(str), options, pen);
|
|
}
|
|
|
|
std::vector<glyph> monospace_font::shape(std::u32string_view str, shape_options const & options, geom::point<float, 2> & pen) const
|
|
{
|
|
return shape_impl(str, options, pen);
|
|
}
|
|
|
|
template <typename String>
|
|
std::vector<glyph> monospace_font::shape_impl(String const & str, shape_options const & options, geom::point<float, 2> & pen) const
|
|
{
|
|
char32_t const unknown = supports_character(options.unknown_character) ? options.unknown_character : '?';
|
|
geom::vector<float, 2> const size = geom::cast<float>(this->size()) * options.scale;
|
|
geom::vector<float, 2> const advance = geom::pointwise_mult(advance_dir(options.direction), size);
|
|
|
|
std::vector<glyph> result;
|
|
|
|
for (char32_t c : str)
|
|
{
|
|
glyph g;
|
|
if (std::isspace(c))
|
|
{
|
|
g.character = c;
|
|
g.position = {{{pen[0], pen[0]}, {pen[1], pen[1]}}};
|
|
}
|
|
else
|
|
{
|
|
g.character = supports_character(c) ? c : unknown;
|
|
g.position = {{{pen[0], pen[0] + size[0]}, {pen[1], pen[1] + size[1]}}};
|
|
}
|
|
result.push_back(g);
|
|
pen += advance;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::optional<geom::box<float, 2>> monospace_font::texcoords(char32_t c) const
|
|
{
|
|
if (!supports_character(c)) return std::nullopt;
|
|
|
|
if (std::isspace(c))
|
|
return texcoords_[' ' - range_.begin];
|
|
|
|
return texcoords_[c - range_.begin];
|
|
}
|
|
|
|
}
|