Worked on FontManager
This commit is contained in:
parent
d5b5f93ed6
commit
93c73b1911
151
src/font.cpp
151
src/font.cpp
@ -1,11 +1,24 @@
|
|||||||
#include "font.hpp"
|
#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/exceptions.hpp>
|
||||||
|
#include <gz-util/log.hpp>
|
||||||
|
#include <gz-util/string/conversion.hpp>
|
||||||
|
|
||||||
namespace gz {
|
namespace gz::vlk {
|
||||||
|
|
||||||
FontManager::FontManager(const std::string&& fontDir)
|
FontManager::FontManager(VulkanInstance& instance, const std::string&& fontDir)
|
||||||
: fontDir(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);
|
FT_Error error = FT_Init_FreeType(&freetype);
|
||||||
if (error != FT_Err_Ok) {
|
if (error != FT_Err_Ok) {
|
||||||
throw Exception("Could not load freetype library", "FontManager");
|
throw Exception("Could not load freetype library", "FontManager");
|
||||||
@ -13,16 +26,132 @@ namespace gz {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FontManager::~FontManager() {
|
||||||
void FontManager::loadFont(const std::string& font) {
|
FT_Error error = FT_Done_FreeType(freetype);
|
||||||
FT_Error error = FT_New_Face(freetype, (fontDir + "/" + font).c_str(), 0, &faces[font]);
|
|
||||||
if (error != FT_Err_Ok) {
|
if (error != FT_Err_Ok) {
|
||||||
throw Exception("Could not load font.", "FontManager::loadFont");
|
fLog.error("~FontManager: FT_Done_FreeType returned", error);
|
||||||
}
|
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
70
src/font.hpp
70
src/font.hpp
@ -1,20 +1,80 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "texture_atlas.hpp"
|
||||||
|
#include "vulkan_allocator.hpp"
|
||||||
|
|
||||||
|
#include <gz-util/string/utility.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <ft2build.h>
|
#include <ft2build.h>
|
||||||
#include FT_FREETYPE_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 {
|
class FontManager {
|
||||||
public:
|
public:
|
||||||
FontManager(const std::string&& fontDir);
|
FontManager(VulkanInstance& instance, const std::string&& fontDir);
|
||||||
void loadFont(const std::string& font);
|
/**
|
||||||
const std::unordered_map<std::string, FT_Face>& getFaces() const { return faces; }
|
* @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:
|
private:
|
||||||
|
Font loadFont(const std::string& fontname);
|
||||||
|
VulkanInstance& vk;
|
||||||
FT_Library freetype;
|
FT_Library freetype;
|
||||||
std::string fontDir;
|
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();
|
||||||
|
/// @}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user