Skip to content

Commit

Permalink
Cleaned up the Font.cpp implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
WillisMedwell committed Feb 14, 2024
1 parent 8f50720 commit d691136
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 111 deletions.
4 changes: 3 additions & 1 deletion code/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
cmake_minimum_required(VERSION 3.16)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Enable unity builds
set(CMAKE_UNITY_BUILD true)

# Enabling LTO
include(CheckIPOSupported)
check_ipo_supported(RESULT has_ipo_support)
Expand Down
6 changes: 3 additions & 3 deletions code/Engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ else()
OpenGL::GL OpenAL::OpenAL GLEW::GLEW glfw
)
if(MSVC)
target_compile_options(Engine PUBLIC
"$<$<CONFIG:Debug>:/Od;/Zi;/RTCs>"
target_compile_options(Engine PRIVATE
"$<$<CONFIG:Debug>:/Od;/ZI;/RTCs;/MDd>"
"$<$<CONFIG:Release>:/O2;/arch:AVX>"
)
else()
target_compile_options(Engine PUBLIC
target_compile_options(Engine PRIVATE
"$<$<CONFIG:Debug>:-O0;-g;-fno-omit-frame-pointer>"
"$<$<CONFIG:Release>:-O3;-march=native>"
)
Expand Down
7 changes: 1 addition & 6 deletions code/Engine/include/Media/Image.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,7 @@ namespace Media {
bool is_gamma_corrected) noexcept
-> Utily::Result<void, Utily::Error>;

[[nodiscard]] auto init_raw(
std::vector<uint8_t>&& raw_data,
uint32_t width,
uint32_t height,
ColourFormat format) noexcept
-> Utily::Result<void, Utily::Error>;
void init_raw(std::vector<uint8_t>&& raw_data, uint32_t width, uint32_t height, ColourFormat format) noexcept;

[[nodiscard]] auto save_to_disk(std::filesystem::path path) noexcept
-> Utily::Result<void, Utily::Error>;
Expand Down
2 changes: 1 addition & 1 deletion code/Engine/include/Renderer/Fence.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Renderer {
public:
Fence() = default;
Fence(const Fence&) = delete;
Fence(Fence&& other)
Fence(Fence&& other) noexcept
: _id(std::exchange(other._id, std::nullopt)) {
}

Expand Down
167 changes: 84 additions & 83 deletions code/Engine/src/Media/Font.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include "Media/Font.hpp"

#include <ft2build.h>
#include <array>
#include <iostream>
#include <ranges>
// #include <mdspan>

#include <ft2build.h>
#include FT_FREETYPE_H

struct FreeType {
Expand Down Expand Up @@ -37,113 +40,111 @@ namespace Media {
return {};
}

struct FreeTypeGlyph {
std::vector<uint8_t> buffer;
std::ptrdiff_t width, height, spanline;

constexpr FreeTypeGlyph() = default;

constexpr FreeTypeGlyph(const FT_GlyphSlot& slot)
: width(slot->bitmap.width)
, height(slot->bitmap.rows)
, spanline(slot->bitmap_top) {
auto buffer_data = std::span<uint8_t>(slot->bitmap.buffer, slot->bitmap.width * slot->bitmap.rows);
buffer = std::vector<uint8_t> { buffer_data.begin(), buffer_data.end() };
}
};

struct GlyphInfo {
std::ptrdiff_t width = 0, height = 0, spanline = 0;

GlyphInfo() = default;

GlyphInfo(const FT_GlyphSlot& slot)
: width(slot->bitmap.width)
, height(slot->bitmap.rows + slot->bitmap_top)
, spanline(slot->bitmap_top) { }

static auto take_max_values(const GlyphInfo& lhs, const GlyphInfo& rhs) -> GlyphInfo {
GlyphInfo gi;
gi.width = std::max(lhs.width, rhs.width);
gi.height = std::max(lhs.height, rhs.height);
gi.spanline = std::max(lhs.spanline, rhs.spanline);
return gi;
}
};

auto Font::gen_image_atlas(uint32_t char_height_px) -> Utily::Result<Media::Image, Utily::Error> {
constexpr auto& drawable_chars = FontAtlasConstants::DRAWABLE_CHARS;

// Validate and Scale FreeType Face.
if (_font_face == nullptr) {
return Utily::Error { "Font not initialised." };
}
FT_Face ff = reinterpret_cast<FT_Face>(_font_face);

if (auto error = FT_Set_Pixel_Sizes(ff, char_height_px, char_height_px); error) {
return Utily::Error { FT_Error_String(error) };
}

int ff_bm_max_top = 0;
int ff_bm_max_spanline_dist = 0;
int ff_bm_max_width = 0;

struct FreeTypeGlyph {
std::vector<uint8_t> buffer;
int width, height;
int left, top;
};

constexpr size_t N = FontAtlasConstants::DRAWABLE_CHARS.size();
Utily::StaticVector<FreeTypeGlyph, N> ff_glyphs;

for (const char& c : FontAtlasConstants::DRAWABLE_CHARS) {
auto generate_and_cache_ft_glyph = [&](char c, FreeTypeGlyph& ftg) -> GlyphInfo {
auto glyph_index = FT_Get_Char_Index(ff, static_cast<std::uint32_t>(c));

if (auto error = FT_Load_Glyph(ff, glyph_index, FT_LOAD_DEFAULT); error) [[unlikely]] {
return Utily::Error { FT_Error_String(error) };
throw std::runtime_error(FT_Error_String(error));
}
if (auto error = FT_Render_Glyph(ff->glyph, FT_Render_Mode::FT_RENDER_MODE_NORMAL); error) [[unlikely]] {
return Utily::Error { FT_Error_String(error) };
throw std::runtime_error(FT_Error_String(error));
}
ftg = FreeTypeGlyph(ff->glyph);
return GlyphInfo(ff->glyph);
};
auto cached_ft_bitmaps = std::array<FreeTypeGlyph, drawable_chars.size()> {};

// 1. Transform -> Generates and caches the freetype bitmaps & returns its glyph layout
// 2. Reduce -> Calculate a general glyph layout for the atlas.
auto atlas_info = std::transform_reduce(
drawable_chars.begin(),
drawable_chars.end(),
cached_ft_bitmaps.begin(),
GlyphInfo {},
&GlyphInfo::take_max_values,
generate_and_cache_ft_glyph);

const size_t atlas_buffer_size = atlas_info.width * atlas_info.height * drawable_chars.size();
std::vector<uint8_t> atlas_buffer(atlas_buffer_size);
std::ranges::fill(atlas_buffer, (uint8_t)0);

const int curr_spanline_dist = ff->glyph->bitmap.rows - ff->glyph->bitmap_top;
const int curr_width = ff->glyph->bitmap.width + ff->glyph->bitmap_left;
const int curr_top = ff->glyph->bitmap_top;

ff_bm_max_width = (curr_width > ff_bm_max_width) ? curr_width : ff_bm_max_width;
ff_bm_max_top = (curr_top > ff_bm_max_top) ? curr_top : ff_bm_max_top;
ff_bm_max_spanline_dist = (curr_spanline_dist > ff_bm_max_spanline_dist) ? curr_spanline_dist : ff_bm_max_spanline_dist;

auto glyph_data = std::span<uint8_t> { ff->glyph->bitmap.buffer, ff->glyph->bitmap.width * ff->glyph->bitmap.rows };

ff_glyphs.emplace_back(
FreeTypeGlyph {
.buffer = { glyph_data.begin(), glyph_data.end() },
.width = static_cast<int>(ff->glyph->bitmap.width),
.height = static_cast<int>(ff->glyph->bitmap.rows),
.left = ff->glyph->bitmap_left,
.top = ff->glyph->bitmap_top });
}

const size_t internal_bm_w = ff_bm_max_width;
const size_t internal_bm_h = ff_bm_max_spanline_dist + ff_bm_max_top;
const size_t internal_spanline = ff_bm_max_spanline_dist;

std::vector<uint8_t> img;
img.resize(internal_bm_w * internal_bm_h * FontAtlasConstants::DRAWABLE_CHARS.size());
std::ranges::fill(img, (uint8_t)0);
#if 1
for (const auto [i, c] : FontAtlasConstants::DRAWABLE_CHARS | std::views::enumerate) {
#else
auto& dc = FontAtlasConstants::DRAWABLE_CHARS;
std::ptrdiff_t i = 0;
for (auto iter = dc.begin(); iter != dc.end(); ++iter, ++i) {
#endif
const auto& ff_glyph = ff_glyphs[i];

std::ptrdiff_t bitmap_offset = (internal_bm_w * internal_bm_h * i);
uint8_t* bitmap = img.data() + bitmap_offset;
assert(bitmap_offset < img.size());

for (int y = 0; y < ff_glyph.height; ++y) {
for (int x = 0; x < ff_glyph.width; ++x) {
const uint8_t& val = *(ff_glyph.buffer.data() + (y * ff_glyph.width + x));
uint8_t& dest = *(bitmap + (internal_bm_w * (internal_spanline - ff_glyph.top + y)) + x + ff_glyph.left);
dest = val;
for (std::ptrdiff_t i = 0; i < static_cast<std::ptrdiff_t>(drawable_chars.size()); ++i) {
const auto& bitmap_ft = cached_ft_bitmaps.at(i);
uint8_t* bitmap_atlas = atlas_buffer.data() + (atlas_info.width * atlas_info.height * i);
for (std::ptrdiff_t y = 0; y < bitmap_ft.height; ++y) {
for (std::ptrdiff_t x = 0; x < bitmap_ft.width; ++x) {
const std::ptrdiff_t atlas_y = y + atlas_info.spanline - bitmap_ft.spanline;
const std::ptrdiff_t atlas_x = x;
const std::ptrdiff_t atlas_offset = atlas_info.width * atlas_y + atlas_x;
const std::ptrdiff_t ft_offset = y * bitmap_ft.width + x;
bitmap_atlas[atlas_offset] = bitmap_ft.buffer[ft_offset];
}
}
}

#if 0
for (size_t y = 0; y < internal_bm_h * FontAtlasConstants::DRAWABLE_CHARS.size(); ++y) {
for (size_t x = 0; x < internal_bm_w; ++x) {
if (y == internal_spanline) {
std::cout << '-';
} else {
auto res = static_cast<int>(std::clamp(*(img.data() + (y * internal_bm_w + x)), (uint8_t)0, (uint8_t)9));
if (res == 0) {
std::cout << ' ';
} else {
std::cout << res;
}
#else
// safer version
auto atlas_bitmaps = atlas_buffer | std::views::chunk(atlas_info.width * atlas_info.height);
for (auto [bitmap_ft, bitmap_atlas] : std::views::zip(cached_ft_bitmaps, atlas_bitmaps)) {
for (std::ptrdiff_t y = 0; y < bitmap_ft.height; ++y) {
for (std::ptrdiff_t x = 0; x < bitmap_ft.width; ++x) {
const std::ptrdiff_t atlas_y = y + atlas_info.spanline - bitmap_ft.spanline;
const std::ptrdiff_t atlas_x = x;
const std::ptrdiff_t atlas_offset = atlas_info.width * atlas_y + atlas_x;
const std::ptrdiff_t ft_offset = y * bitmap_ft.width + x;
bitmap_atlas[atlas_offset] = bitmap_ft.buffer[ft_offset];
}
}
std::cout << "|\n|";
}
#endif
Media::Image image;
auto result = image.init_raw(
std::move(img),
internal_bm_w,
internal_bm_h * FontAtlasConstants::DRAWABLE_CHARS.size(),
Media::ColourFormat::greyscale);
if (result.has_error()) {
return result.error();
}
image.init_raw(std::move(atlas_buffer), atlas_info.width, atlas_info.height * drawable_chars.size(), Media::ColourFormat::greyscale);
return std::move(image);
}

Expand Down
12 changes: 1 addition & 11 deletions code/Engine/src/Media/Image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,7 @@ namespace Media {
return {};
}

auto Image::init_raw(
std::vector<uint8_t>&& raw_data,
uint32_t width,
uint32_t height,
ColourFormat format) noexcept
-> Utily::Result<void, Utily::Error> {
void Image::init_raw(std::vector<uint8_t>&& raw_data, uint32_t width, uint32_t height, ColourFormat format) noexcept {
if (_fence) {
_fence->wait_for_sync();
_fence = std::nullopt;
Expand All @@ -88,11 +83,6 @@ namespace Media {
_format = format;
_width = width;
_height = height;

if (format == ColourFormat::greyscale && _data.size() != _width * _height) {
return Utily::Error("Unexpected image size");
}
return {};
}

auto Image::save_to_disk(std::filesystem::path path) noexcept
Expand Down
12 changes: 9 additions & 3 deletions code/Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,20 @@ if(DEFINED EMSCRIPTEN)
message(STATUS "PRELOAD FILES: \"${PRELOAD_FILES}\"")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PRELOAD_FILES}")
else()
if(NOT MSVC)
if(MSVC)
target_compile_options(Test PRIVATE
"$<$<CONFIG:Debug>:/Od;/ZI;/RTCs;/MDd>"
"$<$<CONFIG:Release>:/O2;/arch:AVX>"
)
else()
target_compile_options(Test PRIVATE -Wall -Wextra -Wpedantic -Wno-unused-parameter)
target_link_options(Test PRIVATE -Wall -Wextra -Wpedantic -Wno-unused-parameter)
target_compile_options(Test PUBLIC
target_compile_options(Test PRIVATE
"$<$<CONFIG:Debug>:-O0;-g;-fno-omit-frame-pointer>"
"$<$<CONFIG:Release>:-O3>"
"$<$<CONFIG:Release>:-O3;-march=native>"
)
endif()

file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()

Expand Down
2 changes: 1 addition & 1 deletion code/Test/include/Integration/BasicApps.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ struct FontData {
struct FontLogic {
void init(AppRenderer& renderer, entt::registry& ecs, FontData& data) {
data.start_time = std::chrono::high_resolution_clock::now();
auto ttf_raw = Utily::FileReader::load_entire_file("assets/Roboto.ttf");
auto ttf_raw = Utily::FileReader::load_entire_file("assets/RobotoMono.ttf");

auto e2 = data.font.init(ttf_raw.value());
auto e3 = data.font.gen_image_atlas(100);
Expand Down
4 changes: 2 additions & 2 deletions code/Test/src/Main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#include "TestPch.hpp"

#include "Integration/AssetLoading.hpp"
#include "Integration/BasicApps.hpp"
#include "Unit/UnitBenchDrawer.hpp"
#include "Unit/UnitModelStatic.hpp"
#include "Integration/AssetLoading.hpp"
#include "Integration/BasicApps.hpp"


int main(int argc, char** argv) {
Expand Down

0 comments on commit d691136

Please sign in to comment.