#include #include #include namespace psemek::fonts { kerned_font::kerned_font(bmfont_data data, gfx::texture_2d atlas) : data_(std::move(data)) , atlas_{std::move(atlas)} { if (!supports_character('?')) throw util::exception("Kerned font must support '?' character"); if (!supports_character(' ')) throw util::exception("Kerned font must support ' ' character"); std::vector chars; for (auto const & g : data_.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 math::vector 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 data_.glyphs.contains(c); } std::vector kerned_font::shape(std::string_view str, shape_options const & options, math::point & pen) const { return shape_impl(util::utf8_range(str), options, pen); } std::vector kerned_font::shape(std::u32string_view str, shape_options const & options, math::point & pen) const { return shape_impl(str, options, pen); } template std::vector kerned_font::shape_impl(String const & str, shape_options const & options, math::point & pen) const { char32_t const unknown = supports_character(options.unknown_character) ? options.unknown_character : '?'; math::vector const advance_mask = advance_dir(options.direction); std::vector result; for (char32_t c : str) { if (!supports_character(c)) c = unknown; auto const & data = data_.glyphs.at(c); glyph g; g.character = c; g.position[0].min = pen[0] + data.offset_x * options.scale; g.position[1].min = pen[1] - (data.offset_y + data.size_y) * options.scale; g.position[0].max = pen[0] + (data.offset_x + data.size_x) * options.scale; g.position[1].max = pen[1] - data.offset_y * options.scale; result.push_back(g); math::vector advance{data.advance * options.scale, data_.size[1] * options.scale}; pen += math::pointwise_mult(advance_mask, advance); } return result; } std::optional> kerned_font::texcoords(char32_t c) const { auto it = data_.glyphs.find(c); if (it == data_.glyphs.end()) return std::nullopt; math::box 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; } std::unique_ptr make_bitmap_font(io::istream && description, io::istream && texture) { auto data = bmfont_data::parse(std::move(description)); auto pixmap = gfx::read_image(std::move(texture)); gfx::texture_2d atlas; atlas.load(pixmap); atlas.linear_filter(); atlas.clamp(); gl::TexParameteri(atlas.target, gl::TEXTURE_SWIZZLE_R, gl::ONE); gl::TexParameteri(atlas.target, gl::TEXTURE_SWIZZLE_G, gl::ONE); gl::TexParameteri(atlas.target, gl::TEXTURE_SWIZZLE_B, gl::ONE); gl::TexParameteri(atlas.target, gl::TEXTURE_SWIZZLE_A, gl::RED); return std::make_unique(std::move(data), std::move(atlas)); } }