/* * Copyright (C) 2022 Riyyi * * SPDX-License-Identifier: MIT */ #include // std::array #include // std;:from_chars #include // int32_t, uint32_t #include // std::numeric_limits #include // std::views::split #include // std::getline #include // std::move #include "ruc/file.h" #include "ruc/format/log.h" #include "ruc/meta/assert.h" #include "ruc/meta/concepts.h" #include "inferno/render/font.h" #include "inferno/render/texture.h" #include "inferno/util/integer.h" namespace Inferno { Font::Font(const std::string& name) : m_name(std::move(name)) { std::string path = name + ".fnt"; std::string image = name + ".png"; std::string font = ruc::File(path).data(); parseFont(font); m_texture = std::make_shared(image); } // TODO: Move this to ruc template static T convert(std::string_view value) { T tmp; std::from_chars(value.data(), value.data() + value.size(), tmp); return tmp; } void Font::parseFont(const std::string& font) { std::istringstream iss(font); for (std::string line; std::getline(iss, line);) { std::string action = findAction(line); const std::vector columns = findColumns(line); // Info // --------------------------------- if (action.compare("info") == 0) { m_size = convert(findValue("size", columns)); auto paddings = findValue("padding", columns) | std::views::split(','); size_t i = 0; for (const auto& padding : paddings) { // top, right, bottom, left m_padding[i++] = convert(padding.data()) - PADDING; } continue; } // Common // --------------------------------- if (action.compare("common") == 0) { m_lineSpacing = convert(findValue("lineHeight", columns)) - m_padding[Padding::Top] - m_padding[Padding::Bottom]; continue; } // Character // --------------------------------- if (action.compare("char") == 0) { unsigned char id = convert(findValue("id", columns)); uint32_t width = convert(findValue("width", columns)); uint32_t height = convert(findValue("height", columns)); Character character = { .id = id, .position = { convert(findValue("x", columns)) + m_padding[Padding::Left], convert(findValue("y", columns)) + m_padding[Padding::Top], }, .size = { width == 0 ? 0 : width - m_padding[Padding::Left] - m_padding[Padding::Right], height == 0 ? 0 : height - m_padding[Padding::Top] - m_padding[Padding::Bottom], }, .offset = { convert(findValue("xoffset", columns)) + m_padding[Padding::Left], convert(findValue("yoffset", columns)) + m_padding[Padding::Top], }, .advance = convert(findValue("xadvance", columns)) - m_padding[Padding::Left] - m_padding[Padding::Right] }; m_characterList.emplace(id, std::make_shared(character)); continue; } // Kerning // --------------------------------- if (action.compare("kerning") == 0) { unsigned char first = convert(findValue("first", columns)); unsigned char second = convert(findValue("second", columns)); char amount = convert(findValue("amount", columns)); // Add the kerning of the previous character to this character if (m_characterList.find(second) != m_characterList.end()) { auto character = m_characterList.at(second); character->kernings.emplace(first, amount); } continue; } } } std::string Font::findAction(const std::string& line) const { return line.substr(0, line.find(" ")); } std::vector Font::findColumns(const std::string& line) const { std::vector elements; size_t index = 0; size_t find = 0; size_t findFirstNotOf = 0; // Loop over line characters while (find != std::string::npos) { find = line.find(" ", index); findFirstNotOf = line.find_first_not_of(" ", index); // Add element if (find > findFirstNotOf) { elements.push_back(line.substr(index, find - findFirstNotOf)); index += 1 + find - findFirstNotOf; } // Skip double spaces else { index = findFirstNotOf; } } VERIFY(!elements.empty(), "Font file did not find any columns"); return elements; } std::string Font::findValue(const std::string& key, const std::vector& columns) const { size_t find = 0; // Loop over columns for (auto& column : columns) { find = column.find(key + "="); if (find != std::string::npos) { return column.substr(key.length() + 1); } } VERIFY(false, "Font file did not contain key '{}'", key); return ""; } // ----------------------------------------- FontManager::FontManager(s) { ruc::info("FontManager initialized"); } FontManager::~FontManager() { } void FontManager::add(const std::string& name, std::shared_ptr font) { // Construct (key, value) pair and insert it into the unordered_map m_fontList.emplace(std::move(name), std::move(font)); } std::shared_ptr FontManager::load(const std::string& name) { if (exists(name)) { return get(name); } std::shared_ptr font = std::make_shared(name); add(name, font); return get(name); } std::shared_ptr FontManager::get(const std::string& name) { return exists(name) ? m_fontList.at(name) : nullptr; } bool FontManager::exists(const std::string& name) { return m_fontList.find(name) != m_fontList.end(); } void FontManager::remove(const std::string& name) { if (exists(name)) { m_fontList.erase(name); } } void FontManager::remove(std::shared_ptr font) { if (exists(font->name())) { m_fontList.erase(font->name()); } } } // namespace Inferno void ruc::format::Formatter::format(Builder& builder, glm::ivec2 value) const { return Formatter>::format(builder, { value.x, value.y }); }