#include "ehs/io/FontAtlas.h" #include "ehs/io/File.h" #include "ehs/Serializer.h" namespace ehs { FontAtlas::~FontAtlas() { delete[] atlas; } FontAtlas::FontAtlas() : hashId(0), glyphScale(0), size(0), atlas(nullptr) { } FontAtlas::FontAtlas(const Str_8& filePath) { File fontFile(filePath, Mode::READ, Disposition::OPEN); hashId = fontFile.GetName().Hash_64(); id = fontFile.GetName(); Serializer fData = fontFile.ReadSerializer_64(Endianness::LE, fontFile.Size()); fontFile.Release(); Version ver = fData.ReadVersion(); if (ver != Version(1, 0, 0)) { EHS_LOG_INT(LogType::ERR, 2, "The Event Horizon Font file, \"" + filePath + "\", must be version 1.0.0, but was version " + Str_8::FromNum(ver.major) + "." + Str_8::FromNum(ver.minor) + "." + Str_8::FromNum(ver.patch) + "."); return; } glyphScale = fData.Read(); glyphs.Resize(fData.Read()); for (UInt_64 i = 0; i < glyphs.Size(); ++i) glyphs[i] = Glyph(fData); resolution = fData.ReadVec2(); atlas = new Byte[resolution.x * resolution.y]; size = 0; fData.ReadArray(atlas, &size); } FontAtlas::FontAtlas(FontAtlas&& fa) noexcept : BaseObj(std::move(fa)), hashId(fa.hashId), id((Str_8&&)fa.id), glyphScale(fa.glyphScale), glyphs((Array&&)fa.glyphs), resolution(fa.resolution), size(fa.size), atlas(fa.atlas) { fa.hashId = 0; fa.glyphScale = 0; fa.resolution = {}; fa.size = 0; fa.atlas = nullptr; } FontAtlas::FontAtlas(const FontAtlas& fa) : BaseObj(fa), hashId(fa.hashId), id(fa.id), glyphScale(fa.glyphScale), glyphs(fa.glyphs), resolution(fa.resolution), size(fa.size), atlas(new Byte[fa.size]) { Util::Copy(atlas, fa.atlas, fa.size); } FontAtlas& FontAtlas::operator=(FontAtlas&& fa) noexcept { if (this == &fa) return *this; BaseObj::operator=(std::move(fa)); hashId = fa.hashId; id = (Str_8&&)fa.id; glyphScale = fa.glyphScale; glyphs = (Array&&)fa.glyphs; resolution = fa.resolution; size = fa.size; delete[] atlas; atlas = fa.atlas; fa.hashId = 0; fa.glyphScale = 0; fa.resolution = {}; fa.size = 0; fa.atlas = nullptr; return *this; } FontAtlas& FontAtlas::operator=(const FontAtlas& fa) { if (this == &fa) return *this; BaseObj::operator=(fa); hashId = fa.hashId; id = fa.id; glyphScale = fa.glyphScale; glyphs = fa.glyphs; resolution = {}; size = fa.size; delete[] atlas; atlas = new Byte[fa.size]; Util::Copy(atlas, fa.atlas, fa.size); return *this; } FontAtlas::operator Byte *() const { return atlas; } void FontAtlas::Release() { hashId = 0; id = {}; glyphScale = 0; glyphs.Clear(); resolution = {0, 0}; size = 0; delete[] atlas; atlas = nullptr; } UInt_64 FontAtlas::GetHashId() const { return hashId; } Str_8 FontAtlas::GetId() const { return id; } UInt_64 FontAtlas::GetGlyphScale() const { return glyphScale; } const Array& FontAtlas::GetGlyphs() const { return glyphs; } Glyph FontAtlas::GetGlyph(const Char_32 code) const { for (UInt_32 i = 0; i < glyphs.Size(); ++i) if (glyphs[i].GetCode() == code) return glyphs[i]; return glyphs[0]; } Vec2_u64 FontAtlas::GetResolution() const { return resolution; } UInt_64 FontAtlas::GetSize() const { return size; } bool FontAtlas::IsValid() const { return size; } Vec2_f FontAtlas::CalculateSize(const Str_8& text) const { Vec2_f result; for (UInt_64 i = 0; i < text.Size(); ++i) { Glyph glyph = GetGlyph(text[i]); result.x += (float)glyph.GetAdvance().x; if ((float)glyph.GetScale().y > result.y) result.y = (float)glyph.GetScale().y; } return result; } float FontAtlas::CalculateWidth(const Str_8 &text) const { float result = 0.0f; for (UInt_64 i = 0; i < text.Size(); ++i) { Glyph glyph = GetGlyph(text[i]); result += (float)glyph.GetAdvance().x; } return result; } float FontAtlas::CalculateHeight(const Str_8& text) const { float result = 0.0f; for (UInt_64 i = 0; i < text.Size(); ++i) { Glyph glyph = GetGlyph(text[i]); if ((float)glyph.GetScale().y > result) result = (float)glyph.GetScale().y; } return result; } UInt_64 FontAtlas::CalculateIndexAtPoint(const Str_8& text, const Vec2_f& point) const { float result = 0.0f; for (UInt_64 i = 0; i < text.Size(); ++i) { Glyph glyph = GetGlyph(text[i]); float temp = result + (float)glyph.GetAdvance().x; if (point.x > temp) result += (float)glyph.GetAdvance().x; else if (point.x <= temp) return i; } return text.Size(); } Mesh FontAtlas::Generate(const Anchor anchor, const Str_8& text) const { Vec2_f pos; switch (anchor) { case Anchor::BOTTOM_CENTER: { pos.x -= CalculateWidth(text) * 0.5f; break; } case Anchor::TOP_LEFT: { pos.x -= CalculateWidth(text); break; } case Anchor::TOP_CENTER: { pos.x -= CalculateWidth(text) * 0.5f; break; } case Anchor::CENTER_RIGHT: { pos.y -= CalculateHeight(text); break; } case Anchor::CENTER: { Vec4_f size = CalculateSize(text); pos.x -= size.x * 0.5f; pos.y -= size.y; break; } } Array verts(text.Size() * 4); Array indices(text.Size() * 6); for (UInt_64 i = 0; i < text.Size(); ++i) { Glyph glyph = GetGlyph(text[i]); Vec2_f scale = glyph.GetScale(); float x = pos.x + (float)glyph.GetBearing().x; float y = pos.y + (float)(GetGlyphScale() - glyph.GetBearing().y); UInt_32 vertsI = i * 4; verts[vertsI] = Vertex_f({x, y, 1.0f}, {}, glyph.GetUV().GetPos()); verts[vertsI + 1] = Vertex_f({x, y + scale.y, 1.0f}, {}, Vec2_f(glyph.GetUV().x, glyph.GetUV().h)); verts[vertsI + 2] = Vertex_f({x + scale.x, y, 1.0f}, {}, Vec2_f(glyph.GetUV().w, glyph.GetUV().y)); verts[vertsI + 3] = Vertex_f({x + scale.x, y + scale.y, 1.0f}, {}, glyph.GetUV().GetScale()); UInt_32 indicesI = i * 6; indices[indicesI] = vertsI; indices[indicesI + 1] = vertsI + 1; indices[indicesI + 2] = vertsI + 2; indices[indicesI + 3] = vertsI + 3; indices[indicesI + 4] = vertsI + 2; indices[indicesI + 5] = vertsI + 1; pos.x += (float)glyph.GetAdvance().x; } return {id, (Array&&)verts, (Array&&)indices}; } }