Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/Harrand/Topaz
Browse files Browse the repository at this point in the history
  • Loading branch information
harrand committed Nov 20, 2023
2 parents 780174b + b3338fd commit 1ce455e
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 31 deletions.
1 change: 1 addition & 0 deletions demo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ endfunction()

add_subdirectory(core)
add_subdirectory(gl)
add_subdirectory(ren)
5 changes: 2 additions & 3 deletions demo/gl/tz_mesh_demo/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "tz/core/matrix_transform.hpp"

#include "tz/ren/mesh.hpp"
#include "tz/ren/mesh2.hpp"
struct dbgui_data_t
{
bool mesh_renderer_enabled = false;
Expand All @@ -22,8 +21,8 @@ int main()
{
dbgui_init();

tz::ren::mesh_renderer2 mr;
tz::ren::mesh_renderer2::mesh m;
tz::ren::mesh_renderer mr;
tz::ren::mesh_renderer::mesh m;
m.indices = {0u, 1u, 2u};
m.vertices =
{
Expand Down
1 change: 1 addition & 0 deletions demo/ren/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(tz_text_rendering_demo)
13 changes: 13 additions & 0 deletions demo/ren/tz_text_rendering_demo/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
add_demo(
TARGET tz_text_rendering_demo
SOURCE_FILES
main.cpp
)

add_text(
TARGET tz_text_rendering_demo
INPUT_DIR ${PROJECT_SOURCE_DIR}
OUTPUT_DIR ${PROJECT_BINARY_DIR}
TEXT_FILES
demo/ren/tz_text_rendering_demo/res/ProggyClean.ttf
)
98 changes: 98 additions & 0 deletions demo/ren/tz_text_rendering_demo/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include "tz/tz.hpp"
#include "tz/gl/device.hpp"
#include "tz/io/ttf.hpp"

#include "tz/core/imported_text.hpp"
#include ImportedTextHeader(ProggyClean, ttf)


// debug rendering
#include "tz/ren/mesh.hpp"

struct dbgui_data_t
{
bool text_renderer_enabled = false;
} dbgui_data;
void dbgui_init();

static tz::io::ttf::rasterise_info rast
{
.dimensions = {32u, 32u},
.angle_threshold = 3.0f,
.range = 0.1f,
.scale = 64.0f,
.translate = tz::vec2::zero()
};

int main()
{
tz::initialise({.name = "tz_text_rendering_demo"});
{
dbgui_init();
tz::io::ttf ttf = tz::io::ttf::from_memory(ImportedTextData(ProggyClean, ttf));
tz::io::image img_c = ttf.rasterise_msdf('A', rast);

tz::ren::mesh_renderer mr;
mr.append_to_render_graph();

tz::ren::mesh_renderer::mesh quad;
quad.vertices =
{
{.position = {0.5f, 0.5f, 0.0f}, .texcoord = {1.0f, 1.0f}},
{.position = {0.5f, -0.5f, 0.0f}, .texcoord = {1.0f, 0.0f}},
{.position = {-0.5f, -0.5f, 0.0f}, .texcoord = {0.0f, 0.0f}},
{.position = {-0.5f, 0.5f, 0.0f}, .texcoord = {0.0f, 1.0f}}
};
quad.indices = {0u, 1u, 3u, 1u, 2u, 3u};
auto mesh = mr.add_mesh(quad);
auto tex = mr.add_texture(img_c);
mr.camera_perspective({.aspect_ratio = 1920.0f/1080.0f, .fov = 1.5708f});
mr.set_camera_transform({.translate = {0.0f, 0.0f, 1.0f}});

auto obj = mr.add_object
({
.mesh = mesh,
.bound_textures = {{.texture = tex}}
});

while(!tz::window().is_close_requested())
{
tz::begin_frame();
tz::gl::get_device().render();
tz::dbgui::run([&mr, &ttf, obj]()
{
if(dbgui_data.text_renderer_enabled)
{
if(ImGui::Begin("Text Renderer", &dbgui_data.text_renderer_enabled))
{
bool changed = false;
changed |= ImGui::SliderInt2("Dimensions", reinterpret_cast<int*>(rast.dimensions.data().data()), 1u, 512u, "%u");
changed |= ImGui::SliderFloat("Angle Threshold", &rast.angle_threshold, 0.0f, 5.0f);
changed |= ImGui::SliderFloat("Range", &rast.range, 0.01f, 2.0f);
changed |= ImGui::SliderFloat("Scale", &rast.scale, 0.01f, 64.0f);
changed |= ImGui::SliderFloat2("Translate", rast.translate.data().data(), -1.0f, 1.0f);

if(changed)
{
auto new_tex = mr.add_texture(ttf.rasterise_msdf('c', rast));
mr.get_object(obj).bound_textures[0].texture = new_tex;
}

ImGui::End();
}
}
});
tz::end_frame();
}
}
tz::terminate();
}

// invoked when dbgui needs to set up initial state.
void dbgui_init()
{
tz::dbgui::game_menu().add_callback([]()
{
ImGui::MenuItem("Text Renderer", nullptr, &dbgui_data.text_renderer_enabled);
});
}
96 changes: 70 additions & 26 deletions src/tz/io/ttf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ namespace tz::io
return ttf::from_memory(buffer);
}

tz::io::image ttf::rasterise_msdf(char c) const
tz::io::image ttf::rasterise_msdf(char c, rasterise_info i) const
{
tz::io::image ret;

Expand Down Expand Up @@ -69,9 +69,11 @@ namespace tz::io
// }

// generate bitmap
msdfgen::edgeColoringSimple(shape, 3.0f);
msdfgen::Bitmap<float, 3> msdf(128u, 128u);
msdfgen::generateMSDF(msdf, shape, 128.0f, 1.0f, msdfgen::Vector2(100.0f, -100.0f));
shape.normalize();
msdfgen::edgeColoringSimple(shape, i.angle_threshold);
msdfgen::Bitmap<float, 3> msdf(i.dimensions[0], i.dimensions[1]);

msdfgen::generateMSDF(msdf, shape, i.range, i.scale, msdfgen::Vector2(i.translate[0], i.translate[1]));
ret.width = msdf.width();
ret.height = msdf.height();
ret.data.resize(1 * 4u * ret.width * ret.height, std::byte{0});
Expand All @@ -82,9 +84,10 @@ namespace tz::io
{
for(std::size_t i = 0; i < 3; i++)
{
imgdata(x, y)[i] = static_cast<std::byte>(msdf(x, y)[i] * 255);
imgdata(x, y)[3] = std::byte{255};
float val = msdf(x, y)[i];
imgdata(x, y)[i] = static_cast<std::byte>(255 - std::clamp(val * 256.0f, 0.0f, 255.0f));
}
imgdata(x, y)[3] = std::byte{255};
}
}

Expand Down Expand Up @@ -327,15 +330,15 @@ namespace tz::io
if(this->head.index_to_loc_format == 0)
{
// 16 bit
for(std::size_t i = 0; std::cmp_less(i, this->maxp.num_glyphs); i++)
for(std::size_t i = 0; std::cmp_less(i, this->maxp.num_glyphs + 1); i++)
{
this->loca.locations16.push_back(ttf_read_value<std::uint16_t>(ptr));
}
}
else
{
// 32 bit
for(std::size_t i = 0; std::cmp_less(i, this->maxp.num_glyphs); i++)
for(std::size_t i = 0; std::cmp_less(i, this->maxp.num_glyphs + 1); i++)
{
this->loca.locations32.push_back(ttf_read_value<std::uint32_t>(ptr));
}
Expand All @@ -360,7 +363,7 @@ namespace tz::io
{
auto& g = this->glyf.glyfs.emplace_back();
g.number_of_contours = ttf_read_value<std::int16_t>(ptr);
tz::assert(g.number_of_contours > 0, "Only support simple glyphs. Number of contours: %d", (int)g.number_of_contours);
tz::assert(g.number_of_contours >= 0, "Only support simple glyphs. Number of contours: %d", (int)g.number_of_contours);
g.xmin = ttf_read_value<std::int16_t>(ptr);
g.ymin = ttf_read_value<std::int16_t>(ptr);
g.xmax = ttf_read_value<std::int16_t>(ptr);
Expand All @@ -374,13 +377,17 @@ namespace tz::io
std::memcpy(g.instructions.data(), ptr, g.instructions.size());
std::advance(ptr, g.instructions.size());

if(g.number_of_contours == 0)
{
return;
}
int last_index = g.end_pts_of_contours.back();
g.flags.resize(last_index + 1);
for(int i = 0; i < (last_index + 1); i++)
{
g.flags[i] = static_cast<std::byte>(*ptr);
ptr++;
if(static_cast<uint8_t>(g.flags[i]) & 0x00001000) // repeat bit
if(1 == ((static_cast<uint8_t>(g.flags[i]) >> 3) & 1))
{
std::uint8_t repeat_count = *ptr;
while(repeat_count-- > 0)
Expand Down Expand Up @@ -409,17 +416,19 @@ namespace tz::io
case 1:
cur_coord = 0;
break;
case 2:
case 3:
cur_coord = -ttf_read_value<std::int8_t>(ptr);
break;
case 3:
case 2:
cur_coord = ttf_read_value<std::int8_t>(ptr);
break;
default:
tz::error("Unrecognised flags. Expected flag_combined to be 0, 1, 2, or 3, but it is %d", flag_combined);
break;
}
g.x_coords[i] = cur_coord + prev_coord;
tz::assert(g.xmin <= g.x_coords[i]);
tz::assert(g.xmax >= g.x_coords[i]);
prev_coord = g.x_coords[i];
}
g.y_coords.resize((last_index + 1));
Expand All @@ -439,17 +448,19 @@ namespace tz::io
case 1:
cur_coord = 0;
break;
case 2:
case 3:
cur_coord = -ttf_read_value<std::int8_t>(ptr);
break;
case 3:
case 2:
cur_coord = ttf_read_value<std::int8_t>(ptr);
break;
default:
tz::error("Unrecognised flags. Expected flag_combined to be 0, 1, 2, or 3, but it is %d", flag_combined);
break;
}
g.y_coords[i] = cur_coord + prev_coord;
tz::assert(g.ymin <= g.y_coords[i]);
tz::assert(g.ymax >= g.y_coords[i]);
prev_coord = g.y_coords[i];
}
};
Expand All @@ -461,7 +472,8 @@ namespace tz::io
for(std::uint16_t loc16 : this->loca.locations16)
{
auto loca_offset = loc16 * multiplier;
read_glyph(ptrcpy + loca_offset);
ptrcpy = ptr + loca_offset;
read_glyph(ptrcpy);
}
}
else
Expand All @@ -471,8 +483,8 @@ namespace tz::io
for(std::uint32_t loc32 : this->loca.locations32)
{
auto loca_offset = loc32 * multiplier;
ptr = ptrcpy + loca_offset;
read_glyph(ptrcpy + loca_offset);
ptrcpy = ptr + loca_offset;
read_glyph(ptrcpy);
}
}
this->glyf.canary = true;
Expand Down Expand Up @@ -618,23 +630,55 @@ namespace tz::io
char c = ttf_alphabet[i];
auto index = this->cmap.glyph_index_map[c | 0] | 0;
auto glyfd = this->glyf.glyfs[index];
auto hmtxd = this->hmtx.hmetrics[index];
// its possible index > hmetrics.length
// spec:The table uses a longHorMetric record to give the advance width and left side bearing of a glyph. Records are indexed by glyph ID. As an optimization, the number of records can be less than the number of glyphs, in which case the advance width value of the last record applies to all remaining glyph IDs
// If numberOfHMetrics is less than the total number of glyphs, then the hMetrics array is followed by an array for the left side bearing values of the remaining glyphs.
std::size_t advance_width, left_side_bearing;
if(std::cmp_greater_equal(index, this->hmtx.hmetrics.size()))
{
advance_width = hmtx.hmetrics.back().advance_width;
left_side_bearing = hmtx.left_side_bearings[index - hmtx.hmetrics.size()];
}
else
{
auto hmtxd = this->hmtx.hmetrics[index];
advance_width = hmtxd.advance_width;
left_side_bearing = hmtxd.left_side_bearing;
}

ttf_glyph_shape_info shape;
tz::assert(glyfd.x_coords.size() == glyfd.y_coords.size());
std::size_t contour_cursor = 0;
shape.contours.resize(glyfd.number_of_contours);
for(std::size_t i = 1; i < glyfd.x_coords.size(); i++)
std::optional<tz::vec2> cache = std::nullopt;
for(std::size_t i = 0; i < glyfd.x_coords.size(); i++)
{
auto beg = static_cast<tz::vec2>(tz::vec2i{glyfd.x_coords[i - 1], glyfd.y_coords[i - 1]});
beg /= this->head.units_per_em;
auto end = static_cast<tz::vec2>(tz::vec2i{glyfd.x_coords[i], glyfd.y_coords[i]});
end /= this->head.units_per_em;
shape.contours[contour_cursor].edges.push_back({beg, end});
if(!cache.has_value())
{
cache = static_cast<tz::vec2>(tz::vec2i{glyfd.x_coords[i], glyfd.y_coords[i]});
}
else
{
shape.contours[contour_cursor].edges.push_back({
cache.value(),
static_cast<tz::vec2>(tz::vec2i{glyfd.x_coords[i], glyfd.y_coords[i]})
});
cache = std::nullopt;
}
if(i == glyfd.end_pts_of_contours[contour_cursor])
{
// contour ends here.
contour_cursor++;
cache = std::nullopt;
}
}

for(auto& contour : shape.contours)
{
for (auto& [a, b] : contour.edges)
{
a /= this->head.units_per_em;
b /= this->head.units_per_em;
}
}

Expand All @@ -644,8 +688,8 @@ namespace tz::io
{
.position = static_cast<tz::vec2i>(tz::vector<std::int16_t, 2>{glyfd.xmin, glyfd.ymin}),
.dimensions = static_cast<tz::vec2ui>(tz::vector<int, 2>{glyfd.xmax - glyfd.xmin, glyfd.ymax - glyfd.ymin}),
.left_side_bearing = static_cast<int>(hmtxd.left_side_bearing),
.right_side_bearing = static_cast<int>(hmtxd.advance_width - hmtxd.left_side_bearing - (glyfd.xmax - glyfd.xmin))
.left_side_bearing = static_cast<int>(left_side_bearing),
.right_side_bearing = static_cast<int>(advance_width - left_side_bearing - (glyfd.xmax - glyfd.xmin))
},
.shape = shape
};
Expand Down
11 changes: 10 additions & 1 deletion src/tz/io/ttf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,16 @@ namespace tz::io
static ttf from_memory(std::string_view sv);
static ttf from_file(const char* path);

tz::io::image rasterise_msdf(char c) const;
struct rasterise_info
{
tz::vec2ui dimensions;
float angle_threshold;
float range;
float scale;
tz::vec2 translate;
};

tz::io::image rasterise_msdf(char c, rasterise_info i) const;
ttf(std::string_view ttf_data);
private:
std::string_view parse_header(std::string_view str);
Expand Down
2 changes: 1 addition & 1 deletion src/tz/ren/shaders/mesh.vertex.tzsl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ resource(id = 2) const buffer camera_buffer
output(id = 0) vec2 out::texcoord;
output(id = 1) vec3 out::normal;
output(id = 2) vec3 out::colour;
output(id = 3) uvec2 out::impl_unused;
output(id = 3) vec3 out::impl_unused;
output(id = 4) texture_locator out::textures[MAX_TEX_COUNT];

void main()
Expand Down

0 comments on commit 1ce455e

Please sign in to comment.