Files
OpenVulkano/openVulkanoCpp/Scene/Shelf.hpp
2025-01-03 14:13:51 +02:00

97 lines
2.7 KiB
C++

/*
* 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 <vector>
#include <optional>
namespace OpenVulkano::Scene
{
struct GlyphForPacking
{
uint32_t code;
Math::Vector2i size;
Math::Vector2d atlasPos;
size_t firstGlyphByteInAtlas;
};
struct Shelf
{
inline static std::vector<Shelf> CreateShelves(uint32_t atlasWidth, std::vector<GlyphForPacking>& 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<uint32_t> 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> Shelf::CreateShelves(uint32_t atlasWidth, std::vector<GlyphForPacking>& glyphs,
const FtFaceRecPtr& face)
{
std::vector<Shelf> 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<uint32_t> 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;
}
}