Add new non-monospace font
This commit is contained in:
parent
68e8ece70a
commit
34e2877873
7 changed files with 295 additions and 7 deletions
|
|
@ -5,6 +5,4 @@ psemek_add_library(psemek-ui ${PSEMEK_UI_HEADERS} ${PSEMEK_UI_SOURCES})
|
||||||
target_include_directories(psemek-ui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
target_include_directories(psemek-ui PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
target_link_libraries(psemek-ui PUBLIC psemek-util psemek-log psemek-geom psemek-cg psemek-gfx psemek-async psemek-sdl2)
|
target_link_libraries(psemek-ui PUBLIC psemek-util psemek-log psemek-geom psemek-cg psemek-gfx psemek-async psemek-sdl2)
|
||||||
|
|
||||||
psemek_add_resources(psemek-ui
|
psemek_glob_resources(psemek-ui resources psemek/ui/resources)
|
||||||
resources/cross_red_16x16.png psemek/ui/resources/cross_red_16x16_png
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <psemek/geom/vector.hpp>
|
#include <psemek/geom/vector.hpp>
|
||||||
#include <psemek/geom/box.hpp>
|
#include <psemek/geom/box.hpp>
|
||||||
|
#include <psemek/util/span.hpp>
|
||||||
|
|
||||||
#include <psemek/gfx/texture.hpp>
|
#include <psemek/gfx/texture.hpp>
|
||||||
|
|
||||||
|
|
@ -54,7 +55,7 @@ namespace psemek::ui
|
||||||
virtual geom::vector<int, 2> size() const = 0;
|
virtual geom::vector<int, 2> size() const = 0;
|
||||||
|
|
||||||
virtual bool supports_character(char32_t c) const = 0;
|
virtual bool supports_character(char32_t c) const = 0;
|
||||||
virtual std::vector<character_range> supported_characters() const = 0;
|
virtual util::span<character_range const> supported_characters() const = 0;
|
||||||
|
|
||||||
virtual std::vector<glyph> shape(std::string_view str, shape_options const & options) const = 0;
|
virtual std::vector<glyph> shape(std::string_view str, shape_options const & options) const = 0;
|
||||||
|
|
||||||
|
|
@ -65,6 +66,7 @@ namespace psemek::ui
|
||||||
virtual ~font() {}
|
virtual ~font() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<font> make_default_monospace_9x12_font();
|
||||||
std::unique_ptr<font> make_default_9x12_font();
|
std::unique_ptr<font> make_default_9x12_font();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
49
libs/ui/include/psemek/ui/kerned_font.hpp
Normal file
49
libs/ui/include/psemek/ui/kerned_font.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <psemek/ui/font.hpp>
|
||||||
|
|
||||||
|
namespace psemek::ui
|
||||||
|
{
|
||||||
|
|
||||||
|
struct kerned_font
|
||||||
|
: font
|
||||||
|
{
|
||||||
|
struct glyph_data
|
||||||
|
{
|
||||||
|
int start_x;
|
||||||
|
int start_y;
|
||||||
|
int size_x;
|
||||||
|
int size_y;
|
||||||
|
int offset_x;
|
||||||
|
int offset_y;
|
||||||
|
int advance;
|
||||||
|
};
|
||||||
|
|
||||||
|
kerned_font(std::string_view name, geom::vector<int, 2> size, int baseline_offset, gfx::texture_2d atlas,
|
||||||
|
std::unordered_map<char32_t, glyph_data> glyphs);
|
||||||
|
|
||||||
|
font_type type() const override { return font_type::bitmap; }
|
||||||
|
|
||||||
|
std::string_view name() const override { return name_; }
|
||||||
|
|
||||||
|
geom::vector<int, 2> size() const override { return size_; }
|
||||||
|
|
||||||
|
bool supports_character(char32_t c) const override;
|
||||||
|
util::span<character_range const> supported_characters() const override { return ranges_; }
|
||||||
|
|
||||||
|
std::vector<glyph> shape(std::string_view str, shape_options const & options) const override;
|
||||||
|
|
||||||
|
gfx::texture_2d const & atlas() const override { return atlas_; };
|
||||||
|
|
||||||
|
std::optional<geom::box<float, 2>> texcoords(char32_t character) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<character_range> ranges_;
|
||||||
|
std::string_view name_;
|
||||||
|
geom::vector<int, 2> size_;
|
||||||
|
int baseline_offset_;
|
||||||
|
gfx::texture_2d atlas_;
|
||||||
|
std::unordered_map<char32_t, glyph_data> glyphs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ namespace psemek::ui
|
||||||
geom::vector<int, 2> size() const override { return size_; }
|
geom::vector<int, 2> size() const override { return size_; }
|
||||||
|
|
||||||
bool supports_character(char32_t c) const override;
|
bool supports_character(char32_t c) const override;
|
||||||
std::vector<character_range> supported_characters() const override { return {range_}; }
|
util::span<character_range const> supported_characters() const override { return {&range_, &range_ + 1}; }
|
||||||
|
|
||||||
std::vector<glyph> shape(std::string_view str, shape_options const & options) const override;
|
std::vector<glyph> shape(std::string_view str, shape_options const & options) const override;
|
||||||
|
|
||||||
|
|
|
||||||
96
libs/ui/resources/font_9x12_glyphs.txt
Normal file
96
libs/ui/resources/font_9x12_glyphs.txt
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
# <character-id> <texcoord-start-x> <texcoord-start-y> <size-x> <size-y> <offset-x> <offset-y> <advance>
|
||||||
|
32 0 0 0 0 0 0 6 # ' '
|
||||||
|
33 16 3 1 8 0 0 2 # !
|
||||||
|
34 25 3 5 3 0 5 6 # "
|
||||||
|
35 36 3 5 9 0 -1 6 # #
|
||||||
|
36 47 3 4 9 0 -1 5 # $
|
||||||
|
37 58 3 5 8 0 0 6 # %
|
||||||
|
38 69 5 5 6 0 0 6 # &
|
||||||
|
39 82 3 1 4 0 4 2 # '
|
||||||
|
40 93 2 2 10 0 -1 3 # (
|
||||||
|
41 103 2 2 10 0 -1 3 # )
|
||||||
|
42 113 3 5 5 0 3 6 # *
|
||||||
|
43 123 4 7 7 0 0 8 # +
|
||||||
|
44 136 9 3 4 0 -2 4 # ,
|
||||||
|
45 146 7 5 1 0 3 6 # -
|
||||||
|
46 158 9 2 2 0 0 3 # .
|
||||||
|
47 168 3 5 9 0 -1 6 # /
|
||||||
|
48 3 17 5 8 0 0 6 # 0
|
||||||
|
49 14 17 5 8 0 0 6 # 1
|
||||||
|
50 25 17 5 8 0 0 6 # 2
|
||||||
|
51 36 17 5 8 0 0 6 # 3
|
||||||
|
52 47 17 6 8 0 0 7 # 4
|
||||||
|
53 58 17 5 8 0 0 6 # 5
|
||||||
|
54 69 17 5 8 0 0 6 # 6
|
||||||
|
55 80 17 5 8 0 0 6 # 7
|
||||||
|
56 91 17 5 8 0 0 6 # 8
|
||||||
|
57 102 17 5 8 0 0 6 # 9
|
||||||
|
58 114 19 2 6 0 0 3 # :
|
||||||
|
59 125 19 3 7 0 -1 4 # ;
|
||||||
|
60 134 18 6 7 0 0 7 # <
|
||||||
|
61 146 20 5 3 0 2 6 # =
|
||||||
|
62 156 18 6 7 0 0 7 # >
|
||||||
|
63 169 18 4 7 0 0 5 # ?
|
||||||
|
64 3 30 5 10 0 -1 6 # @
|
||||||
|
65 13 31 7 8 0 0 8 # A
|
||||||
|
66 24 31 6 8 0 0 7 # B
|
||||||
|
67 36 31 5 8 0 0 6 # C
|
||||||
|
68 46 31 6 8 0 0 7 # D
|
||||||
|
69 57 31 6 8 0 0 7 # E
|
||||||
|
70 68 31 6 8 0 0 7 # F
|
||||||
|
71 80 31 6 8 0 0 7 # G
|
||||||
|
72 90 31 7 8 0 0 8 # H
|
||||||
|
73 102 31 5 8 0 0 6 # I
|
||||||
|
74 113 31 5 8 0 0 6 # J
|
||||||
|
75 123 31 7 8 0 0 8 # K
|
||||||
|
76 135 31 5 8 0 0 6 # L
|
||||||
|
77 145 31 7 8 0 0 8 # M
|
||||||
|
78 156 31 7 8 0 0 8 # N
|
||||||
|
79 168 31 5 8 0 0 6 # O
|
||||||
|
80 3 45 5 8 0 0 6 # P
|
||||||
|
81 14 45 5 9 0 -1 6 # Q
|
||||||
|
82 24 45 7 8 0 0 8 # R
|
||||||
|
83 36 45 5 8 0 0 6 # S
|
||||||
|
84 46 45 7 8 0 0 8 # T
|
||||||
|
85 57 45 7 8 0 0 8 # U
|
||||||
|
86 68 45 7 8 0 0 8 # V
|
||||||
|
87 78 45 7 8 0 0 8 # W
|
||||||
|
88 90 45 7 8 0 0 8 # X
|
||||||
|
89 101 45 7 8 0 0 8 # Y
|
||||||
|
90 113 45 5 8 0 0 6 # Z
|
||||||
|
91 125 44 3 10 0 -1 4 # [
|
||||||
|
92 135 45 4 9 0 -1 5 # \
|
||||||
|
93 147 44 3 10 0 -1 4 # ]
|
||||||
|
94 157 45 5 4 0 4 6 # ^
|
||||||
|
95 167 54 7 1 0 -2 8 # _
|
||||||
|
96 5 58 2 2 0 7 3 # `
|
||||||
|
97 14 61 6 6 0 0 7 # a
|
||||||
|
98 24 59 6 8 0 0 7 # b
|
||||||
|
99 36 61 5 6 0 0 6 # c
|
||||||
|
100 47 59 6 8 0 0 7 # d
|
||||||
|
101 58 61 5 6 0 0 6 # e
|
||||||
|
102 69 59 5 8 0 0 6 # f
|
||||||
|
103 80 61 6 8 0 -2 7 # g
|
||||||
|
104 90 59 7 8 0 0 8 # h
|
||||||
|
105 102 59 5 8 0 0 6 # i
|
||||||
|
106 113 59 4 10 0 -2 5 # j
|
||||||
|
107 123 59 6 8 0 0 7 # k
|
||||||
|
108 135 59 5 8 0 0 6 # l
|
||||||
|
109 145 61 7 6 0 0 8 # m
|
||||||
|
110 156 61 7 6 0 0 8 # n
|
||||||
|
111 168 61 5 6 0 0 6 # o
|
||||||
|
112 2 75 6 8 0 -2 7 # p
|
||||||
|
113 14 75 6 8 0 -2 7 # q
|
||||||
|
114 25 75 5 6 0 0 6 # r
|
||||||
|
115 36 75 5 6 0 0 6 # s
|
||||||
|
116 46 74 6 7 0 0 7 # t
|
||||||
|
117 57 75 7 6 0 0 8 # u
|
||||||
|
118 68 75 7 6 0 0 8 # v
|
||||||
|
119 79 75 7 6 0 0 8 # w
|
||||||
|
120 91 75 6 6 0 0 7 # x
|
||||||
|
121 101 75 7 8 0 -2 8 # y
|
||||||
|
122 113 75 5 6 0 0 6 # z
|
||||||
|
123 125 72 3 10 0 -1 4 # {
|
||||||
|
124 137 72 1 9 0 0 2 # |
|
||||||
|
125 147 72 3 10 0 -1 4 # }
|
||||||
|
126 157 73 5 2 0 6 6 # ~
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
#include <psemek/ui/font.hpp>
|
#include <psemek/ui/font.hpp>
|
||||||
#include <psemek/ui/monospace_font.hpp>
|
#include <psemek/ui/monospace_font.hpp>
|
||||||
|
#include <psemek/ui/kerned_font.hpp>
|
||||||
|
|
||||||
#include <psemek/gfx/resource/font_9x12_png.hpp>
|
#include <psemek/gfx/resource/font_9x12_png.hpp>
|
||||||
|
#include <psemek/ui/resources/font_9x12_glyphs_txt.hpp>
|
||||||
|
|
||||||
#include <psemek/io/memory_stream.hpp>
|
#include <psemek/io/memory_stream.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace psemek::ui
|
namespace psemek::ui
|
||||||
{
|
{
|
||||||
|
|
||||||
std::unique_ptr<font> make_default_9x12_font()
|
std::unique_ptr<font> make_default_monospace_9x12_font()
|
||||||
{
|
{
|
||||||
character_range range{32, 128};
|
character_range range{32, 128};
|
||||||
std::string_view name = "default_9x12";
|
std::string_view name = "default_monospace_9x12";
|
||||||
geom::vector<int, 2> size{9, 12};
|
geom::vector<int, 2> size{9, 12};
|
||||||
|
|
||||||
gfx::texture_2d atlas = gfx::texture_2d::from_pixmap(gfx::read_png_monochrome(io::memory_istream{gfx::resource::font_9x12_png}));
|
gfx::texture_2d atlas = gfx::texture_2d::from_pixmap(gfx::read_png_monochrome(io::memory_istream{gfx::resource::font_9x12_png}));
|
||||||
|
|
@ -40,4 +44,41 @@ namespace psemek::ui
|
||||||
return std::make_unique<monospace_font>(range, name, size, std::move(atlas), std::move(texcoords));
|
return std::make_unique<monospace_font>(range, name, size, std::move(atlas), std::move(texcoords));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<font> make_default_9x12_font()
|
||||||
|
{
|
||||||
|
std::string_view name = "default_9x12";
|
||||||
|
geom::vector<int, 2> size{9, 12};
|
||||||
|
|
||||||
|
gfx::texture_2d atlas = gfx::texture_2d::from_pixmap(gfx::read_png_monochrome(io::memory_istream{gfx::resource::font_9x12_png}));
|
||||||
|
atlas.nearest_filter();
|
||||||
|
atlas.clamp();
|
||||||
|
gl::TexParameteri(atlas.target, gl::TEXTURE_SWIZZLE_G, gl::RED);
|
||||||
|
gl::TexParameteri(atlas.target, gl::TEXTURE_SWIZZLE_B, gl::RED);
|
||||||
|
gl::TexParameteri(atlas.target, gl::TEXTURE_SWIZZLE_A, gl::RED);
|
||||||
|
|
||||||
|
std::unordered_map<char32_t, kerned_font::glyph_data> glyphs;
|
||||||
|
{
|
||||||
|
std::istringstream is{std::string(ui::resources::font_9x12_glyphs_txt)};
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(is, line))
|
||||||
|
{
|
||||||
|
if (auto pos = line.find('#'); pos != std::string::npos)
|
||||||
|
line = line.substr(0, pos);
|
||||||
|
|
||||||
|
if (line.empty()) continue;
|
||||||
|
|
||||||
|
std::istringstream is{std::move(line)};
|
||||||
|
|
||||||
|
int c;
|
||||||
|
kerned_font::glyph_data data;
|
||||||
|
|
||||||
|
is >> c >> data.start_x >> data.start_y >> data.size_x >> data.size_y >> data.offset_x >> data.offset_y >> data.advance;
|
||||||
|
glyphs[c] = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_unique<kerned_font>(name, size, 2, std::move(atlas), std::move(glyphs));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
102
libs/ui/source/kerned_font.cpp
Normal file
102
libs/ui/source/kerned_font.cpp
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
#include <psemek/ui/kerned_font.hpp>
|
||||||
|
|
||||||
|
#include <psemek/util/unicode.hpp>
|
||||||
|
|
||||||
|
namespace psemek::ui
|
||||||
|
{
|
||||||
|
|
||||||
|
kerned_font::kerned_font(std::string_view name, geom::vector<int, 2> size, int baseline_offset, gfx::texture_2d atlas,
|
||||||
|
std::unordered_map<char32_t, glyph_data> glyphs)
|
||||||
|
: name_{name}
|
||||||
|
, size_{size}
|
||||||
|
, baseline_offset_{baseline_offset}
|
||||||
|
, atlas_{std::move(atlas)}
|
||||||
|
, glyphs_{std::move(glyphs)}
|
||||||
|
{
|
||||||
|
if (!supports_character('?'))
|
||||||
|
throw std::runtime_error("Kerned font must support '?' character");
|
||||||
|
if (!supports_character(' '))
|
||||||
|
throw std::runtime_error("Kerned font must support ' ' character");
|
||||||
|
|
||||||
|
std::vector<char32_t> chars;
|
||||||
|
for (auto const & g : glyphs_)
|
||||||
|
chars.push_back(g.first);
|
||||||
|
std::sort(chars.begin(), chars.end());
|
||||||
|
|
||||||
|
for (auto c : chars)
|
||||||
|
{
|
||||||
|
if (!ranges_.empty() && ranges_.back().end == c)
|
||||||
|
++ranges_.back().end;
|
||||||
|
else
|
||||||
|
ranges_.push_back({c, c + 1});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 kerned_font::supports_character(char32_t c) const
|
||||||
|
{
|
||||||
|
return std::isspace(c) || glyphs_.contains(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<glyph> kerned_font::shape(std::string_view str, shape_options const & options) const
|
||||||
|
{
|
||||||
|
char32_t const unknown = supports_character(options.unknown_character) ? options.unknown_character : '?';
|
||||||
|
geom::vector<float, 2> const advance_mask = advance_dir(options.direction);
|
||||||
|
|
||||||
|
std::vector<glyph> result;
|
||||||
|
|
||||||
|
geom::vector<float, 2> pos{0.f, (size_[1] - baseline_offset_) * options.scale};
|
||||||
|
for (char32_t c : util::utf8_range(str))
|
||||||
|
{
|
||||||
|
if (!supports_character(c))
|
||||||
|
c = unknown;
|
||||||
|
|
||||||
|
auto const & data = glyphs_.at(c);
|
||||||
|
|
||||||
|
glyph g;
|
||||||
|
g.character = c;
|
||||||
|
g.position[0].min = pos[0] + data.offset_x * options.scale;
|
||||||
|
g.position[1].min = pos[1] - (data.offset_y + data.size_y) * options.scale;
|
||||||
|
g.position[0].max = pos[0] + (data.offset_x + data.size_x) * options.scale;
|
||||||
|
g.position[1].max = pos[1] - data.offset_y * options.scale;
|
||||||
|
result.push_back(g);
|
||||||
|
|
||||||
|
geom::vector<float, 2> advance{data.advance * options.scale, size_[1] * options.scale};
|
||||||
|
|
||||||
|
pos += geom::pointwise_mult(advance_mask, advance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<geom::box<float, 2>> kerned_font::texcoords(char32_t c) const
|
||||||
|
{
|
||||||
|
if (std::isspace(c))
|
||||||
|
c = ' ';
|
||||||
|
|
||||||
|
auto it = glyphs_.find(c);
|
||||||
|
if (it == glyphs_.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
geom::box<float, 2> box;
|
||||||
|
box[0].min = it->second.start_x;
|
||||||
|
box[1].min = it->second.start_y;
|
||||||
|
box[0].max = box[0].min + it->second.size_x;
|
||||||
|
box[1].max = box[1].min + it->second.size_y;
|
||||||
|
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue