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_link_libraries(psemek-ui PUBLIC psemek-util psemek-log psemek-geom psemek-cg psemek-gfx psemek-async psemek-sdl2)
|
||||
|
||||
psemek_add_resources(psemek-ui
|
||||
resources/cross_red_16x16.png psemek/ui/resources/cross_red_16x16_png
|
||||
)
|
||||
psemek_glob_resources(psemek-ui resources psemek/ui/resources)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <psemek/geom/vector.hpp>
|
||||
#include <psemek/geom/box.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
|
||||
#include <psemek/gfx/texture.hpp>
|
||||
|
||||
|
|
@ -54,7 +55,7 @@ namespace psemek::ui
|
|||
virtual geom::vector<int, 2> size() 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;
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ namespace psemek::ui
|
|||
virtual ~font() {}
|
||||
};
|
||||
|
||||
std::unique_ptr<font> make_default_monospace_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_; }
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
|||
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/monospace_font.hpp>
|
||||
#include <psemek/ui/kerned_font.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 <sstream>
|
||||
|
||||
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};
|
||||
std::string_view name = "default_9x12";
|
||||
std::string_view name = "default_monospace_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}));
|
||||
|
|
@ -40,4 +44,41 @@ namespace psemek::ui
|
|||
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