Move some more classes
This commit is contained in:
226
openVulkanoCpp/Scene/Text/BitmapFontAtlasGenerator.cpp
Normal file
226
openVulkanoCpp/Scene/Text/BitmapFontAtlasGenerator.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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 "BitmapFontAtlasGenerator.hpp"
|
||||
#include "Base/Logger.hpp"
|
||||
#include "FontAtlas.hpp"
|
||||
#include <freetype/ftlcdfil.h>
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
void BitmapFontAtlasGenerator::GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput)
|
||||
{
|
||||
Generate(fontFile, charset, pngOutput);
|
||||
}
|
||||
|
||||
void BitmapFontAtlasGenerator::GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput)
|
||||
{
|
||||
Generate(fontData, charset, pngOutput);
|
||||
}
|
||||
|
||||
void BitmapFontAtlasGenerator::Generate(const std::variant<std::string, Array<char>>& source,
|
||||
const std::set<uint32_t>& chset,
|
||||
const std::optional<std::string>& pngOutput)
|
||||
{
|
||||
if (chset.empty())
|
||||
{
|
||||
Logger::APP->info("Charset is empty. Nothing to generate.");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& [lib, face] = FontAtlasGeneratorBase::InitFreetype(source);
|
||||
FT_Set_Pixel_Sizes(face.get(), 0, m_pixelSizeConfig.CalculatePixelSize());
|
||||
if (m_subpixelLayout != SubpixelLayout::UNKNOWN)
|
||||
{
|
||||
FT_Error error = FT_Library_SetLcdFilter(lib.get(), FT_LCD_FILTER_DEFAULT);
|
||||
if (error != 0)
|
||||
{
|
||||
m_subpixelLayout = SubpixelLayout::UNKNOWN;
|
||||
m_channelsCount = 1;
|
||||
Logger::SCENE->error("Failed to set lcd filter for subpixel rendering. {}", GetFreetypeErrorDescription(error));
|
||||
}
|
||||
}
|
||||
|
||||
auto [allGlyphs, atlasWidth] = InitGlyphsForPacking(chset, face);
|
||||
std::vector<Shelf> shelves = Shelf::CreateShelves(atlasWidth, allGlyphs, face, m_channelsCount);
|
||||
uint32_t atlasHeight = 0;
|
||||
std::for_each(shelves.begin(), shelves.end(), [&](const Shelf& shelf) { atlasHeight += shelf.GetHeight(); });
|
||||
const Math::Vector2ui atlasResolution = { atlasWidth, atlasHeight };
|
||||
|
||||
// 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
|
||||
// but since some algorithms have already been implemented for EM_NORMALIZED mode, currently there is no support for default font metrics (ints)
|
||||
// The coordinates will be normalized to the em size, i.e. 1 = 1 em
|
||||
const double scaleFactor = (1. / face->units_per_EM);
|
||||
m_atlasData = std::make_shared<FontAtlas>(atlasResolution, face->height * scaleFactor,
|
||||
static_cast<bool>(m_subpixelLayout) ? FontAtlasType::BITMAP_SUBPIXEL :
|
||||
FontAtlasType::BITMAP,
|
||||
m_subpixelLayout.GetTextureDataFormat());
|
||||
FillGlyphsInfo(allGlyphs, face, scaleFactor);
|
||||
if (pngOutput) m_atlasData->Save(*pngOutput);
|
||||
}
|
||||
|
||||
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, GetGlyphRenderMode());
|
||||
if (error)
|
||||
{
|
||||
Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", codepoint,
|
||||
GetFreetypeErrorDescription(error));
|
||||
continue;
|
||||
}
|
||||
// TODO: Try to reduce resulting texture size in subpixel rendering mode,
|
||||
// since freetype for some glyphs not only triples width/height by 3, but also adds extra padding and extra(meaningful?) pixels.
|
||||
// NOTE: looks like it adds 2 pixels to the left and right in FT_LOAD_TARGET_LCD mode, so we should take this into account in FillSubpixelData.
|
||||
// https://freetype.org/freetype2/docs/reference/ft2-lcd_rendering.html
|
||||
// So, the possible approach to try is:
|
||||
// 1) render glyph here with FT_LOAD_RENDER mode;
|
||||
// 2) render glyph in FillGlyphsInfo with FT_LOAD_RENDER | FT_LOAD_TARGET_LCD mode;
|
||||
// 3) take into account all mentioned things above for proper mapping.
|
||||
FT_GlyphSlot slot = face->glyph;
|
||||
GlyphForPacking& glyph = allGlyphs.emplace_back(codepoint, ScaleGlyphSize(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; });
|
||||
// make atlas in square form, so that atlasWidth +- equals atlasHeight
|
||||
return { allGlyphs, ceil(sqrt(area / (m_channelsCount == 1 ? 1 : 3))) };
|
||||
}
|
||||
|
||||
Math::Vector2ui BitmapFontAtlasGenerator::ScaleGlyphSize(unsigned int w, unsigned int h) const
|
||||
{
|
||||
if (m_subpixelLayout == SubpixelLayout::UNKNOWN || m_channelsCount == 1)
|
||||
{
|
||||
return { w, h };
|
||||
}
|
||||
|
||||
if (m_subpixelLayout.IsHorizontalSubpixelLayout())
|
||||
{
|
||||
assert(w % 3 == 0);
|
||||
w /= 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(h % 3 == 0);
|
||||
h /= 3;
|
||||
}
|
||||
return { w, h };
|
||||
}
|
||||
|
||||
FT_Int32 BitmapFontAtlasGenerator::GetGlyphRenderMode() const
|
||||
{
|
||||
if (m_channelsCount == 1)
|
||||
{
|
||||
return FT_LOAD_RENDER;
|
||||
}
|
||||
FT_Int32 glyphRenderMode = FT_LOAD_RENDER;
|
||||
if (m_subpixelLayout < SubpixelLayout::RGBV)
|
||||
{
|
||||
glyphRenderMode |= FT_LOAD_TARGET_LCD;
|
||||
}
|
||||
else if (m_subpixelLayout < SubpixelLayout::UNKNOWN)
|
||||
{
|
||||
glyphRenderMode |= FT_LOAD_TARGET_LCD_V;
|
||||
}
|
||||
return glyphRenderMode;
|
||||
}
|
||||
|
||||
void BitmapFontAtlasGenerator::FillGlyphsInfo(const std::vector<GlyphForPacking>& allGlyphs, const FtFaceRecPtr& face, double scaleFactor)
|
||||
{
|
||||
size_t loadedGlyphs = 0;
|
||||
for (const GlyphForPacking& glyph : allGlyphs)
|
||||
{
|
||||
FT_Error error = FT_Load_Char(face.get(), glyph.code, GetGlyphRenderMode());
|
||||
if (error)
|
||||
{
|
||||
Logger::SCENE->error("FT_Load_Char for codepoint {} has failed. {}", glyph.code,
|
||||
GetFreetypeErrorDescription(error));
|
||||
continue;
|
||||
}
|
||||
|
||||
FT_GlyphSlot slot = face->glyph;
|
||||
if (m_channelsCount == 1)
|
||||
{
|
||||
char* baseAddress = static_cast<char*>(m_atlasData->GetTexture()->textureBuffer)
|
||||
+ glyph.firstGlyphByteInAtlas;
|
||||
for (int row = 0; row < slot->bitmap.rows; row++)
|
||||
{
|
||||
std::memcpy(baseAddress - row * m_atlasData->GetTexture()->resolution.x,
|
||||
&slot->bitmap.buffer[row * slot->bitmap.pitch], slot->bitmap.width);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FillSubpixelData(slot->bitmap, glyph);
|
||||
}
|
||||
|
||||
GlyphInfo& glyphInfo = m_atlasData->GetGlyphs()[glyph.code];
|
||||
const Math::Vector2d glyphMetrics = { slot->metrics.width * scaleFactor,
|
||||
slot->metrics.height * scaleFactor };
|
||||
const Math::Vector2d glyphBearing = { slot->metrics.horiBearingX * scaleFactor,
|
||||
slot->metrics.horiBearingY * scaleFactor };
|
||||
|
||||
const Math::Vector2ui scaledAtlasSize = ScaleGlyphSize(slot->bitmap.width, slot->bitmap.rows);
|
||||
Math::AABB glyphAtlasAABB(Math::Vector3f(glyph.atlasPos.x, glyph.atlasPos.y, 0), Math::Vector3f(glyph.atlasPos.x + scaledAtlasSize.x, glyph.atlasPos.y + scaledAtlasSize.y, 0));
|
||||
SetGlyphData(glyphInfo, glyphBearing, glyphMetrics, glyphAtlasAABB, slot->advance.x * scaleFactor);
|
||||
loadedGlyphs++;
|
||||
}
|
||||
Logger::SCENE->debug("Created atlas with {} glyphs, {} glyphs could not be loaded", loadedGlyphs, allGlyphs.size() - loadedGlyphs);
|
||||
}
|
||||
|
||||
void BitmapFontAtlasGenerator::FillSubpixelData(const FT_Bitmap& bitmap, const GlyphForPacking& glyph)
|
||||
{
|
||||
Texture* tex = m_atlasData->GetTexture();
|
||||
char* texBuffer = static_cast<char*>(tex->textureBuffer);
|
||||
if (m_subpixelLayout.IsHorizontalSubpixelLayout())
|
||||
{
|
||||
// RGB RGB RGB
|
||||
assert(bitmap.width % 3 == 0);
|
||||
for (int row = 0; row < bitmap.rows; row++)
|
||||
{
|
||||
for (int col = 0, atlasPos = 0; col < bitmap.width; col += 3, atlasPos += 4)
|
||||
{
|
||||
const size_t bitmapPos = row * bitmap.pitch + col;
|
||||
const size_t texturePos = (glyph.firstGlyphByteInAtlas - row * tex->resolution.x * m_channelsCount) + atlasPos;
|
||||
const uint8_t rgb[3] = { bitmap.buffer[bitmapPos], bitmap.buffer[bitmapPos + 1],
|
||||
bitmap.buffer[bitmapPos + 2] };
|
||||
std::memcpy(texBuffer + texturePos, rgb, 3);
|
||||
texBuffer[texturePos + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RRR
|
||||
// GGG
|
||||
// BBB
|
||||
assert(bitmap.rows % 3 == 0);
|
||||
for (int row = 0; row < bitmap.rows; row += 3)
|
||||
{
|
||||
for (int col = 0; col < bitmap.width; col++)
|
||||
{
|
||||
const size_t bitmapPos = col + (bitmap.pitch * row);
|
||||
const size_t texturePos = (glyph.firstGlyphByteInAtlas + col * m_channelsCount)
|
||||
- ((row / 3) * (tex->resolution.x * m_channelsCount));
|
||||
const uint8_t rgb[3] = { bitmap.buffer[bitmapPos + 2 * bitmap.pitch],
|
||||
bitmap.buffer[bitmapPos + bitmap.pitch], bitmap.buffer[bitmapPos] };
|
||||
std::memcpy(texBuffer + texturePos, rgb, 3);
|
||||
texBuffer[texturePos + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
61
openVulkanoCpp/Scene/Text/BitmapFontAtlasGenerator.hpp
Normal file
61
openVulkanoCpp/Scene/Text/BitmapFontAtlasGenerator.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 "FontAtlasGeneratorBase.hpp"
|
||||
#include "SubpixelLayout.hpp"
|
||||
#include "Shelf.hpp"
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
class FontPixelSizeConfig
|
||||
{
|
||||
public:
|
||||
FontPixelSizeConfig(float size = 24.f, float dpi = 72.f, bool isPixelSize = true)
|
||||
: m_size(size), m_dpi(dpi), m_isPixelSize(isPixelSize)
|
||||
{
|
||||
}
|
||||
void SetSize(float size) { m_size = size; }
|
||||
void SetDpi(float dpi) { m_dpi = dpi; }
|
||||
void SetIsPixelSize(bool isPixelSize) { m_isPixelSize = isPixelSize; }
|
||||
[[nodiscard]] float GetSize() const { return m_size; }
|
||||
[[nodiscard]] float GetDpi() const { return m_dpi; }
|
||||
[[nodiscard]] bool GetIsPixelSize() const { return m_isPixelSize; }
|
||||
[[nodiscard]] unsigned CalculatePixelSize() const { return m_isPixelSize ? m_size : (m_size * m_dpi) / 72.0f; }
|
||||
private:
|
||||
float m_size;
|
||||
float m_dpi;
|
||||
bool m_isPixelSize;
|
||||
};
|
||||
|
||||
class BitmapFontAtlasGenerator : public FontAtlasGeneratorBase
|
||||
{
|
||||
public:
|
||||
BitmapFontAtlasGenerator(FontPixelSizeConfig config = FontPixelSizeConfig(),
|
||||
std::optional<SubpixelLayout> subpixelLayout = std::nullopt)
|
||||
: FontAtlasGeneratorBase(subpixelLayout.has_value() && *subpixelLayout < SubpixelLayout::UNKNOWN ? 4 : 1)
|
||||
, m_pixelSizeConfig(config)
|
||||
, m_subpixelLayout(subpixelLayout.value_or(SubpixelLayout::UNKNOWN))
|
||||
{
|
||||
}
|
||||
void GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput = std::nullopt) override;
|
||||
void GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput = std::nullopt) override;
|
||||
private:
|
||||
void Generate(const std::variant<std::string, Array<char>>& source, const std::set<uint32_t>& chset, const std::optional<std::string>& pngOutput);
|
||||
void FillGlyphsInfo(const std::vector<GlyphForPacking>& allGlyphs, const FtFaceRecPtr& face, double scaleFactor);
|
||||
void FillSubpixelData(const FT_Bitmap& bitmap, const GlyphForPacking& glyph);
|
||||
FT_Int32 GetGlyphRenderMode() const;
|
||||
// tmp function
|
||||
Math::Vector2ui ScaleGlyphSize(unsigned int w, unsigned int h) const;
|
||||
std::pair<std::vector<GlyphForPacking>, double> InitGlyphsForPacking(const std::set<uint32_t>& chset, const FtFaceRecPtr& face);
|
||||
private:
|
||||
FontPixelSizeConfig m_pixelSizeConfig;
|
||||
SubpixelLayout m_subpixelLayout;
|
||||
};
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
#include "FontAtlasFactory.hpp"
|
||||
#include "Scene/SdfFontAtlasGenerator.hpp"
|
||||
#include "Scene/BitmapFontAtlasGenerator.hpp"
|
||||
#include "SdfFontAtlasGenerator.hpp"
|
||||
#include "BitmapFontAtlasGenerator.hpp"
|
||||
#include "Host/SystemFontResolver.hpp"
|
||||
#include "Base/Logger.hpp"
|
||||
#include "Host/ResourceLoader.hpp"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "FontAtlas.hpp"
|
||||
#include "Scene/SubpixelLayout.hpp"
|
||||
#include "SubpixelLayout.hpp"
|
||||
#include "Data/Containers/Array.hpp"
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
|
||||
105
openVulkanoCpp/Scene/Text/FontAtlasGeneratorBase.cpp
Normal file
105
openVulkanoCpp/Scene/Text/FontAtlasGeneratorBase.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 "FontAtlas.hpp"
|
||||
#include "Base/Logger.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
std::pair<FtLibraryRecPtr, FtFaceRecPtr>
|
||||
FontAtlasGeneratorBase::InitFreetype(const std::variant<std::string, Array<char>>& 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<std::string>(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::SetGlyphData(GlyphInfo& info, Math::Vector2d bearing, Math::Vector2d size,
|
||||
const Math::AABB& aabb, double advance)
|
||||
{
|
||||
const auto& resolution = m_atlasData->GetTexture()->resolution;
|
||||
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.pos[0].x = bearing.x;
|
||||
info.pos[0].y = size.y - bearing.y;
|
||||
info.uv[0].x = l / resolution.x;
|
||||
info.uv[0].y = b / resolution.y;
|
||||
|
||||
info.pos[1].x = bearing.x + size.x;
|
||||
info.pos[1].y = size.y - bearing.y;
|
||||
info.uv[1].x = r / resolution.x;
|
||||
info.uv[1].y = b / resolution.y;
|
||||
|
||||
info.pos[2].x = bearing.x + size.x;
|
||||
info.pos[2].y = bearing.y;
|
||||
info.uv[2].x = r / resolution.x;
|
||||
info.uv[2].y = t / resolution.y;
|
||||
|
||||
info.pos[3].x = bearing.x;
|
||||
info.pos[3].y = bearing.y;
|
||||
info.uv[3].x = l / resolution.x;
|
||||
info.uv[3].y = t / resolution.y;
|
||||
|
||||
info.advance = advance;
|
||||
}
|
||||
|
||||
std::set<uint32_t> FontAtlasGeneratorBase::LoadAllGlyphs(const std::variant<std::string, Array<char>>& data)
|
||||
{
|
||||
const auto& [lib, face] = InitFreetype(data);
|
||||
std::set<uint32_t> 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;
|
||||
}
|
||||
|
||||
}
|
||||
35
openVulkanoCpp/Scene/Text/FontAtlasGeneratorBase.hpp
Normal file
35
openVulkanoCpp/Scene/Text/FontAtlasGeneratorBase.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 "IFontAtlasGenerator.hpp"
|
||||
#include "Math/AABB.hpp"
|
||||
#include "Extensions/FreetypeHelper.hpp"
|
||||
#include <variant>
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
class GlyphInfo;
|
||||
|
||||
class FontAtlasGeneratorBase : public IFontAtlasGenerator
|
||||
{
|
||||
protected:
|
||||
int m_channelsCount;
|
||||
std::shared_ptr<FontAtlas> m_atlasData;
|
||||
|
||||
public:
|
||||
FontAtlasGeneratorBase(const int channelsCount) : m_channelsCount(channelsCount) {}
|
||||
[[nodiscard]] const std::shared_ptr<FontAtlas>& GetAtlas() const final { return m_atlasData; }
|
||||
[[nodiscard]] int GetAtlasChannelsCount() const { return m_channelsCount; }
|
||||
[[nodiscard]] static std::set<uint32_t> LoadAllGlyphs(const std::variant<std::string, Array<char>>& data);
|
||||
|
||||
protected:
|
||||
void SetGlyphData(GlyphInfo& info, Math::Vector2d bearing, Math::Vector2d size, const Math::AABB& aabb, double advance);
|
||||
[[nodiscard]] static std::string GetFreetypeErrorDescription(FT_Error error);
|
||||
[[nodiscard]] static std::pair<FtLibraryRecPtr, FtFaceRecPtr> InitFreetype(const std::variant<std::string, Array<char>>& source);
|
||||
};
|
||||
}
|
||||
29
openVulkanoCpp/Scene/Text/IFontAtlasGenerator.hpp
Normal file
29
openVulkanoCpp/Scene/Text/IFontAtlasGenerator.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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 <Data/Containers/Array.hpp>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
struct FontAtlas;
|
||||
|
||||
class IFontAtlasGenerator
|
||||
{
|
||||
public:
|
||||
virtual ~IFontAtlasGenerator() = default;
|
||||
virtual void GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput = std::nullopt) = 0;
|
||||
virtual void GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput = std::nullopt) = 0;
|
||||
virtual const std::shared_ptr<FontAtlas>& GetAtlas() const = 0;
|
||||
};
|
||||
}
|
||||
220
openVulkanoCpp/Scene/Text/SdfFontAtlasGenerator.cpp
Normal file
220
openVulkanoCpp/Scene/Text/SdfFontAtlasGenerator.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#if __has_include("msdfgen.h")
|
||||
#include "FontAtlas.hpp"
|
||||
#include "SdfFontAtlasGenerator.hpp"
|
||||
#include "Base/Logger.hpp"
|
||||
#include <msdfgen.h>
|
||||
#include <msdfgen-ext.h>
|
||||
#include <msdf-atlas-gen/msdf-atlas-gen.h>
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
SdfFontAtlasGeneratorConfig SdfFontAtlasGeneratorConfig::sdfDefaultConfig = { 42, 1.0, 5 };
|
||||
SdfFontAtlasGeneratorConfig SdfFontAtlasGeneratorConfig::msdfDefaultConfig = { 32, 1.0, 3 };
|
||||
|
||||
template<int Channels>
|
||||
void SdfFontAtlasGeneratorGeneric<Channels>::GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput)
|
||||
{
|
||||
msdfgen::FreetypeHandle* ft;
|
||||
msdfgen::FontHandle* font;
|
||||
InitFreetypeFromFile(ft, font, fontFile);
|
||||
msdf_atlas::Charset s;
|
||||
std::for_each(charset.begin(), charset.end(), [&](uint32_t unicode) { s.add(unicode); });
|
||||
Generate(ft, font, s, pngOutput);
|
||||
}
|
||||
|
||||
template<int Channels> SdfFontAtlasGeneratorGeneric<Channels>::SdfFontAtlasGeneratorGeneric() : FontAtlasGeneratorBase(Channels)
|
||||
{
|
||||
if constexpr (Channels == 1) m_config = SdfFontAtlasGeneratorConfig::sdfDefaultConfig;
|
||||
else m_config = SdfFontAtlasGeneratorConfig::msdfDefaultConfig;
|
||||
}
|
||||
|
||||
template<int Channels>
|
||||
void SdfFontAtlasGeneratorGeneric<Channels>::GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput)
|
||||
{
|
||||
msdfgen::FreetypeHandle* ft;
|
||||
msdfgen::FontHandle* font;
|
||||
InitFreetypeFromBuffer(ft, font, (const msdfgen::byte*)(fontData.Data()), fontData.Size());
|
||||
msdf_atlas::Charset s;
|
||||
std::for_each(charset.begin(), charset.end(), [&](uint32_t unicode) { s.add(unicode); });
|
||||
Generate(ft, font, s, pngOutput);
|
||||
}
|
||||
|
||||
template<int Channels>
|
||||
void SdfFontAtlasGeneratorGeneric<Channels>::GenerateAtlas(const std::string& fontFile,
|
||||
const msdf_atlas::Charset& charset,
|
||||
const std::optional<std::string>& pngOutput)
|
||||
{
|
||||
// TODO: dynamic atlas and add only those symbols which are not present yet in current atlas
|
||||
msdfgen::FreetypeHandle* ft;
|
||||
msdfgen::FontHandle* font;
|
||||
InitFreetypeFromFile(ft, font, fontFile);
|
||||
Generate(ft, font, charset, pngOutput);
|
||||
}
|
||||
|
||||
template<int Channels>
|
||||
void SdfFontAtlasGeneratorGeneric<Channels>::GenerateAtlas(const msdfgen::byte* fontData, int length,
|
||||
const msdf_atlas::Charset& charset,
|
||||
const std::optional<std::string>& pngOutput)
|
||||
{
|
||||
msdfgen::FreetypeHandle* ft;
|
||||
msdfgen::FontHandle* font;
|
||||
InitFreetypeFromBuffer(ft, font, fontData, length);
|
||||
Generate(ft, font, charset, pngOutput);
|
||||
}
|
||||
|
||||
template<int Channels>
|
||||
void SdfFontAtlasGeneratorGeneric<Channels>::InitFreetypeFromFile(msdfgen::FreetypeHandle*& ft,
|
||||
msdfgen::FontHandle*& font,
|
||||
const std::string& fontFile)
|
||||
{
|
||||
ft = msdfgen::initializeFreetype();
|
||||
if (!ft)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize freetype");
|
||||
}
|
||||
font = loadFont(ft, fontFile.data());
|
||||
if (!font)
|
||||
{
|
||||
deinitializeFreetype(ft);
|
||||
ft = nullptr;
|
||||
throw std::runtime_error(fmt::format("Failed to load font from file {0}", fontFile.data()));
|
||||
}
|
||||
}
|
||||
|
||||
template<int Channels>
|
||||
void SdfFontAtlasGeneratorGeneric<Channels>::InitFreetypeFromBuffer(msdfgen::FreetypeHandle*& ft,
|
||||
msdfgen::FontHandle*& font,
|
||||
const msdfgen::byte* fontData, int length)
|
||||
{
|
||||
ft = msdfgen::initializeFreetype();
|
||||
if (!ft)
|
||||
{
|
||||
throw std::runtime_error("Failed to initialize freetype");
|
||||
}
|
||||
font = loadFontData(ft, fontData, length);
|
||||
if (!font)
|
||||
{
|
||||
deinitializeFreetype(ft);
|
||||
ft = nullptr;
|
||||
throw std::runtime_error("Failed to load font data from given buffer");
|
||||
}
|
||||
}
|
||||
|
||||
template<int Channels>
|
||||
void SdfFontAtlasGeneratorGeneric<Channels>::Generate(msdfgen::FreetypeHandle* ft, msdfgen::FontHandle* font,
|
||||
const msdf_atlas::Charset& chset,
|
||||
const std::optional<std::string>& pngOutput)
|
||||
{
|
||||
std::vector<msdf_atlas::GlyphGeometry> glyphsGeometry;
|
||||
// FontGeometry is a helper class that loads a set of glyphs from a single font.
|
||||
msdf_atlas::FontGeometry fontGeometry(&glyphsGeometry);
|
||||
fontGeometry.loadCharset(font, 1, chset);
|
||||
|
||||
if constexpr (Channels == 3)
|
||||
{
|
||||
const double maxCornerAngle = 3.0;
|
||||
for (msdf_atlas::GlyphGeometry& glyph : glyphsGeometry)
|
||||
{
|
||||
glyph.edgeColoring(&msdfgen::edgeColoringByDistance, maxCornerAngle, 0);
|
||||
}
|
||||
}
|
||||
|
||||
msdf_atlas::TightAtlasPacker packer;
|
||||
packer.setDimensionsConstraint(msdf_atlas::DimensionsConstraint::SQUARE);
|
||||
int width, height;
|
||||
const int glyphsPerRow = std::sqrt(glyphsGeometry.size());
|
||||
const int glyphSize = m_config.glyphSize;
|
||||
const int rowWidth = glyphSize * glyphsPerRow;
|
||||
packer.setDimensions(rowWidth, rowWidth);
|
||||
// something to play with. should not be too high.
|
||||
// more value - more sdf impact
|
||||
packer.setPixelRange(m_config.pixelRange);
|
||||
packer.setMiterLimit(m_config.miterLimit);
|
||||
packer.pack(glyphsGeometry.data(), glyphsGeometry.size());
|
||||
packer.getDimensions(width, height);
|
||||
|
||||
Generator generator;
|
||||
generator.resize(width, height);
|
||||
msdf_atlas::GeneratorAttributes attributes;
|
||||
generator.setAttributes(attributes);
|
||||
generator.setThreadCount(4);
|
||||
generator.generate(glyphsGeometry.data(), glyphsGeometry.size());
|
||||
|
||||
int idx = 0;
|
||||
m_atlasData =
|
||||
std::make_shared<FontAtlas>(Math::Vector2ui { width, height }, fontGeometry.getMetrics().lineHeight,
|
||||
channelsCount == 1 ? FontAtlasType::SDF : FontAtlasType::MSDF,
|
||||
m_channelsCount == 1 ? DataFormat::R8_UNORM : DataFormat::R8G8B8A8_UNORM);
|
||||
|
||||
if constexpr (Channels == 3)
|
||||
{
|
||||
// store RGB as RGBA
|
||||
const msdfgen::BitmapConstRef<msdfgen::byte, 3> storage = generator.atlasStorage();
|
||||
msdfgen::byte* data = static_cast<msdfgen::byte*>(m_atlasData->GetTexture()->textureBuffer);
|
||||
for (size_t srcPos = 0, dstPos = 0; srcPos < width * height * 3; srcPos += 3, dstPos += 4)
|
||||
{
|
||||
data[dstPos] = storage.pixels[srcPos];
|
||||
data[dstPos + 1] = storage.pixels[srcPos + 1];
|
||||
data[dstPos + 2] = storage.pixels[srcPos + 2];
|
||||
data[dstPos + 3] = 255;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const msdfgen::BitmapConstRef<msdfgen::byte, 1>& storage = generator.atlasStorage();
|
||||
memcpy(m_atlasData->GetTexture()->textureBuffer, storage.pixels, width * height);
|
||||
}
|
||||
|
||||
struct Bbox
|
||||
{
|
||||
double l = 0, r = 0, t = 0, b = 0;
|
||||
};
|
||||
|
||||
for (const auto& glyph : glyphsGeometry)
|
||||
{
|
||||
GlyphInfo& info = m_atlasData->GetGlyphs()[glyph.getCodepoint()];
|
||||
const msdf_atlas::GlyphBox& glyphBox = generator.getLayout()[idx++];
|
||||
|
||||
Bbox glyphBaselineBbox, glyphAtlasBbox;
|
||||
glyph.getQuadPlaneBounds(glyphBaselineBbox.l, glyphBaselineBbox.b, glyphBaselineBbox.r,
|
||||
glyphBaselineBbox.t);
|
||||
glyph.getQuadAtlasBounds(glyphAtlasBbox.l, glyphAtlasBbox.b, glyphAtlasBbox.r, glyphAtlasBbox.t);
|
||||
double bearingX = glyphBox.bounds.l;
|
||||
double bearingY = glyphBox.bounds.t;
|
||||
double w = glyphBaselineBbox.r - glyphBaselineBbox.l;
|
||||
double h = glyphBaselineBbox.t - glyphBaselineBbox.b;
|
||||
|
||||
// UV mapping
|
||||
double l = glyphAtlasBbox.l;
|
||||
double r = glyphAtlasBbox.r;
|
||||
double t = glyphAtlasBbox.t;
|
||||
double b = glyphAtlasBbox.b;
|
||||
|
||||
Math::AABB glyphAtlasAABB;
|
||||
glyphAtlasAABB.min.x = l;
|
||||
glyphAtlasAABB.min.y = b;
|
||||
glyphAtlasAABB.max.x = r;
|
||||
glyphAtlasAABB.max.y = t;
|
||||
SetGlyphData(info, { bearingX, bearingY }, { w, h }, glyphAtlasAABB, glyphBox.advance);
|
||||
}
|
||||
|
||||
if (pngOutput && !pngOutput->empty())
|
||||
{
|
||||
m_atlasData->Save(*pngOutput);
|
||||
}
|
||||
destroyFont(font);
|
||||
deinitializeFreetype(ft);
|
||||
}
|
||||
|
||||
template class SdfFontAtlasGeneratorGeneric<1>;
|
||||
template class SdfFontAtlasGeneratorGeneric<3>;
|
||||
}
|
||||
#endif
|
||||
68
openVulkanoCpp/Scene/Text/SdfFontAtlasGenerator.hpp
Normal file
68
openVulkanoCpp/Scene/Text/SdfFontAtlasGenerator.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#if __has_include("msdfgen.h")
|
||||
|
||||
#include "FontAtlasGeneratorBase.hpp"
|
||||
#include <msdfgen.h>
|
||||
#include <msdf-atlas-gen/msdf-atlas-gen.h>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#define MSDFGEN_AVAILABLE 1
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
|
||||
struct SdfFontAtlasGeneratorConfig
|
||||
{
|
||||
static SdfFontAtlasGeneratorConfig sdfDefaultConfig;
|
||||
static SdfFontAtlasGeneratorConfig msdfDefaultConfig;
|
||||
int glyphSize;
|
||||
double miterLimit;
|
||||
msdfgen::Range pixelRange;
|
||||
};
|
||||
|
||||
template<int Channels>
|
||||
class SdfFontAtlasGeneratorGeneric final : public FontAtlasGeneratorBase
|
||||
{
|
||||
private:
|
||||
using SdfGenerator = msdf_atlas::ImmediateAtlasGenerator<float, 1, msdf_atlas::sdfGenerator,
|
||||
msdf_atlas::BitmapAtlasStorage<msdfgen::byte, 1>>;
|
||||
using MsdfGenerator = msdf_atlas::ImmediateAtlasGenerator<float, 3, msdf_atlas::msdfGenerator,
|
||||
msdf_atlas::BitmapAtlasStorage<msdfgen::byte, 3>>;
|
||||
public:
|
||||
using Generator = std::conditional<Channels == 1, SdfGenerator, MsdfGenerator>::type;
|
||||
using Config = SdfFontAtlasGeneratorConfig;
|
||||
static constexpr int channelsCount = (Channels == 1 ? 1 : 4);
|
||||
SdfFontAtlasGeneratorGeneric();
|
||||
void GenerateAtlas(const std::string& fontFile, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput = std::nullopt) override;
|
||||
void GenerateAtlas(const Array<char>& fontData, const std::set<uint32_t>& charset,
|
||||
const std::optional<std::string>& pngOutput = std::nullopt) override;
|
||||
void GenerateAtlas(const std::string& fontFile, const msdf_atlas::Charset& charset = msdf_atlas::Charset::ASCII,
|
||||
const std::optional<std::string>& pngOutput = std::nullopt);
|
||||
void GenerateAtlas(const msdfgen::byte* fontData, int length,
|
||||
const msdf_atlas::Charset& charset = msdf_atlas::Charset::ASCII,
|
||||
const std::optional<std::string>& pngOutput = std::nullopt);
|
||||
void SetGeneratorConfig(const Config& config) { m_config = config; }
|
||||
Config& GetGeneratorConfig() { return m_config; }
|
||||
private:
|
||||
void InitFreetypeFromFile(msdfgen::FreetypeHandle*& ft, msdfgen::FontHandle*& font, const std::string& file);
|
||||
void InitFreetypeFromBuffer(msdfgen::FreetypeHandle*& ft, msdfgen::FontHandle*& font,
|
||||
const msdfgen::byte* fontData, int length);
|
||||
void Generate(msdfgen::FreetypeHandle* ft, msdfgen::FontHandle* font, const msdf_atlas::Charset& chset,
|
||||
const std::optional<std::string>& pngOutput);
|
||||
private:
|
||||
Config m_config;
|
||||
};
|
||||
using SdfFontAtlasGenerator = SdfFontAtlasGeneratorGeneric<1>;
|
||||
using MsdfFontAtlasGenerator = SdfFontAtlasGeneratorGeneric<3>;
|
||||
}
|
||||
#endif
|
||||
109
openVulkanoCpp/Scene/Text/Shelf.hpp
Normal file
109
openVulkanoCpp/Scene/Text/Shelf.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 "Extensions/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, int channelsCount);
|
||||
|
||||
Shelf(uint32_t width, uint32_t height, int pixelSize, uint32_t prevShelvesHeight)
|
||||
: m_width(width), m_height(height), m_remainingWidth(width), m_pixelSize(pixelSize), m_prevShelvesHeight(prevShelvesHeight) {}
|
||||
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); }
|
||||
uint32_t GetPrevShelvesHeight() const { return m_prevShelvesHeight; }
|
||||
int GetPixelSize() const { return m_pixelSize; }
|
||||
|
||||
std::optional<std::pair<uint32_t, 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;
|
||||
|
||||
uint32_t hOffset = m_height - (m_height - glyphHeight);
|
||||
uint32_t glyphFirstByte = (insertionPos * m_pixelSize)
|
||||
+ ((hOffset * m_width * m_pixelSize) - (m_width * m_pixelSize))
|
||||
+ (m_prevShelvesHeight * m_width * m_pixelSize);
|
||||
|
||||
return std::make_pair(insertionPos, glyphFirstByte);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t m_width;
|
||||
uint32_t m_height;
|
||||
uint32_t m_remainingWidth;
|
||||
uint32_t m_nextGlyphPos = 0;
|
||||
uint32_t m_prevShelvesHeight;
|
||||
int m_pixelSize;
|
||||
};
|
||||
|
||||
std::vector<Shelf> Shelf::CreateShelves(uint32_t atlasWidth, std::vector<GlyphForPacking>& glyphs,
|
||||
const FtFaceRecPtr& face, int channelsCount)
|
||||
{
|
||||
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<std::pair<uint32_t, uint32_t>> opt = shelf.AddGlyph(glyph.size.x, glyph.size.y))
|
||||
{
|
||||
glyph.firstGlyphByteInAtlas = opt->second;
|
||||
glyph.atlasPos.x = opt->first;
|
||||
glyph.atlasPos.y = totalPrevShelvesHeight;
|
||||
needNewShelf = false;
|
||||
break;
|
||||
}
|
||||
totalPrevShelvesHeight += shelf.GetHeight();
|
||||
}
|
||||
|
||||
if (needNewShelf)
|
||||
{
|
||||
shelves.emplace_back(atlasWidth, glyph.size.y, channelsCount, totalPrevShelvesHeight);
|
||||
Shelf& shelf = shelves.back();
|
||||
uint32_t firstByte = (*shelf.AddGlyph(glyph.size.x, glyph.size.y)).second;
|
||||
glyph.firstGlyphByteInAtlas = firstByte;
|
||||
glyph.atlasPos.x = 0;
|
||||
glyph.atlasPos.y = totalPrevShelvesHeight;
|
||||
}
|
||||
}
|
||||
return shelves;
|
||||
}
|
||||
}
|
||||
75
openVulkanoCpp/Scene/Text/SubpixelLayout.hpp
Normal file
75
openVulkanoCpp/Scene/Text/SubpixelLayout.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 "Scene/DataFormat.hpp"
|
||||
#include <magic_enum.hpp>
|
||||
#include <cinttypes>
|
||||
#include <string_view>
|
||||
|
||||
namespace OpenVulkano
|
||||
{
|
||||
class SubpixelLayout
|
||||
{
|
||||
public:
|
||||
enum Layout : uint32_t
|
||||
{
|
||||
RGB,
|
||||
BGR,
|
||||
RGBV,
|
||||
BGRV,
|
||||
// unknown and auto must be last two
|
||||
UNKNOWN,
|
||||
NONE = UNKNOWN,
|
||||
AUTO = UNKNOWN
|
||||
};
|
||||
|
||||
constexpr SubpixelLayout() = default;
|
||||
|
||||
constexpr SubpixelLayout(Layout layout) : m_layout(layout) {}
|
||||
|
||||
[[nodiscard]] DataFormat GetTextureDataFormat() const
|
||||
{
|
||||
if (m_layout >= SubpixelLayout::UNKNOWN)
|
||||
{
|
||||
return DataFormat::R8_UINT;
|
||||
}
|
||||
if (m_layout == SubpixelLayout::BGR || m_layout == SubpixelLayout::BGRV)
|
||||
{
|
||||
return DataFormat::B8G8R8A8_UINT;
|
||||
}
|
||||
return DataFormat::R8G8B8A8_UINT;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view GetName() const { return magic_enum::enum_name(m_layout); }
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(Layout rhs) const
|
||||
{
|
||||
return m_layout == rhs;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator!=(Layout rhs) const
|
||||
{
|
||||
return m_layout != rhs;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr operator uint32_t() const
|
||||
{
|
||||
return m_layout;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsHorizontalSubpixelLayout() const
|
||||
{
|
||||
return m_layout == SubpixelLayout::RGB || m_layout == SubpixelLayout::BGR;
|
||||
}
|
||||
|
||||
explicit operator bool() const { return m_layout < Layout::UNKNOWN; }
|
||||
|
||||
private:
|
||||
Layout m_layout = Layout::UNKNOWN;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user