From 1488721360aa76b04fe950418713ef993a3575e2 Mon Sep 17 00:00:00 2001 From: X Ray Date: Wed, 5 Nov 2025 20:31:27 +0300 Subject: [PATCH 1/3] I moved rasterization into a separate function and looped the drawing of characters --- src/font.c | 122 +++++++++++++++++++++++++++-------------------------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/src/font.c b/src/font.c index 34a2463..0aa29b0 100644 --- a/src/font.c +++ b/src/font.c @@ -14,54 +14,33 @@ typedef struct { } Point; typedef struct { - Point *items; + Point* items; size_t count; size_t capacity; } Points; -int main() -{ - FT_Library library = {0}; - - FT_Error error = FT_Init_FreeType(&library); - if (error) { - fprintf(stderr, "ERROR: Could not initialize FreeType2 library\n"); - return 1; - } - - const char *const font_file_path = "./assets/Vollkorn-Regular.ttf"; - - FT_Face face; - error = FT_New_Face(library, font_file_path, 0, &face); - if (error == FT_Err_Unknown_File_Format) { - fprintf(stderr, "ERROR: `%s` has an unknown format\n", font_file_path); - return 1; - } else if (error) { - fprintf(stderr, "ERROR: Could not load file `%s`\n", font_file_path); - return 1; - } - - // char c = '#'; - // char c = 'x'; - char c = 'Q'; - // char c = '?'; - // char c = 'O'; - error = FT_Load_Char(face, c, FT_LOAD_DEFAULT); +void rasterize(FT_Face face, int ch, float scale) { + // char ch = '#'; + // char ch = 'x'; + // char ch = 'Q'; + // char ch = '?'; + // char ch = 'O'; + FT_Error error = FT_Load_Char(face, ch, FT_LOAD_DEFAULT); // error = FT_Load_Glyph(face, 'Q', FT_LOAD_DEFAULT); - if (error) { + if(error) { fprintf(stderr, "ERROR: could not load glyph\n"); - return 1; + return /*1*/; } long int min_x = LONG_MAX, max_x = LONG_MIN; long int min_y = LONG_MAX, max_y = LONG_MIN; - for (int i = 0; i < face->glyph->outline.n_points; ++i) { + for(int i = 0; i < face->glyph->outline.n_points; ++i) { FT_Vector p = face->glyph->outline.points[i]; unsigned char t = face->glyph->outline.tags[i]; - if (min_x > p.x) min_x = p.x; - if (max_x < p.x) max_x = p.x; - if (min_y > p.y) min_y = p.y; - if (max_y < p.y) max_y = p.y; + if(min_x > p.x) min_x = p.x; + if(max_x < p.x) max_x = p.x; + if(min_y > p.y) min_y = p.y; + if(max_y < p.y) max_y = p.y; printf("%ld %ld %d\n", p.x, p.y, t); } @@ -72,15 +51,14 @@ int main() printf("n_contours = %d\n", face->glyph->outline.n_contours); printf("------------------------------\n"); - int factor = 100; Points points = {0}; - for (int pindex = 0; pindex <= face->glyph->outline.n_points; pindex++) { + for(int pindex = 0; pindex <= face->glyph->outline.n_points; pindex++) { FT_Vector p = face->glyph->outline.points[pindex]; unsigned char t = FT_CURVE_TAG(face->glyph->outline.tags[pindex]); assert(t != FT_CURVE_TAG_CUBIC); float scale = 0.5; - float x = (p.x - min_x)*scale + 100; - float y = (max_y - p.y)*scale + 100; + float x = (p.x - min_x) * scale + 100; + float y = (max_y - p.y) * scale + 100; Point point = { .position = {x, y}, .on = t == FT_CURVE_TAG_ON, @@ -90,18 +68,18 @@ int main() size_t contour_start = 0; Spline spline = {0}; - for (int i = 0; i < face->glyph->outline.n_contours; ++i) { - Point *contour = &points.items[contour_start]; + for(int i = 0; i < face->glyph->outline.n_contours; ++i) { + Point* contour = &points.items[contour_start]; size_t contour_size = face->glyph->outline.contours[i] - contour_start + 1; assert(contour_size > 2); Vector2 p = {0}; size_t j = 0; bool hack = false; - if (contour[0].on) { + if(contour[0].on) { p = contour[0].position; j = 1; hack = true; - } else if (contour[contour_size - 1].on) { + } else if(contour[contour_size - 1].on) { p = contour[contour_size - 1].position; j = 0; } else { @@ -111,36 +89,35 @@ int main() printf("------------------------------\n"); printf("j = %zu\n", j); printf("------------------------------\n"); - while ((hack && j <= contour_size) || j < contour_size) { - if (contour[j%contour_size].on) { + while((hack && j <= contour_size) || j < contour_size) { + if(contour[j % contour_size].on) { Segment seg = { .kind = SEGMENT_LINE, .p1 = p, - .p2 = contour[j%contour_size].position, + .p2 = contour[j % contour_size].position, }; da_append(&spline, seg); - p = contour[j%contour_size].position; + p = contour[j % contour_size].position; j += 1; - } else if (contour[(j+1)%contour_size].on) { + } else if(contour[(j + 1) % contour_size].on) { Segment seg = { .kind = SEGMENT_QUAD, .p1 = p, - .p2 = contour[j%contour_size].position, - .p3 = contour[(j+1)%contour_size].position, + .p2 = contour[j % contour_size].position, + .p3 = contour[(j + 1) % contour_size].position, }; da_append(&spline, seg); - p = contour[(j+1)%contour_size].position; + p = contour[(j + 1) % contour_size].position; j += 2; } else { Vector2 v = Vector2Lerp( - contour[j%contour_size].position, - contour[(j+1)%contour_size].position, - 0.5 - ); + contour[j % contour_size].position, + contour[(j + 1) % contour_size].position, + 0.5); Segment seg = { .kind = SEGMENT_QUAD, .p1 = p, - .p2 = contour[j%contour_size].position, + .p2 = contour[j % contour_size].position, .p3 = v, }; da_append(&spline, seg); @@ -153,12 +130,38 @@ int main() printf("spline.count = %zu\n", spline.count); render_spline_into_grid(&spline); +} - InitWindow(16*factor, 9*factor, "main"); +int main() { + FT_Library library = {0}; + + FT_Error error = FT_Init_FreeType(&library); + if(error) { + fprintf(stderr, "ERROR: Could not initialize FreeType2 library\n"); + return 1; + } + + const char* const font_file_path = "./assets/Vollkorn-Regular.ttf"; + + FT_Face face; + error = FT_New_Face(library, font_file_path, 0, &face); + if(error == FT_Err_Unknown_File_Format) { + fprintf(stderr, "ERROR: `%s` has an unknown format\n", font_file_path); + return 1; + } else if(error) { + fprintf(stderr, "ERROR: Could not load file `%s`\n", font_file_path); + return 1; + } + + int factor = 100; + InitWindow(16 * factor, 9 * factor, "main"); SetTargetFPS(60); - while (!WindowShouldClose()) { + int ctr = 0; + int ch = 0; + while(!WindowShouldClose()) { ClearBackground(BLACK); BeginDrawing(); + if(!(ctr++ & 30)) rasterize(face, ch++, 0.5); display_grid(); EndDrawing(); } @@ -175,7 +178,6 @@ int main() // * outline's content easily. // - FT_Glyph - printf("OK\n"); return 0; } From 30dc15102cdf1fea35c24fbaa77789f57c4e0433 Mon Sep 17 00:00:00 2001 From: X Ray Date: Wed, 5 Nov 2025 22:36:02 +0300 Subject: [PATCH 2/3] Easier spline filling --- src/font.c | 133 ++++++++++++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 62 deletions(-) diff --git a/src/font.c b/src/font.c index 0aa29b0..d33d16f 100644 --- a/src/font.c +++ b/src/font.c @@ -19,12 +19,46 @@ typedef struct { size_t capacity; } Points; +int append_to_spline(Point* const p1, Point* const p2, Vector2* p, Spline* spline) { + Segment seg; + int advance; + if(p1->on) { + seg = (Segment){ + .kind = SEGMENT_LINE, + .p1 = *p, + .p2 = p1->position, + }; + *p = seg.p2; + advance = 1; + } else if(p2->on) { + seg = (Segment){ + .kind = SEGMENT_QUAD, + .p1 = *p, + .p2 = p1->position, + .p3 = p2->position, + }; + *p = seg.p3; + advance = 2; + } else { + seg = (Segment){ + .kind = SEGMENT_QUAD, + .p1 = *p, + .p2 = p1->position, + .p3 = Vector2Lerp(p1->position, p2->position, 0.5), + }; + *p = seg.p3; + advance = 1; + } + da_append(spline, seg); + return advance; +} + +void _Spline(Spline* ptr) { da_free(*ptr); } +void _Points(Points* ptr) { da_free(*ptr); } + +#define printf(...) // NOTE supress output + void rasterize(FT_Face face, int ch, float scale) { - // char ch = '#'; - // char ch = 'x'; - // char ch = 'Q'; - // char ch = '?'; - // char ch = 'O'; FT_Error error = FT_Load_Char(face, ch, FT_LOAD_DEFAULT); // error = FT_Load_Glyph(face, 'Q', FT_LOAD_DEFAULT); if(error) { @@ -32,6 +66,9 @@ void rasterize(FT_Face face, int ch, float scale) { return /*1*/; } + if(!face->glyph->outline.n_points) + return clear_grid(); // NOTE is empty, do nothing + long int min_x = LONG_MAX, max_x = LONG_MIN; long int min_y = LONG_MAX, max_y = LONG_MIN; for(int i = 0; i < face->glyph->outline.n_points; ++i) { @@ -51,12 +88,11 @@ void rasterize(FT_Face face, int ch, float scale) { printf("n_contours = %d\n", face->glyph->outline.n_contours); printf("------------------------------\n"); - Points points = {0}; + [[gnu::cleanup(_Points)]] Points points = {0}; for(int pindex = 0; pindex <= face->glyph->outline.n_points; pindex++) { FT_Vector p = face->glyph->outline.points[pindex]; unsigned char t = FT_CURVE_TAG(face->glyph->outline.tags[pindex]); assert(t != FT_CURVE_TAG_CUBIC); - float scale = 0.5; float x = (p.x - min_x) * scale + 100; float y = (max_y - p.y) * scale + 100; Point point = { @@ -67,67 +103,29 @@ void rasterize(FT_Face face, int ch, float scale) { } size_t contour_start = 0; - Spline spline = {0}; + [[gnu::cleanup(_Spline)]] Spline spline = {0}; + for(int i = 0; i < face->glyph->outline.n_contours; ++i) { - Point* contour = &points.items[contour_start]; size_t contour_size = face->glyph->outline.contours[i] - contour_start + 1; assert(contour_size > 2); + + Point* const contour_front = points.items + contour_start; + Point* const contour_back = contour_front + contour_size - 1; + Point* it = contour_front; Vector2 p = {0}; - size_t j = 0; - bool hack = false; - if(contour[0].on) { - p = contour[0].position; - j = 1; - hack = true; - } else if(contour[contour_size - 1].on) { - p = contour[contour_size - 1].position; - j = 0; + if(contour_back->on) { + p = contour_back->position; + } else if(contour_front->on) { + p = it++->position; } else { - p = Vector2Lerp(contour[0].position, contour[contour_size - 1].position, 0.5); - j = 0; - } - printf("------------------------------\n"); - printf("j = %zu\n", j); - printf("------------------------------\n"); - while((hack && j <= contour_size) || j < contour_size) { - if(contour[j % contour_size].on) { - Segment seg = { - .kind = SEGMENT_LINE, - .p1 = p, - .p2 = contour[j % contour_size].position, - }; - da_append(&spline, seg); - p = contour[j % contour_size].position; - j += 1; - } else if(contour[(j + 1) % contour_size].on) { - Segment seg = { - .kind = SEGMENT_QUAD, - .p1 = p, - .p2 = contour[j % contour_size].position, - .p3 = contour[(j + 1) % contour_size].position, - }; - da_append(&spline, seg); - p = contour[(j + 1) % contour_size].position; - j += 2; - } else { - Vector2 v = Vector2Lerp( - contour[j % contour_size].position, - contour[(j + 1) % contour_size].position, - 0.5); - Segment seg = { - .kind = SEGMENT_QUAD, - .p1 = p, - .p2 = contour[j % contour_size].position, - .p3 = v, - }; - da_append(&spline, seg); - p = v; - j += 1; - } + p = Vector2Lerp(contour_front->position, contour_back->position, 0.5); } + while(it < contour_back) + it += append_to_spline(it, it + 1, &p, &spline); + append_to_spline(contour_back, contour_front, &p, &spline); + contour_start = face->glyph->outline.contours[i] + 1; } - printf("spline.count = %zu\n", spline.count); render_spline_into_grid(&spline); } @@ -154,19 +152,30 @@ int main() { } int factor = 100; + SetTraceLogLevel(LOG_WARNING); InitWindow(16 * factor, 9 * factor, "main"); SetTargetFPS(60); int ctr = 0; - int ch = 0; + int ch = L' '; + // int ch = '#'; + // int ch = 'x'; + // int ch = 'Q'; + // int ch = '?'; + // int ch = 'O'; + // int ch = L'Ы'; + // int ch = L'繁';//汉仪菱心体 + // rasterize(face,ch, 0.5); while(!WindowShouldClose()) { ClearBackground(BLACK); BeginDrawing(); - if(!(ctr++ & 30)) rasterize(face, ch++, 0.5); + if(!(++ctr % 5)) rasterize(face, ch++, 0.5); display_grid(); EndDrawing(); } CloseWindow(); + FT_Done_FreeType(library); + // Types we are interested in // - FT_Outline from ftimage.h // - FT_Vector From 9453a85d65f4decdcdf9b054f784edcef020b858 Mon Sep 17 00:00:00 2001 From: X Ray Date: Wed, 5 Nov 2025 22:42:51 +0300 Subject: [PATCH 3/3] A denser grid and a cleaning function --- src/raster.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/raster.c b/src/raster.c index c0a6912..81c569f 100644 --- a/src/raster.c +++ b/src/raster.c @@ -4,7 +4,7 @@ #define windows_factor 200 #define window_width (width_factor*windows_factor) #define window_height (height_factor*windows_factor) -#define grid_factor 20 +#define grid_factor 25 #define grid_width (width_factor*grid_factor) #define grid_height (height_factor*grid_factor) #define cell_width (window_width/grid_width) @@ -135,14 +135,18 @@ void solve_row(const Spline *spline, size_t row, Solutions *solutions) qsort(solutions->items, solutions->count, sizeof(*solutions->items), compare_solutions_by_tx); } -void render_spline_into_grid(const Spline *spline) -{ - static Solutions solutions = {0}; - for (size_t row = 0; row < grid_height; ++row) { - for (size_t col = 0; col < grid_width; ++col) { +void clear_grid() { + for(size_t row = 0; row < grid_height; ++row) { + for(size_t col = 0; col < grid_width; ++col) { grid[row][col] = false; } } +} + +void render_spline_into_grid(const Spline *spline) +{ + static Solutions solutions = {0}; + clear_grid(); for (size_t row = 0; row < grid_height; ++row) { int winding = 0; solve_row(spline, row, &solutions);