code refactoring
This commit is contained in:
@@ -44,7 +44,7 @@ namespace OpenVulkano::Image
|
|||||||
if (channels == 3)
|
if (channels == 3)
|
||||||
{
|
{
|
||||||
result.data = OpenVulkano::Array<uint8_t>(width * height * 4);
|
result.data = OpenVulkano::Array<uint8_t>(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] = pixelData[srcPos];
|
||||||
result.data[dstPos + 1] = pixelData[srcPos + 1];
|
result.data[dstPos + 1] = pixelData[srcPos + 1];
|
||||||
|
|||||||
@@ -7,88 +7,6 @@
|
|||||||
#include "BitmapFontAtlasGenerator.hpp"
|
#include "BitmapFontAtlasGenerator.hpp"
|
||||||
#include "Base/Logger.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<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> CreateShelves(uint32_t atlasWidth, std::vector<GlyphForPacking>& glyphs, const Scene::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.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
|
namespace OpenVulkano::Scene
|
||||||
{
|
{
|
||||||
void BitmapFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
void BitmapFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
||||||
@@ -125,33 +43,12 @@ namespace OpenVulkano::Scene
|
|||||||
FT_Set_Pixel_Sizes(face.get(), 0, pixelSize);
|
FT_Set_Pixel_Sizes(face.get(), 0, pixelSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
FT_Error error = 0;
|
auto [allGlyphs, area] = InitGlyphsForPacking(chset, face);
|
||||||
double area = 0;
|
const double atlasWidth = ceil(sqrt(area));
|
||||||
std::vector<GlyphForPacking> allGlyphs;
|
std::vector<Shelf> shelves = Shelf::CreateShelves(atlasWidth, allGlyphs, face);
|
||||||
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<Shelf> shelves = ::CreateShelves(sq, allGlyphs, face);
|
|
||||||
uint32_t atlasHeight = 0;
|
uint32_t atlasHeight = 0;
|
||||||
std::for_each(shelves.begin(), shelves.end(), [&](const Shelf& shelf) { atlasHeight += shelf.GetHeight(); });
|
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
|
// 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
|
// TODO: probably also support keeping coordinates as the integer values native to the font file
|
||||||
@@ -163,10 +60,10 @@ namespace OpenVulkano::Scene
|
|||||||
size_t loadedGlyphs = 0;
|
size_t loadedGlyphs = 0;
|
||||||
for (const GlyphForPacking& glyph : allGlyphs)
|
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)
|
if (error)
|
||||||
{
|
{
|
||||||
Logger::APP->error("FT_Load_Char for codepoint {} has failed. {}", glyph.code,
|
Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", glyph.code,
|
||||||
GetFreetypeErrorDescription(error));
|
GetFreetypeErrorDescription(error));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -174,11 +71,9 @@ namespace OpenVulkano::Scene
|
|||||||
FT_GlyphSlot slot = face->glyph;
|
FT_GlyphSlot slot = face->glyph;
|
||||||
for (int row = 0; row < slot->bitmap.rows; row++)
|
for (int row = 0; row < slot->bitmap.rows; row++)
|
||||||
{
|
{
|
||||||
for (int col = 0; col < slot->bitmap.width; col++)
|
std::memcpy(&m_atlasData->img->data[glyph.firstGlyphByteInAtlas + row * atlasResolution.x],
|
||||||
{
|
&slot->bitmap.buffer[(slot->bitmap.rows - 1 - row) * slot->bitmap.pitch],
|
||||||
m_atlasData->img->data[glyph.firstGlyphByteInAtlas + row * atlasResolution.x + col] =
|
slot->bitmap.width);
|
||||||
slot->bitmap.buffer[(slot->bitmap.rows - 1 - row) * slot->bitmap.pitch + col];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphInfo& glyphInfo = m_atlasData->glyphs[glyph.code];
|
GlyphInfo& glyphInfo = m_atlasData->glyphs[glyph.code];
|
||||||
@@ -195,6 +90,31 @@ namespace OpenVulkano::Scene
|
|||||||
{
|
{
|
||||||
SavePng(*pngOutput);
|
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<std::vector<GlyphForPacking>, double>
|
||||||
|
BitmapFontAtlasGenerator::InitGlyphsForPacking(const std::set<uint32_t>& chset, const FtFaceRecPtr& face)
|
||||||
|
{
|
||||||
|
FT_Error error = 0;
|
||||||
|
double area = 0;
|
||||||
|
std::vector<GlyphForPacking> 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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "FontAtlasGeneratorBase.hpp"
|
#include "FontAtlasGeneratorBase.hpp"
|
||||||
|
#include "Shelf.hpp"
|
||||||
|
|
||||||
namespace OpenVulkano::Scene
|
namespace OpenVulkano::Scene
|
||||||
{
|
{
|
||||||
@@ -27,6 +28,7 @@ namespace OpenVulkano::Scene
|
|||||||
const std::optional<std::string>& pngOutput = std::nullopt) override;
|
const std::optional<std::string>& pngOutput = std::nullopt) override;
|
||||||
private:
|
private:
|
||||||
void Generate(const std::variant<std::string, Array<char>>& source, const std::set<uint32_t>& chset, const std::optional<std::string>& pngOutput);
|
void Generate(const std::variant<std::string, Array<char>>& source, const std::set<uint32_t>& chset, const std::optional<std::string>& pngOutput);
|
||||||
|
std::pair<std::vector<GlyphForPacking>, double> InitGlyphsForPacking(const std::set<uint32_t>& chset, const FtFaceRecPtr& face);
|
||||||
private:
|
private:
|
||||||
FontPixelSizeConfig m_pixelSizeConfig;
|
FontPixelSizeConfig m_pixelSizeConfig;
|
||||||
};
|
};
|
||||||
|
|||||||
96
openVulkanoCpp/Scene/Shelf.hpp
Normal file
96
openVulkanoCpp/Scene/Shelf.hpp
Normal file
@@ -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 <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user