From 9ff67815d0c3ba628dbb1c0a377bab23f5248a38 Mon Sep 17 00:00:00 2001 From: ohyzha Date: Fri, 3 Jan 2025 12:16:59 +0200 Subject: [PATCH] code refactoring --- openVulkanoCpp/Image/ImageLoader.cpp | 2 +- .../Scene/BitmapFontAtlasGenerator.cpp | 152 +++++------------- .../Scene/BitmapFontAtlasGenerator.hpp | 2 + openVulkanoCpp/Scene/Shelf.hpp | 96 +++++++++++ 4 files changed, 135 insertions(+), 117 deletions(-) create mode 100644 openVulkanoCpp/Scene/Shelf.hpp diff --git a/openVulkanoCpp/Image/ImageLoader.cpp b/openVulkanoCpp/Image/ImageLoader.cpp index f81b6ef..c5d6d17 100644 --- a/openVulkanoCpp/Image/ImageLoader.cpp +++ b/openVulkanoCpp/Image/ImageLoader.cpp @@ -44,7 +44,7 @@ namespace OpenVulkano::Image if (channels == 3) { result.data = OpenVulkano::Array(width * height * 4); - for (size_t srcPos = 0, dstPos = 0; srcPos < width * height * 3; srcPos += 3, dstPos += 4) + for (size_t srcPos = 0, dstPos = 0; dstPos < result.data.Size(); srcPos += 3, dstPos += 4) { result.data[dstPos] = pixelData[srcPos]; result.data[dstPos + 1] = pixelData[srcPos + 1]; diff --git a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp index 4be3489..a869827 100644 --- a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp +++ b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.cpp @@ -7,88 +7,6 @@ #include "BitmapFontAtlasGenerator.hpp" #include "Base/Logger.hpp" -namespace -{ - using namespace OpenVulkano; - - struct GlyphForPacking - { - uint32_t code; - size_t firstGlyphByteInAtlas; - Math::Vector2d atlasPos; - Math::Vector2i wh; - }; - - struct Shelf - { - Shelf(uint32_t width, uint32_t height) : m_width(width), m_height(height), m_remainingWidth(width) {} - bool HasSpaceForGlyph(uint32_t glyphWidth, uint32_t glyphHeight) const - { - return m_remainingWidth >= glyphWidth && m_height >= glyphHeight; - } - uint32_t GetWidth() const { return m_width; } - uint32_t GetHeight() const { return m_height; } - uint32_t GetNextGlyphPos() const { return m_nextGlyphPos; }; - uint32_t GetOccupiedSize() const { return ((m_width - m_remainingWidth) * m_height); } - std::optional AddGlyph(uint32_t glyphWidth, uint32_t glyphHeight) - { - if (!HasSpaceForGlyph(glyphWidth, glyphHeight)) - { - return {}; - } - uint32_t insertionPos = m_nextGlyphPos; - m_nextGlyphPos += glyphWidth; - m_remainingWidth -= glyphWidth; - return insertionPos; - } - - private: - uint32_t m_width; - uint32_t m_height; - uint32_t m_remainingWidth; - uint32_t m_nextGlyphPos = 0; - }; - - std::vector CreateShelves(uint32_t atlasWidth, std::vector& glyphs, const Scene::FtFaceRecPtr& face) - { - std::vector shelves; - for (GlyphForPacking& glyph : glyphs) - { - FT_Error error = FT_Load_Char(face.get(), glyph.code, FT_LOAD_RENDER); - if (error) - { - continue; - } - - FT_GlyphSlot slot = face->glyph; - bool needNewShelf = true; - uint32_t totalPrevShelvesHeight = 0; - for (Shelf& shelf : shelves) - { - if (std::optional insertionPosX = shelf.AddGlyph(glyph.wh.x, glyph.wh.y)) - { - glyph.firstGlyphByteInAtlas = *insertionPosX + (totalPrevShelvesHeight * atlasWidth); - glyph.atlasPos.x = *insertionPosX; - glyph.atlasPos.y = totalPrevShelvesHeight; - needNewShelf = false; - break; - } - totalPrevShelvesHeight += shelf.GetHeight(); - } - - if (needNewShelf) - { - shelves.emplace_back(atlasWidth, glyph.wh.y); - shelves.back().AddGlyph(glyph.wh.x, glyph.wh.y); - glyph.firstGlyphByteInAtlas = totalPrevShelvesHeight * atlasWidth; - glyph.atlasPos.x = 0; - glyph.atlasPos.y = totalPrevShelvesHeight; - } - } - return shelves; - } -} - namespace OpenVulkano::Scene { void BitmapFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set& charset, @@ -125,33 +43,12 @@ namespace OpenVulkano::Scene FT_Set_Pixel_Sizes(face.get(), 0, pixelSize); } - FT_Error error = 0; - double area = 0; - std::vector allGlyphs; - allGlyphs.reserve(chset.size()); - for (uint32_t codepoint : chset) - { - error = FT_Load_Char(face.get(), codepoint, FT_LOAD_RENDER); - if (error) - { - Logger::APP->error("FT_Load_Char for codepoint {} has failed. {}", codepoint, GetFreetypeErrorDescription(error)); - continue; - } - FT_GlyphSlot slot = face->glyph; - unsigned int h = slot->bitmap.rows; - unsigned int w = slot->bitmap.width; - GlyphForPacking& glyph = allGlyphs.emplace_back(); - glyph.code = codepoint; - glyph.wh = { slot->bitmap.width, slot->bitmap.rows }; - area += h * w; - } - - const double sq = ceil(sqrt(area)); - std::sort(allGlyphs.begin(), allGlyphs.end(), [](const GlyphForPacking& a, const GlyphForPacking& b) { return a.wh.y > b.wh.y; }); - std::vector shelves = ::CreateShelves(sq, allGlyphs, face); + auto [allGlyphs, area] = InitGlyphsForPacking(chset, face); + const double atlasWidth = ceil(sqrt(area)); + std::vector shelves = Shelf::CreateShelves(atlasWidth, allGlyphs, face); uint32_t atlasHeight = 0; std::for_each(shelves.begin(), shelves.end(), [&](const Shelf& shelf) { atlasHeight += shelf.GetHeight(); }); - const Math::Vector2ui atlasResolution = { sq, atlasHeight }; + const Math::Vector2ui atlasResolution = { atlasWidth, atlasHeight }; // same as in msdfgen lib by default. see import-font.h for reference // TODO: probably also support keeping coordinates as the integer values native to the font file @@ -163,22 +60,20 @@ namespace OpenVulkano::Scene size_t loadedGlyphs = 0; for (const GlyphForPacking& glyph : allGlyphs) { - error = FT_Load_Char(face.get(), glyph.code, FT_LOAD_RENDER); + FT_Error error = FT_Load_Char(face.get(), glyph.code, FT_LOAD_RENDER); if (error) { - Logger::APP->error("FT_Load_Char for codepoint {} has failed. {}", glyph.code, - GetFreetypeErrorDescription(error)); + Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", glyph.code, + GetFreetypeErrorDescription(error)); continue; } FT_GlyphSlot slot = face->glyph; for (int row = 0; row < slot->bitmap.rows; row++) { - for (int col = 0; col < slot->bitmap.width; col++) - { - m_atlasData->img->data[glyph.firstGlyphByteInAtlas + row * atlasResolution.x + col] = - slot->bitmap.buffer[(slot->bitmap.rows - 1 - row) * slot->bitmap.pitch + col]; - } + std::memcpy(&m_atlasData->img->data[glyph.firstGlyphByteInAtlas + row * atlasResolution.x], + &slot->bitmap.buffer[(slot->bitmap.rows - 1 - row) * slot->bitmap.pitch], + slot->bitmap.width); } GlyphInfo& glyphInfo = m_atlasData->glyphs[glyph.code]; @@ -195,6 +90,31 @@ namespace OpenVulkano::Scene { SavePng(*pngOutput); } - Logger::APP->debug("Created atlas with {} glyphs, {} glyphs could not be loaded", loadedGlyphs, chset.size() - loadedGlyphs); + Logger::SCENE->debug("Created atlas with {} glyphs, {} glyphs could not be loaded", loadedGlyphs, chset.size() - loadedGlyphs); + } + + std::pair, double> + BitmapFontAtlasGenerator::InitGlyphsForPacking(const std::set& chset, const FtFaceRecPtr& face) + { + FT_Error error = 0; + double area = 0; + std::vector allGlyphs; + allGlyphs.reserve(chset.size()); + for (uint32_t codepoint : chset) + { + error = FT_Load_Char(face.get(), codepoint, FT_LOAD_RENDER); + if (error) + { + Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", codepoint, + GetFreetypeErrorDescription(error)); + continue; + } + FT_GlyphSlot slot = face->glyph; + GlyphForPacking& glyph = allGlyphs.emplace_back(codepoint, Math::Vector2ui(slot->bitmap.width, slot->bitmap.rows)); + area += slot->bitmap.rows * slot->bitmap.width; + } + std::sort(allGlyphs.begin(), allGlyphs.end(), + [](const GlyphForPacking& a, const GlyphForPacking& b) { return a.size.y > b.size.y; }); + return { allGlyphs, area }; } } diff --git a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.hpp b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.hpp index a4db4b5..bf61664 100644 --- a/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.hpp +++ b/openVulkanoCpp/Scene/BitmapFontAtlasGenerator.hpp @@ -7,6 +7,7 @@ #pragma once #include "FontAtlasGeneratorBase.hpp" +#include "Shelf.hpp" namespace OpenVulkano::Scene { @@ -27,6 +28,7 @@ namespace OpenVulkano::Scene const std::optional& pngOutput = std::nullopt) override; private: void Generate(const std::variant>& source, const std::set& chset, const std::optional& pngOutput); + std::pair, double> InitGlyphsForPacking(const std::set& chset, const FtFaceRecPtr& face); private: FontPixelSizeConfig m_pixelSizeConfig; }; diff --git a/openVulkanoCpp/Scene/Shelf.hpp b/openVulkanoCpp/Scene/Shelf.hpp new file mode 100644 index 0000000..b0522b7 --- /dev/null +++ b/openVulkanoCpp/Scene/Shelf.hpp @@ -0,0 +1,96 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "Math/Math.hpp" +#include "Scene/FreetypeHelper.hpp" +#include +#include + +namespace OpenVulkano::Scene +{ + struct GlyphForPacking + { + uint32_t code; + Math::Vector2i size; + Math::Vector2d atlasPos; + size_t firstGlyphByteInAtlas; + }; + + struct Shelf + { + inline static std::vector CreateShelves(uint32_t atlasWidth, std::vector& glyphs, + const FtFaceRecPtr& face); + + Shelf(uint32_t width, uint32_t height) : m_width(width), m_height(height), m_remainingWidth(width) {} + bool HasSpaceForGlyph(uint32_t glyphWidth, uint32_t glyphHeight) const + { + return m_remainingWidth >= glyphWidth && m_height >= glyphHeight; + } + uint32_t GetWidth() const { return m_width; } + uint32_t GetHeight() const { return m_height; } + uint32_t GetNextGlyphPos() const { return m_nextGlyphPos; }; + uint32_t GetOccupiedSize() const { return ((m_width - m_remainingWidth) * m_height); } + std::optional AddGlyph(uint32_t glyphWidth, uint32_t glyphHeight) + { + if (!HasSpaceForGlyph(glyphWidth, glyphHeight)) + { + return {}; + } + uint32_t insertionPos = m_nextGlyphPos; + m_nextGlyphPos += glyphWidth; + m_remainingWidth -= glyphWidth; + return insertionPos; + } + + private: + uint32_t m_width; + uint32_t m_height; + uint32_t m_remainingWidth; + uint32_t m_nextGlyphPos = 0; + }; + + std::vector Shelf::CreateShelves(uint32_t atlasWidth, std::vector& glyphs, + const FtFaceRecPtr& face) + { + std::vector shelves; + for (GlyphForPacking& glyph : glyphs) + { + FT_Error error = FT_Load_Char(face.get(), glyph.code, FT_LOAD_RENDER); + if (error) + { + continue; + } + + FT_GlyphSlot slot = face->glyph; + bool needNewShelf = true; + uint32_t totalPrevShelvesHeight = 0; + for (Shelf& shelf : shelves) + { + if (std::optional insertionPosX = shelf.AddGlyph(glyph.size.x, glyph.size.y)) + { + glyph.firstGlyphByteInAtlas = *insertionPosX + (totalPrevShelvesHeight * atlasWidth); + glyph.atlasPos.x = *insertionPosX; + glyph.atlasPos.y = totalPrevShelvesHeight; + needNewShelf = false; + break; + } + totalPrevShelvesHeight += shelf.GetHeight(); + } + + if (needNewShelf) + { + shelves.emplace_back(atlasWidth, glyph.size.y); + shelves.back().AddGlyph(glyph.size.x, glyph.size.y); + glyph.firstGlyphByteInAtlas = totalPrevShelvesHeight * atlasWidth; + glyph.atlasPos.x = 0; + glyph.atlasPos.y = totalPrevShelvesHeight; + } + } + return shelves; + } +}