Worked on FontManager

This commit is contained in:
matthias@arch 2022-11-14 23:00:57 +01:00
parent d5b5f93ed6
commit 93c73b1911
2 changed files with 205 additions and 16 deletions

View File

@ -1,11 +1,24 @@
#include "font.hpp"
#include "texture_atlas.hpp"
#include "vulkan_allocator.hpp"
#include "vulkan_instance.hpp"
#include <glm/exponential.hpp>
#include <gz-util/exceptions.hpp>
#include <gz-util/log.hpp>
#include <gz-util/string/conversion.hpp>
namespace gz {
namespace gz::vlk {
FontManager::FontManager(const std::string&& fontDir)
: fontDir(fontDir) {
FontManager::FontManager(VulkanInstance& instance, const std::string&& fontDir)
: vk(instance), fontDir(fontDir)
{
fLog = Log(LogCreateInfo {
.showLog = true,
.storeLog = false,
.prefix = "FontManager",
.prefixColor = Color::BO_GREEN,
});
FT_Error error = FT_Init_FreeType(&freetype);
if (error != FT_Err_Ok) {
throw Exception("Could not load freetype library", "FontManager");
@ -13,16 +26,132 @@ namespace gz {
}
FontManager::~FontManager() {
FT_Error error = FT_Done_FreeType(freetype);
if (error != FT_Err_Ok) {
fLog.error("~FontManager: FT_Done_FreeType returned", error);
}
}
void FontManager::loadFont(const std::string& font) {
FT_Error error = FT_New_Face(freetype, (fontDir + "/" + font).c_str(), 0, &faces[font]);
Font FontManager::getFont(const std::string& fontname) {
if (fonts.contains(fontname)) {
return fonts.at(fontname);
}
else {
return loadFont(fontname);
}
}
Font FontManager::loadFont(const std::string& fontname) {
static constexpr FT_Long faceIndex = 0;
Font font = fontInfos.size();
fontInfos.emplace_back(FontInfo(TextureAtlas(vk, 48, 48, 16, 16, vk::Format::eR8Uint)));
FT_Face& face = fontInfos.back().face;
FT_Error error = FT_New_Face(freetype, (fontDir + "/" + fontname).c_str(), faceIndex, &face);
if (error != FT_Err_Ok) {
throw Exception("Could not load font.", "FontManager::loadFont");
}
FT_Set_Pixel_Sizes(faces[font], 0, 48);
if (FT_Load_Char(faces[font], 'X', FT_LOAD_RENDER)) {
throw Exception("Could not load char.", "FontManager::loadFont");
for (FT_Int i = 0; i < face->num_charmaps; i++) {
if (face->charmaps[i]->encoding == ft_encoding_unicode) {
face->charmap = face->charmaps[i];
fLog("loadFont: loading", font, "with unicode charmap index", i);
goto foundUnicodeMap;
}
fLog.log0("loadFont: Charmap", i, face->charmaps[i]->platform_id, face->charmaps[i]->encoding);
}
throw Exception("Could not find unicode char map for font '" + fontname + "'.", "FontManager::loadFont");
foundUnicodeMap:
/* FT_Set_Pixel_Sizes(fonts[font], 0, 48); */
/* fonts[font]->charmap->encoding_idk */
fLog("Font num_glyphs:", face->num_glyphs);
addChars(font, "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789üß");
return font;
}
void FontManager::addChars(Font font, const std::string& chars) {
FT_Face& face = fontInfos.at(font).face;
TextureAtlas& atlas = fontInfos.at(font).atlas;
auto& charInfoMap = fontInfos.at(font).charInfos;
for (char c : chars) {
if (charInfoMap.contains(c)) { continue; }
FT_UInt charIndex = FT_Get_Char_Index(face, c);
fLog("Char:", c, "index:", charIndex, "glyph index:", face->glyph->glyph_index, "bitmap rows/width:", face->glyph->bitmap.rows, face->glyph->bitmap.width, "bitmap ptr:", reinterpret_cast<uint64_t>(face->glyph->bitmap.buffer), "(before load char)");
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
throw Exception(std::string("Could not load char.") + c, "FontManager::loadFont");
}
if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) {
throw Exception(std::string("Could not render char.") + c, "FontManager::loadFont");
}
/* for (size_t i = 0; i < face->glyph->bitmap_left) */
FT_Bitmap& bitmap = face->glyph->bitmap;
fLog("Char:", c,
"index:", charIndex,
"glyph index:", face->glyph->glyph_index,
"bitmap rows/width:", bitmap.rows, bitmap.width,
"bitmap pitch:", bitmap.pitch,
"bitmap ptr:", gz::toHexString(reinterpret_cast<uint64_t>(bitmap.buffer)));
assert(bitmap.pitch > 0);
CharInfo& charInfo = charInfoMap[c];
charInfo.atlas = font;
uint8_t bytesPerPixel = bitmap.width / bitmap.pitch;
std::tie(charInfo.texCoordTopLeft, charInfo.texCoordBottomRight) = atlas.addTexture(bitmap.buffer, bitmap.rows, bitmap.width, bytesPerPixel);
}
}
void FontManager::createDescriptorResources() {
// 1) layout
// combined image sampler
std::vector<vk::DescriptorSetLayoutBinding> samplerLayoutBindings(fontInfos.size(), {
.binding = 0,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eFragment,
/* .pImmutableSamplers = nullptr, */
}
);
vk.createDescriptorSetLayout(samplerLayoutBindings, descriptorSetLayout);
// 2) pool
vk::DescriptorPoolSize poolSize {
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = vk.getMaxFramesInFlight(),
};
vk.createDescriptorPool({ poolSize }, vk.getMaxFramesInFlight(), descriptorPool);
// 3) Set
vk.createDescriptorSet(descriptorSetLayout, descriptorPool, descriptorSet);
// 4) configure sets
std::vector<vk::DescriptorImageInfo> imageInfos;
for (auto it = fontInfos.cbegin(); it != fontInfos.cend(); it++) {
imageInfos.emplace_back();
imageInfos.back().imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
imageInfos.back().imageView = it->atlas.getTextureImageView();
imageInfos.back().sampler = it->atlas.getTextureSampler();
}
vk::WriteDescriptorSet descriptorW {
.dstSet = descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
};
descriptorW.setImageInfo(imageInfos);
// 1 write, no copies
vk.getDevice().updateDescriptorSets(descriptorW, nullptr);
fLog.log0("createDescriptorSets: Created descriptor sets.");
}
void FontManager::getTexCoords(Font font, char c, glm::vec2& texCoords) {
CharInfo& charI = fontInfos.at(font).charInfos.at(c);
fLog.log0("getTexCoords", texCoords, "textureInfo: topleft", charI.texCoordTopLeft, "botright", charI.texCoordBottomRight);
texCoords = charI.texCoordTopLeft + texCoords * (charI.texCoordBottomRight - charI.texCoordTopLeft);
}
}

View File

@ -1,20 +1,80 @@
#pragma once
#include "texture_atlas.hpp"
#include "vulkan_allocator.hpp"
#include <gz-util/string/utility.hpp>
#include <string>
#include <unordered_map>
#include <ft2build.h>
#include FT_FREETYPE_H
namespace gz {
#include <gz-util/log.hpp>
namespace gz::vlk {
struct CharInfo {
// TODO is atlas member needed?
uint32_t atlas;
glm::vec2 texCoordTopLeft;
glm::vec2 texCoordBottomRight;
};
/// Handle
using Font = uint16_t;
struct FontInfo {
FontInfo(TextureAtlas&& atlas) : atlas(atlas) {};
FT_Face face;
TextureAtlas atlas;
using CharInfos = std::unordered_map<char, CharInfo>;
CharInfos charInfos;
};
class FontManager {
public:
FontManager(const std::string&& fontDir);
void loadFont(const std::string& font);
const std::unordered_map<std::string, FT_Face>& getFaces() const { return faces; }
FontManager(VulkanInstance& instance, const std::string&& fontDir);
/**
* @brief Load a font
* @details
* -# load face
* -# set the charmap to unicode (throw if not found)
*/
~FontManager();
FontManager(const FontManager& other) = delete;
FontManager(FontManager&& other) = delete;
Font getFont(const std::string& fontname);
const std::unordered_map<std::string, Font>& getFonts() const { return fonts; }
void getTexCoords(Font font, const char c, glm::vec2& texCoords);
const vk::DescriptorSet& getDescriptorSet() const { return descriptorSet; }
const vk::DescriptorSetLayout& getDescriptorSetLayout() const { return descriptorSetLayout; }
private:
Font loadFont(const std::string& fontname);
VulkanInstance& vk;
FT_Library freetype;
std::string fontDir;
std::unordered_map<std::string, FT_Face> faces;
Log fLog;
void addChars(Font font, const std::string& chars);
/**
* @name Font management
*/
/// @{
/// Font name to handle
std::unordered_map<std::string, Font> fonts;
/// Font handle as index
std::vector<FontInfo> fontInfos;
/// @}
/**
* @name Create desciptors
* @todo Currently the same as in TextureManager -> merge?
* @see fm_descriptors
*/
/// @{
vk::DescriptorSetLayout descriptorSetLayout;
vk::DescriptorPool descriptorPool;
vk::DescriptorSet descriptorSet;
void createDescriptorResources();
/// @}
};
}