/* * 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/. */ #include "FontAtlasGeneratorBase.hpp" #include "Base/Logger.hpp" #include #include #include #define STBI_MSC_SECURE_CRT #define STB_IMAGE_WRITE_IMPLEMENTATION #define STBIW_ZLIB_COMPRESS OpenVulkano::Utils::STBZlibCompressor #include namespace OpenVulkano::Scene { std::pair FontAtlasGeneratorBase::InitFreetype(const std::variant>& source) { FT_Library library; auto error = FT_Init_FreeType(&library); if (error) { throw std::runtime_error(fmt::format("Could not initalize freetype library. {}", GetFreetypeErrorDescription(error))); } FT_Face face; if (std::holds_alternative(source)) { error = FT_New_Face(library, std::get<0>(source).c_str(), 0, &face); } else { auto& arr = std::get<1>(source); error = FT_New_Memory_Face(library, (const FT_Byte*) (arr.Data()), arr.Size(), 0, &face); } if (error == FT_Err_Unknown_File_Format) { throw std::runtime_error("Unknown font file format\n"); } else if (error) { throw std::runtime_error(fmt::format("Font file could not be opened or read or it's corrupted. {}", GetFreetypeErrorDescription(error))); } // some fancy font without unicode charmap if (face->charmap == nullptr) { throw std::runtime_error("Selected font doesn't contain unicode charmap"); } return { FtLibraryRecPtr(library), FtFaceRecPtr(face) }; } std::string FontAtlasGeneratorBase::GetFreetypeErrorDescription(FT_Error error) { if (const char* s = FT_Error_String(error)) { return s; } return fmt::format("Error code is {}", error); } void FontAtlasGeneratorBase::SaveAtlasMetadataInfo(const std::string& outputFile, bool packIntoSingleFile) const { if (m_atlasData->glyphs.empty()) { Logger::DATA->info("No glyphs loaded. Nothing to save."); return; } std::string fileName = outputFile; uint32_t packedFlag = packIntoSingleFile; if (packIntoSingleFile) { std::filesystem::path fPath(fileName); fileName = (fPath.parent_path() / fPath.stem()).string() + "_packed.png"; SavePng(fileName); } std::fstream fs(fileName.c_str(), std::ios_base::out | std::ios_base::binary | (packedFlag ? std::ios_base::app : std::ios_base::trunc)); fs.write(reinterpret_cast(&m_atlasData->meta), sizeof(AtlasMetadata)); uint64_t metadataBytes = sizeof(AtlasMetadata); for (const auto& [key, val] : m_atlasData->glyphs) { fs.write(reinterpret_cast(&key), sizeof(uint32_t)); fs.write(reinterpret_cast(&val), sizeof(GlyphInfo)); metadataBytes += sizeof(uint32_t); metadataBytes += sizeof(GlyphInfo); } fs.write(reinterpret_cast(&metadataBytes), sizeof(uint64_t)); fs.write(reinterpret_cast(&packedFlag), sizeof(uint32_t)); fs.close(); } void FontAtlasGeneratorBase::SavePng(std::string output) const { stbi_flip_vertically_on_write(1); if (std::filesystem::path(output).extension() != ".png") { output += ".png"; } stbi_write_png(output.c_str(), m_atlasData->img->resolution.x, m_atlasData->img->resolution.y, m_channelsCount, m_atlasData->img->data.Data(), m_channelsCount * m_atlasData->img->resolution.x); } void FontAtlasGeneratorBase::SetupAtlasData(Math::Vector2ui textureResolution, double lineHeight, FontAtlasType::Type atlasType) { // generate texture m_atlasData->img = std::make_unique(); m_atlasData->img->resolution = Math::Vector3ui(textureResolution, 1); m_atlasData->img->dataFormat = m_channelsCount == 1 ? DataFormat::R8_UNORM : DataFormat::R8G8B8A8_UNORM; m_atlasData->img->data = Array(m_atlasData->img->dataFormat.CalculatedSize(textureResolution.x, textureResolution.y)); m_atlasData->texture.resolution = m_atlasData->img->resolution; m_atlasData->texture.textureBuffer = m_atlasData->img->data.Data(); m_atlasData->texture.format = m_atlasData->img->dataFormat; m_atlasData->texture.size = m_atlasData->img->data.Size(); m_atlasData->meta.atlasType = atlasType; m_atlasData->meta.lineHeight = lineHeight; if (atlasType == FontAtlasType::BITMAP) { m_atlasData->texture.m_samplerConfig = &SamplerConfig::NEAREST; } } void FontAtlasGeneratorBase::SetGlyphData(GlyphInfo& info, Math::Vector2d bearing, Math::Vector2d size, const Math::AABB& aabb, double advance) { const double bearingX = bearing.x; const double bearingY = bearing.y; const double w = size.x; const double h = size.y; const double l = aabb.min.x; const double r = aabb.max.x; const double t = aabb.max.y; const double b = aabb.min.y; info.xyz[0].x = bearingX; info.xyz[0].y = h - bearingY; info.uv[0].x = l / m_atlasData->texture.resolution.x; info.uv[0].y = b / m_atlasData->texture.resolution.y; info.xyz[1].x = bearingX + w; info.xyz[1].y = h - bearingY; info.uv[1].x = r / m_atlasData->texture.resolution.x; info.uv[1].y = b / m_atlasData->texture.resolution.y; info.xyz[2].x = bearingX + w; info.xyz[2].y = bearingY; //h - bearingY + h; info.uv[2].x = r / m_atlasData->texture.resolution.x; info.uv[2].y = t / m_atlasData->texture.resolution.y; info.xyz[3].x = bearingX; info.xyz[3].y = bearingY; info.uv[3].x = l / m_atlasData->texture.resolution.x; info.uv[3].y = t / m_atlasData->texture.resolution.y; info.advance = advance; } std::set FontAtlasGeneratorBase::LoadAllGlyphs(const std::variant>& data) { const auto& [lib, face] = InitFreetype(data); std::set s; FT_UInt glyphIndex; FT_ULong unicode = FT_Get_First_Char(face.get(), &glyphIndex); while (glyphIndex != 0) { s.insert(unicode); unicode = FT_Get_Next_Char(face.get(), unicode, &glyphIndex); } return s; } }