From d09de68cd836ef0e4d178a4273624b8dd7d013ce Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Tue, 12 Oct 2021 12:05:56 +0800 Subject: [PATCH] Add FreeType implementation of the typeface class, add FreeType typeface loader. Closes #4. --- CMakeLists.txt | 4 +- src/game/bootloader.cpp | 1 + src/game/context.hpp | 2 + src/game/states/loading.cpp | 130 ++++++++++------------- src/resources/json-loader.cpp | 2 +- src/resources/typeface-loader.cpp | 56 ++++++++++ src/type/font-metrics.hpp | 6 ++ src/type/freetype/typeface.cpp | 164 ++++++++++++++++++++++++++++++ src/type/freetype/typeface.hpp | 80 +++++++++++++++ src/type/glyph-metrics.hpp | 17 ++-- src/type/typeface.cpp | 44 ++++++++ src/type/typeface.hpp | 103 ++++++++++++++++++- 12 files changed, 526 insertions(+), 83 deletions(-) create mode 100644 src/resources/typeface-loader.cpp create mode 100644 src/type/freetype/typeface.cpp create mode 100644 src/type/freetype/typeface.hpp create mode 100644 src/type/typeface.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index af368e1..3bc5ed2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ find_package(OpenGL REQUIRED) find_package(SDL2 REQUIRED COMPONENTS SDL2::SDL2-static SDL2::SDL2main CONFIG) find_package(OpenAL REQUIRED CONFIG) find_library(physfs REQUIRED NAMES physfs-static PATHS "${CMAKE_PREFIX_PATH}/lib") +find_package(freetype REQUIRED CONFIG) # Determine dependencies set(STATIC_LIBS @@ -24,7 +25,8 @@ set(STATIC_LIBS SDL2::SDL2-static SDL2::SDL2main OpenAL::OpenAL - ${physfs}) + ${physfs} + freetype) set(SHARED_LIBS ${OPENGL_gl_LIBRARY}) diff --git a/src/game/bootloader.cpp b/src/game/bootloader.cpp index 760a63c..abf2fcc 100644 --- a/src/game/bootloader.cpp +++ b/src/game/bootloader.cpp @@ -346,6 +346,7 @@ void setup_resources(game::context* ctx) ctx->resource_manager->include("/behaviors/"); ctx->resource_manager->include("/controls/"); ctx->resource_manager->include("/localization/"); + ctx->resource_manager->include("/localization/fonts/"); ctx->resource_manager->include("/biomes/"); ctx->resource_manager->include("/traits/"); ctx->resource_manager->include("/"); diff --git a/src/game/context.hpp b/src/game/context.hpp index c31f4cf..288a3e7 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -44,6 +44,7 @@ #include #include #include "resources/json.hpp" +#include "type/typeface.hpp" // Forward declarations class animator; @@ -139,6 +140,7 @@ struct context string_table* string_table; string_table_map string_table_map; std::unordered_map* strings; + std::unordered_map typefaces; // Framebuffers gl::framebuffer* shadow_map_framebuffer; diff --git a/src/game/states/loading.cpp b/src/game/states/loading.cpp index 3e6c11f..b79f1cd 100644 --- a/src/game/states/loading.cpp +++ b/src/game/states/loading.cpp @@ -52,7 +52,6 @@ #include "utility/timestamp.hpp" #include "type/type.hpp" #include -#include namespace game { namespace state { @@ -61,6 +60,8 @@ namespace loading { /// Loads control profile and calibrates gamepads static void load_controls(game::context* ctx); +static void load_fonts(game::context* ctx); + /// Creates the universe and solar system. static void cosmogenesis(game::context* ctx); @@ -93,6 +94,18 @@ void enter(game::context* ctx) } ctx->logger->pop_task(EXIT_SUCCESS); + // Load fonts + ctx->logger->push_task("Loading fonts"); + try + { + load_fonts(ctx); + } + catch (...) + { + ctx->logger->pop_task(EXIT_FAILURE); + } + ctx->logger->pop_task(EXIT_SUCCESS); + // Create universe ctx->logger->push_task("Creating the universe"); try @@ -121,79 +134,6 @@ void enter(game::context* ctx) next_state.exit = std::bind(game::state::splash::exit, ctx); } - - type::bitmap_font font; - image& font_bitmap = font.get_bitmap(); - font_bitmap.format(sizeof(unsigned char), 1); - - std::ifstream font_file(ctx->config_path + "Vollkorn-Regular.ttf", std::ios::binary | std::ios::ate); - std::streamsize size = font_file.tellg(); - font_file.seekg(0, std::ios::beg); - std::vector font_buffer(size); - if (!font_file.read((char*)font_buffer.data(), size)) - { - ctx->logger->error("Failed to read font file"); - } - - // stb_truetype - { - stbtt_fontinfo font_info; - if (!stbtt_InitFont(&font_info, font_buffer.data(), 0)) - { - ctx->logger->error("stb init font failed"); - } - - const float font_size = 64.0f; - const float scale = stbtt_ScaleForPixelHeight(&font_info, font_size); - - int ascent = 0; - int descent = 0; - int linegap = 0; - stbtt_GetFontVMetrics(&font_info, &ascent, &descent, &linegap); - - float scaled_ascent = static_cast(ascent) * scale; - float scaled_descent = static_cast(descent) * scale; - float scaled_linegap = static_cast(linegap) * scale; - - type::unicode::block block = type::unicode::block::basic_latin; - for (int c = block.first; c <= block.last; ++c) - { - int glyph_index = stbtt_FindGlyphIndex(&font_info, c); - if (!glyph_index) - continue; - - type::bitmap_glyph& glyph = font[static_cast(c)]; - - int advance_width = 0; - int left_side_bearing = 0; - stbtt_GetGlyphHMetrics(&font_info, glyph_index, &advance_width, &left_side_bearing); - - int min_x; - int min_y; - int max_x; - int max_y; - stbtt_GetGlyphBitmapBox(&font_info, glyph_index, scale, scale, &min_x, &min_y, &max_x, &max_y); - - int glyph_width = max_x - min_x; - int glyph_height = max_y - min_y; - - glyph.metrics.width = static_cast(glyph_width); - glyph.metrics.height = static_cast(glyph_height); - glyph.metrics.bearing_left = static_cast(left_side_bearing) * scale; - glyph.metrics.bearing_top = 0.0f; - glyph.metrics.advance = static_cast(advance_width) * scale; - - glyph.bitmap.format(sizeof(unsigned char), 1); - glyph.bitmap.resize(glyph_width, glyph_height); - stbtt_MakeGlyphBitmap(&font_info, (unsigned char*)glyph.bitmap.get_pixels(), glyph_width, glyph_height, glyph_width, scale, scale, glyph_index); - } - } - font.pack(); - - std::string bitmap_path = ctx->config_path + "bitmap-font.png"; - stbi_flip_vertically_on_write(0); - stbi_write_png(bitmap_path.c_str(), font_bitmap.get_width(), font_bitmap.get_height(), font_bitmap.get_channel_count(), font_bitmap.get_pixels(), font_bitmap.get_width() * font_bitmap.get_channel_count()); - // Queue next game state ctx->app->queue_state(next_state); } @@ -287,6 +227,48 @@ void load_controls(game::context* ctx) ctx->controls["menu_right"]->set_activation_threshold(menu_activation_threshold); } +void load_fonts(game::context* ctx) +{ + // Load typefaces + if (auto it = ctx->strings->find("font_serif"); it != ctx->strings->end()) + ctx->typefaces["serif"] = ctx->resource_manager->load(it->second); + if (auto it = ctx->strings->find("font_sans_serif"); it != ctx->strings->end()) + ctx->typefaces["sans_serif"] = ctx->resource_manager->load(it->second); + if (auto it = ctx->strings->find("font_monospace"); it != ctx->strings->end()) + ctx->typefaces["monospace"] = ctx->resource_manager->load(it->second); + + // Build bitmap fonts + if (auto it = ctx->typefaces.find("serif"); it != ctx->typefaces.end()) + { + type::typeface* typeface = it->second; + type::bitmap_font font; + + const float size = 48.0f; + if (type::font_metrics metrics; typeface->get_metrics(size, metrics)) + font.set_font_metrics(metrics); + + image& font_bitmap = font.get_bitmap(); + font_bitmap.format(sizeof(unsigned char), 1); + + type::unicode::block block = type::unicode::block::basic_latin; + for (char32_t code = block.first; code <= block.last; ++code) + { + if (!typeface->has_glyph(code)) + continue; + + type::bitmap_glyph& glyph = font[code]; + typeface->get_metrics(size, code, glyph.metrics); + typeface->get_bitmap(size, code, glyph.bitmap); + } + + font.pack(); + + std::string bitmap_path = ctx->config_path + "bitmap-font-serif.png"; + stbi_flip_vertically_on_write(0); + stbi_write_png(bitmap_path.c_str(), font_bitmap.get_width(), font_bitmap.get_height(), font_bitmap.get_channel_count(), font_bitmap.get_pixels(), font_bitmap.get_width() * font_bitmap.get_channel_count()); + } +} + void cosmogenesis(game::context* ctx) { // Init time diff --git a/src/resources/json-loader.cpp b/src/resources/json-loader.cpp index 0f1559d..f4c8e45 100644 --- a/src/resources/json-loader.cpp +++ b/src/resources/json-loader.cpp @@ -26,7 +26,7 @@ template <> json* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) { // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::size_t size = static_cast(PHYSFS_fileLength(file)); std::string buffer; buffer.resize(size); PHYSFS_readBytes(file, &buffer[0], size); diff --git a/src/resources/typeface-loader.cpp b/src/resources/typeface-loader.cpp new file mode 100644 index 0000000..bcd517c --- /dev/null +++ b/src/resources/typeface-loader.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "resource-loader.hpp" +#include "type/freetype/typeface.hpp" +#include +#include +#include +#include FT_FREETYPE_H + +template <> +type::typeface* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file) +{ + FT_Error error = 0; + + // Init FreeType library object + FT_Library library; + error = FT_Init_FreeType(&library); + if (error) + { + throw std::runtime_error("Failed to init FreeType library (error code " + std::to_string(error) + ")"); + } + + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + unsigned char* buffer = new unsigned char[size]; + PHYSFS_readBytes(file, buffer, size); + + // Load FreeType face from buffer + FT_Face face; + error = FT_New_Memory_Face(library, buffer, size, 0, &face); + if (error) + { + delete[] buffer; + FT_Done_FreeType(library); + throw std::runtime_error("Failed to load FreeType face (error code " + std::to_string(error) + ")"); + } + + return new type::freetype::typeface(library, face, buffer); +} diff --git a/src/type/font-metrics.hpp b/src/type/font-metrics.hpp index d4546e5..781f622 100644 --- a/src/type/font-metrics.hpp +++ b/src/type/font-metrics.hpp @@ -44,6 +44,12 @@ struct font_metrics /// Thickness of an underline. float underline_thickness; + + /// Maximum advance with for all glyphs, for horizontal layouts. + float max_horizontal_advance; + + /// Maximum advance height for all glyphs, for vertical layouts. + float max_vertical_advance; }; } // namespace type diff --git a/src/type/freetype/typeface.cpp b/src/type/freetype/typeface.cpp new file mode 100644 index 0000000..8036693 --- /dev/null +++ b/src/type/freetype/typeface.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "type/freetype/typeface.hpp" +#include +#include + +namespace type { +namespace freetype { + +typeface::typeface(FT_Library library, FT_Face face, unsigned char* buffer): + library(library), + face(face), + buffer(buffer), + height(-1.0f) +{} + +typeface::~typeface() +{ + FT_Done_Face(face); + delete[] buffer; + FT_Done_FreeType(library); +} + +bool typeface::has_kerning() const +{ + return FT_HAS_KERNING(face); +} + +bool typeface::has_glyph(char32_t code) const +{ + return FT_Get_Char_Index(face, static_cast(code)) != 0; +} + +bool typeface::get_metrics(float height, font_metrics& metrics) const +{ + // Set font size + set_face_pixel_size(height); + + // Get font metrics + metrics.ascent = face->size->metrics.ascender / 64.0f; + metrics.descent = face->size->metrics.descender / 64.0f; + metrics.linegap = face->size->metrics.height / 64.0f; + metrics.linespace = metrics.ascent - metrics.descent + metrics.linegap; + metrics.underline_position = FT_MulFix(face->underline_position, face->size->metrics.y_scale) / 64.0f; + metrics.underline_thickness = FT_MulFix(face->underline_thickness, face->size->metrics.y_scale) / 64.0f; + metrics.max_horizontal_advance = face->size->metrics.max_advance / 64.0f; + metrics.max_vertical_advance = FT_MulFix(face->max_advance_height, face->size->metrics.y_scale) / 64.0f; + + return true; +} + +bool typeface::get_metrics(float height, char32_t code, glyph_metrics& metrics) const +{ + // Set font size + set_face_pixel_size(height); + + // Get index of glyph + FT_UInt glyph_index = FT_Get_Char_Index(face, static_cast(code)); + + // Load glyph and render bitmap + FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (error) + { + throw std::runtime_error("FreeType failed to load glyph (error code " + std::to_string(error) + ")"); + } + + // Get glyph metrics + metrics.width = face->glyph->metrics.width / 64.0f; + metrics.height = face->glyph->metrics.height / 64.0f; + metrics.horizontal_bearing.x = face->glyph->metrics.horiBearingX / 64.0f; + metrics.horizontal_bearing.y = face->glyph->metrics.horiBearingY / 64.0f; + metrics.vertical_bearing.x = face->glyph->metrics.vertBearingX / 64.0f; + metrics.vertical_bearing.y = face->glyph->metrics.vertBearingY / 64.0f; + metrics.horizontal_advance = face->glyph->metrics.horiAdvance / 64.0f; + metrics.vertical_advance = face->glyph->metrics.vertAdvance / 64.0f; + + return true; +} + +bool typeface::get_bitmap(float height, char32_t code, image& bitmap) const +{ + // Set font size + set_face_pixel_size(height); + + // Get index of glyph + FT_UInt glyph_index = FT_Get_Char_Index(face, static_cast(code)); + + // Load glyph and render bitmap + FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_TARGET_(FT_RENDER_MODE_NORMAL)); + if (error) + { + throw std::runtime_error("FreeType failed to load glyph (error code " + std::to_string(error) + ")"); + } + + // Format and resize bitmap + bitmap.resize(0, 0); + bitmap.format(sizeof(unsigned char), 1); + bitmap.resize(face->glyph->bitmap.width, face->glyph->bitmap.rows); + + // Copy glyph bitmap data in bitmap + std::memcpy(bitmap.get_pixels(), face->glyph->bitmap.buffer, bitmap.get_size()); + + return true; +} + +bool typeface::get_kerning(float height, char32_t first, char32_t second, float2& offset) const +{ + // Check if typeface has kerning information + if (!has_kerning()) + return false; + + // Set font size + set_face_pixel_size(height); + + // Get indices of the two glyphs + FT_UInt first_index = FT_Get_Char_Index(face, static_cast(first)); + FT_UInt second_index = FT_Get_Char_Index(face, static_cast(second)); + + // Get kerning vector + FT_Vector kerning; + FT_Error error = FT_Get_Kerning(face, first_index, second_index, FT_KERNING_DEFAULT, &kerning); + if (error) + { + throw std::runtime_error("FreeType failed to get kerning vector (error code " + std::to_string(error) + ")"); + } + + offset = float2{static_cast(kerning.x), static_cast(kerning.y)} / 64.0f; + + return true; +} + +void typeface::set_face_pixel_size(float height) const +{ + if (this->height == height) + return; + + FT_Error error = FT_Set_Pixel_Sizes(face, 0, static_cast(height)); + if (error) + { + throw std::runtime_error("FreeType failed to set face size (error code " + std::to_string(error) + ")"); + } + + this->height = height; +} + +} // namespace freetype +} // namespace type diff --git a/src/type/freetype/typeface.hpp b/src/type/freetype/typeface.hpp new file mode 100644 index 0000000..f7e9d91 --- /dev/null +++ b/src/type/freetype/typeface.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP +#define ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP + +#include "type/typeface.hpp" +#include +#include FT_FREETYPE_H + +namespace type { +namespace freetype { + +/** + * Typeface implementation using the FreeType library. + * + * @see type::typeface + */ +class typeface: public type::typeface +{ +public: + /** + * Creates a FreeType typeface. + * + * @param library Pointer to a FreeType library instance. + * @param face Pointer to the FreeType object instance. + * @param buffer Pointer to file buffer containing FreeType face data. + */ + typeface(FT_Library library, FT_Face face, unsigned char* buffer); + + /// Destroys a FreeType typeface. + virtual ~typeface(); + + /// @copydoc type::typeface::has_kerning() const + virtual bool has_kerning() const; + + /// @copydoc type::typeface::has_glyph(char32_t) const + virtual bool has_glyph(char32_t code) const; + + /// @copydoc type::typeface::get_metrics(float, font_metrics&) const + virtual bool get_metrics(float height, font_metrics& metrics) const; + + /// @copydoc type::typeface::get_metrics(float, char32_t, glyph_metrics&) const + virtual bool get_metrics(float height, char32_t code, glyph_metrics& metrics) const; + + /// @copydoc type::typeface::get_bitmap(float, char32_t, image&) const + virtual bool get_bitmap(float height, char32_t code, image& bitmap) const; + + /// @copydoc type::typeface::get_kerning(float, char32_t, char32_t, float2&) const + virtual bool get_kerning(float height, char32_t first, char32_t second, float2& offset) const; + +private: + void set_face_pixel_size(float height) const; + + FT_Library library; + FT_Face face; + unsigned char* buffer; + mutable float height; +}; + +} // namespace freetype +} // namespace type + +#endif // ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP diff --git a/src/type/glyph-metrics.hpp b/src/type/glyph-metrics.hpp index 80c3a1e..e94502d 100644 --- a/src/type/glyph-metrics.hpp +++ b/src/type/glyph-metrics.hpp @@ -20,6 +20,8 @@ #ifndef ANTKEEPER_TYPE_GLYPH_METRICS_HPP #define ANTKEEPER_TYPE_GLYPH_METRICS_HPP +#include "utility/fundamental-types.hpp" + namespace type { /** @@ -33,14 +35,17 @@ struct glyph_metrics /// Vertical extent of the glyph. float height; - /// Horizontal distance from the pen position to the glyph's left edge. - float bearing_left; + /// Offset from the pen position to the glyph's top-left edge, in horizontal layouts. + float2 horizontal_bearing; + + /// Offset from the pen position to the glph's top-left edge, in vertical layouts. + float2 vertical_bearing; - /// Vertical distance from the baseline to the glyph's top edge. - float bearing_top; + /// Distance to move the pen position after the glyph has been rendered, in horizontal layouts. + float horizontal_advance; - /// Distance to move the pen position after the glyph has been rendered. - float advance; + /// Distance to move the pen position after the glyph has been rendered, in vertical layouts. + float vertical_advance; }; } // namespace type diff --git a/src/type/typeface.cpp b/src/type/typeface.cpp new file mode 100644 index 0000000..fc06640 --- /dev/null +++ b/src/type/typeface.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "type/typeface.hpp" + +namespace type { + +typeface::typeface(typeface_style style, int weight): + style(style), + weight(weight) +{} + +typeface::typeface(): + style(typeface_style::normal), + weight(400) +{} + +void typeface::set_style(typeface_style style) +{ + this->style = style; +} + +void typeface::set_weight(int weight) +{ + this->weight = weight; +} + +} // namespace type diff --git a/src/type/typeface.hpp b/src/type/typeface.hpp index 8c3b457..1d9bda7 100644 --- a/src/type/typeface.hpp +++ b/src/type/typeface.hpp @@ -20,33 +20,134 @@ #ifndef ANTKEEPER_TYPE_TYPEFACE_HPP #define ANTKEEPER_TYPE_TYPEFACE_HPP +#include "type/font-metrics.hpp" +#include "type/glyph-metrics.hpp" +#include "resources/image.hpp" +#include "utility/fundamental-types.hpp" + namespace type { +/// Emumerates typeface styles. enum class typeface_style { + /// Normal typeface style. normal, + + /// Italic typeface style. italic, + + /// Oblique typeface style. oblique }; /** - * Variant a typeface family, with a specific style and weight. A typeface corresponds to a single digital font file. + * Abstract base class for a typeface, which corresponds to a single digital font file. * * @see type::font */ class typeface { public: + /** + * Creates a typeface, setting its style and weight. + * + * @param style Typeface style. + * @param weight Typeface weight. + */ typeface(typeface_style style, int weight); + /// Creates an empty typeface. + typeface(); + + /// Destroys a typeface. + virtual ~typeface() = default; + + /** + * Sets the style of the typeface. + * + * @param style Typeface style. + */ + void set_style(typeface_style style); + + /** + * Sets the weight of the typeface. + * + * @param weight Typeface weight. + */ + void set_weight(int weight); + + /// Returns the style of the typeface. typeface_style get_style() const; + + /// Returns the weight of the typeface. int get_weight() const; + /// Returns `true` if the typeface contains kerning information, `false` otherwise. + virtual bool has_kerning() const = 0; + + /** + * Returns `true` if the typeface contains a glyph, `false` otherwise. + * + * @param code UTF-32 character code of a glyph. + * @return `true` if the typeface contains the glyph, `false` otherwise. + */ + virtual bool has_glyph(char32_t code) const = 0; + + /** + * Gets metrics for a font of the specified size. + * + * @param[in] height Height of the font, in pixels. + * @param[out] metrics Font metrics. + * @return `true` if font metrics were returned, `false` otherwise. + */ + virtual bool get_metrics(float height, font_metrics& metrics) const = 0; + + /** + * Gets metrics for a glyph in a font of the specified size. + * + * @param[in] height Height of the font, in pixels. + * @param[in] code UTF-32 character code of a glyph. + * @param[out] metrics Glyph metrics. + * @return `true` if glyph metrics were returned, `false` otherwise. + */ + virtual bool get_metrics(float height, char32_t code, glyph_metrics& metrics) const = 0; + + /** + * Gets a bitmap of a glyph in a font of the specified size. + * + * @param[in] height Height of the font, in pixels. + * @param[in] code UTF-32 character code of a glyph. + * @param[out] bitmap Glyph bitmap data. + * @return `true` if glyph bitmap data was returned, `false` otherwise. + */ + virtual bool get_bitmap(float height, char32_t code, image& bitmap) const = 0; + + /** + * Gets the kerning offset for a pair of glyphs. + * + * @param[in] height Height of the font, in pixels. + * @param[in] first UTF-32 character code of the first glyph. + * @param[in] second UTF-32 character code of the second glyph. + * @param[out] offset Kerning offset. + * @return `true` if a kerning offset was returned, `false` otherwise. + */ + virtual bool get_kerning(float height, char32_t first, char32_t second, float2& offset) const = 0; + private: typeface_style style; int weight; }; +inline typeface_style typeface::get_style() const +{ + return style; +} + +inline int typeface::get_weight() const +{ + return weight; +} + } // namespace type #endif // ANTKEEPER_TYPE_TYPEFACE_HPP