Created
December 21, 2021 22:59
-
-
Save michaeljclark/22ddfb74c09e2aabd30994a991325fc9 to your computer and use it in GitHub Desktop.
loads TrueType Bézier curves for codepoint using FreeType
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* ftload.cc loads TrueType Bézier curves for codepoint using FreeType | |
* | |
* Compile: c++ -std=c++17 -o ftload ftload.cc -Ithird_party/glm \ | |
* $(pkg-config --cflags --libs freetype2) | |
* | |
* Usage: ./ftload <ttf-file> <dpi> <size> <code-point> | |
* | |
* Example: ./ftload DejaVuSans.ttf 72 32 99 | |
*/ | |
#include <cstdio> | |
#include <cstdlib> | |
#include <vector> | |
#include <glm/glm.hpp> | |
#include <ft2build.h> | |
#include FT_FREETYPE_H | |
#include FT_MODULE_H | |
#include FT_GLYPH_H | |
#include FT_OUTLINE_H | |
using vec2 = glm::vec2; | |
template <typename T> using vector = std::vector<T>; | |
typedef const FT_Vector ft_vec; | |
#define Panic(...) do { fprintf(stderr, __VA_ARGS__); exit(9); } while(0); | |
enum EdgeType { Linear = 2, Quadratic = 3, Cubic = 4, }; | |
struct Edge { | |
int type; | |
vec2 p[4]; | |
}; | |
struct Contour { | |
int edge_offset, edge_count; | |
}; | |
struct Shape { | |
int contour_offset, contour_count, edge_offset, edge_count; | |
vec2 offset, size; | |
}; | |
struct Context { | |
vector<Shape> shapes; | |
vector<Contour> contours; | |
vector<Edge> edges; | |
vec2 pos; | |
}; | |
static void new_shape(Context &ctx, vec2 offset, vec2 size) { | |
ctx.shapes.emplace_back(Shape{(int)ctx.contours.size(), 0, | |
(int)ctx.edges.size(), 0, offset, size }); | |
} | |
static void new_contour(Context &ctx) { | |
ctx.contours.emplace_back(Contour{(int)ctx.edges.size(), 0}); | |
ctx.shapes.back().contour_count++; | |
} | |
static void new_edge(Context &ctx, Edge&& e) { | |
ctx.edges.emplace_back(e); | |
ctx.contours.back().edge_count++; | |
ctx.shapes.back().edge_count++; | |
} | |
static Context& ctx(void *u) { return *static_cast<Context*>(u); } | |
static vec2 pt(ft_vec *v) { return vec2(v->x/64.f, v->y/64.f); } | |
static int ft_move_to(ft_vec *p, void *u) { | |
new_contour(ctx(u)); | |
ctx(u).pos = pt(p); | |
return 0; | |
} | |
static int ft_line_to(ft_vec *p, void *u) { | |
new_edge(ctx(u), Edge{Linear, { ctx(u).pos, pt(p) }}); | |
ctx(u).pos = pt(p); | |
return 0; | |
} | |
static int ftConicTo(ft_vec *c, ft_vec *p, void *u) { | |
new_edge(ctx(u), Edge{Quadratic, { ctx(u).pos, pt(c), pt(p) }}); | |
ctx(u).pos = pt(p); | |
return 0; | |
} | |
static int ftCubicTo(ft_vec *c1, ft_vec *c2, ft_vec *p, void *u) { | |
new_edge(ctx(u), Edge{Cubic, { ctx(u).pos, pt(c1), pt(c2), pt(p) }}); | |
ctx(u).pos = pt(p); | |
return 0; | |
} | |
static vec2 ft_glyph_offset(FT_Glyph_Metrics *m) { | |
return vec2(m->horiBearingX, m->horiBearingY-m->height); | |
} | |
static vec2 ft_glyph_size(FT_Glyph_Metrics *m) { | |
return vec2(m->width, m->height); | |
} | |
static void load_glyph(Context &ctx, FT_Face ftface, int sz, int dpi, int glyph) | |
{ | |
FT_Outline_Funcs ftfuncs = { ft_move_to, ft_line_to, ftConicTo, ftCubicTo }; | |
FT_Glyph_Metrics *m = &ftface->glyph->metrics; | |
FT_Error fterr; | |
if ((fterr = FT_Set_Char_Size(ftface, 0, sz, dpi, dpi))) | |
Panic("error: FT_Set_Char_Size failed: fterr=%d\n", fterr); | |
if ((fterr = FT_Load_Glyph(ftface, glyph, 0))) | |
Panic("error: FT_Load_Glyph failed: fterr=%d\n", fterr); | |
new_shape(ctx, ft_glyph_offset(m), ft_glyph_size(m)); | |
if ((fterr = FT_Outline_Decompose(&ftface->glyph->outline, &ftfuncs, &ctx))) | |
Panic("error: FT_Outline_Decompose failed: fterr=%d\n", fterr); | |
} | |
static const char* edge_formats[] = { | |
"\t edge %zu Unknown\n", | |
"\t edge %zu Linear (%f,%f) - (%f, %f)\n", | |
"\t edge %zu Quadratic (%f,%f) - [%f, %f] - (%f, %f)\n", | |
"\t edge %zu Cubic (%f,%f) - [%f, %f] - [%f, %f] - (%f, %f)\n", | |
}; | |
static void dump_glyph(Context &ctx, int shape) | |
{ | |
Shape &s = ctx.shapes[shape]; | |
for (size_t i = 0; i < s.contour_count; i++) { | |
Contour &c = ctx.contours[s.contour_offset + i]; | |
printf("contour %zu\n", i); | |
for (size_t j = 0; j < c.edge_count; j++) { | |
Edge &e = ctx.edges[c.edge_offset + j]; | |
int fmt = e.type >= 2 ? e.type <= 4 ? e.type-1 : 0 : 0; | |
printf(edge_formats[fmt], | |
j, e.p[0].x, e.p[0].y, e.p[1].x, e.p[1].y, | |
e.p[2].x, e.p[2].y, e.p[3].x, e.p[3].y); | |
} | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
FT_Error fterr; | |
FT_Library ftlib; | |
FT_Face ftface; | |
Context ctx; | |
if (argc != 5) | |
Panic("usage: %s <ttf-file> <dpi> <size> <code-point>\n", argv[0]); | |
const char * fontpath = argv[1]; | |
int sz = atoi(argv[2]), dpi = atoi(argv[3]), cp = atoi(argv[4]); | |
if ((fterr = FT_Init_FreeType(&ftlib))) | |
Panic("error: FT_Init_FreeType failed: fterr=%d\n", fterr); | |
if ((fterr = FT_New_Face(ftlib, fontpath, 0, &ftface))) | |
Panic("error: FT_New_Face failed: fterr=%d, path=%s\n", | |
fterr, fontpath); | |
FT_Select_Charmap(ftface, FT_ENCODING_UNICODE); | |
load_glyph(ctx, ftface, sz, dpi, FT_Get_Char_Index(ftface, cp)); | |
dump_glyph(ctx, 0); | |
exit(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment