Files
Copium/CopiumEngine/src/copium/sampler/Font.cpp
T
Thraix ecc11f07db Improve vulkan resource freeing
and some minor improvements to the Renderer, now taking in an
AssetRef<Pipeline> as parameter.
2024-10-11 20:06:22 +02:00

165 lines
6.0 KiB
C++

#include "copium/sampler/Font.h"
#include "copium/buffer/Buffer.h"
#include "copium/core/Vulkan.h"
#include "copium/sampler/Image.h"
#include <msdf-atlas-gen/msdf-atlas-gen.h>
namespace Copium
{
Font::Font(const MetaFile& metaFile)
: Sampler{SamplerCreator{metaFile.GetMetaClass("Font")}}
{
msdfgen::FreetypeHandle* ft = msdfgen::initializeFreetype();
CP_ASSERT(ft, "Failed to initialize FreeType"); // TODO: Move to Vulkan singleton class?
std::string fontPath = metaFile.GetMetaClass("Font").GetValue("filepath");
msdfgen::FontHandle* font = msdfgen::loadFont(ft, fontPath.c_str());
CP_ASSERT(font, "Failed to initialize font: %s", fontPath.c_str());
std::vector<msdf_atlas::GlyphGeometry> glyphs;
msdf_atlas::FontGeometry fontGeometry(&glyphs);
fontGeometry.loadCharset(font, 1.0, msdf_atlas::Charset::ASCII);
const double maxCornerAngle = 3.0;
for (msdf_atlas::GlyphGeometry& glyph : glyphs)
{
glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0);
}
msdf_atlas::TightAtlasPacker packer;
packer.setDimensionsConstraint(msdf_atlas::TightAtlasPacker::DimensionsConstraint::SQUARE);
packer.setMinimumScale(64.0);
packer.setPixelRange(2.0);
packer.setMiterLimit(1.0);
packer.setPadding(2);
packer.pack(glyphs.data(), glyphs.size());
int width = 0, height = 0;
packer.getDimensions(width, height);
msdf_atlas::ImmediateAtlasGenerator<float, 3, msdf_atlas::msdfGenerator, msdf_atlas::BitmapAtlasStorage<msdf_atlas::byte, 3>> generator(width, height);
msdf_atlas::GeneratorAttributes attributes;
generator.setAttributes(attributes);
generator.setThreadCount(4);
generator.generate(glyphs.data(), glyphs.size());
msdf_atlas::BitmapAtlasStorage<msdf_atlas::byte, 3> pixels = generator.atlasStorage();
std::vector<uint8_t> data;
data.resize(width * height * 4);
msdfgen::Bitmap<msdf_atlas::byte, 3> bitmap = (msdfgen::Bitmap<msdf_atlas::byte, 3>)pixels;
for (int i = 0; i < width * height; i++)
{
data[i * 4] = bitmap[i * 3];
data[i * 4 + 1] = bitmap[i * 3 + 1];
data[i * 4 + 2] = bitmap[i * 3 + 2];
data[i * 4 + 3] = 255;
}
InitializeTextureImageFromData(data.data(), width, height);
for (msdf_atlas::GlyphGeometry& glyphGeom : glyphs)
{
Glyph glyph;
glyph.advance = glyphGeom.getAdvance();
double l, b, r, t;
glyphGeom.getQuadPlaneBounds(l, b, r, t);
glyph.boundingBox = BoundingBox{(float)l, (float)b, (float)r, (float)t};
glyphGeom.getQuadAtlasBounds(l, b, r, t);
glyph.texCoordBoundingBox = BoundingBox{(float)l / width, (float)b / height, (float)r / width, (float)t / height};
this->glyphs.emplace((char)glyphGeom.getCodepoint(), glyph);
}
lineHeight = fontGeometry.getMetrics().lineHeight;
baseHeight = fontGeometry.getMetrics().ascenderY;
msdfgen::destroyFont(font);
msdfgen::deinitializeFreetype(ft);
}
Font::~Font()
{
VkImage imageCpy = image;
VkDeviceMemory imageMemoryCpy = imageMemory;
VkImageView imageViewCpy = imageView;
Vulkan::GetDevice().QueueIdleCommand([imageCpy, imageMemoryCpy, imageViewCpy]() {
vkDestroyImage(Vulkan::GetDevice(), imageCpy, nullptr);
vkFreeMemory(Vulkan::GetDevice(), imageMemoryCpy, nullptr);
vkDestroyImageView(Vulkan::GetDevice(), imageViewCpy, nullptr);
});
}
VkDescriptorImageInfo Font::GetDescriptorImageInfo(int index) const
{
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.sampler = sampler;
imageInfo.imageView = imageView;
return imageInfo;
}
const Glyph& Font::GetGlyph(char c) const
{
CP_ASSERT(glyphs.find(c) != glyphs.end(), "Glyph not found: %c", c);
return glyphs.find(c)->second;
}
float Font::GetLineHeight() const
{
return lineHeight;
}
float Font::GetBaseHeight() const
{
return baseHeight;
}
BoundingBox Font::GetTextBoundingBox(const std::string& str, float size) const
{
BoundingBox boundingBox{0.0f};
glm::vec2 offset{0.0f};
for (auto c : str)
{
if(c == ' ')
{
const Glyph& glyph = GetGlyph(c);
offset.x += glyph.advance;
continue;
}
else if (c == '\t')
{
const Glyph& glyph = GetGlyph(' ');
offset.x += glyph.advance * 4;
continue;
}
else if (c == '\n')
{
boundingBox.r = std::max(boundingBox.r, offset.x);
offset.y -= GetLineHeight();
offset.x = 0.0f;
continue;
}
const Glyph& glyph = GetGlyph(c);
boundingBox.l = std::min(boundingBox.l, offset.x + glyph.boundingBox.l);
boundingBox.t = std::max(boundingBox.t, offset.y + glyph.boundingBox.t);
offset.x += glyph.advance;
boundingBox.b = std::min(boundingBox.b, offset.y + glyph.boundingBox.b);
}
boundingBox.r = std::max(boundingBox.r, offset.x);
boundingBox.lbrt *= size;
return boundingBox;
}
void Font::InitializeTextureImageFromData(const uint8_t* rgbaData, int width, int height)
{
VkDeviceSize bufferSize = width * height * 4;
Buffer stagingBuffer{VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, bufferSize, 1};
void* data = stagingBuffer.Map();
memcpy(data, rgbaData, bufferSize);
stagingBuffer.Unmap();
Image::InitializeImage(width, height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &image, &imageMemory);
Image::TransitionImageLayout(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
Image::CopyBufferToImage(stagingBuffer, image, width, height);
Image::TransitionImageLayout(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
imageView = Image::InitializeImageView(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
}
}