fortrangoingonforty/fortty / d016cd4

Browse files

Add FreeType bindings for font loading

- C helper functions for FreeType (init, load font, render glyph)
- Fortran font_mod module with font_t type
- Fortran glyph_mod module with glyph_t type
- Updated CMake to find and link FreeType
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
d016cd47cf1808d3eaaa45af56b03dbf931115fd
Parents
14f0f3a
Tree
adb9533

4 changed files

StatusFile+-
M CMakeLists.txt 10 5
A c_src/freetype_helpers.c 101 0
A src/text/font.f90 158 0
A src/text/glyph.f90 39 0
CMakeLists.txtmodified
@@ -13,14 +13,17 @@ endif()
1313
 # Find dependencies
1414
 find_package(glfw3 REQUIRED)
1515
 find_package(OpenGL REQUIRED)
16
+find_package(Freetype REQUIRED)
1617
 
17
-# GLAD loader library (C) and helper functions
18
-add_library(glad STATIC
18
+# C helper libraries (GLAD + FreeType wrappers)
19
+add_library(helpers STATIC
1920
     src/gl/glad.c
2021
     c_src/gl_loader.c
22
+    c_src/freetype_helpers.c
2123
 )
22
-target_include_directories(glad PUBLIC ${CMAKE_SOURCE_DIR}/include)
23
-target_link_libraries(glad PRIVATE glfw)
24
+target_include_directories(helpers PUBLIC ${CMAKE_SOURCE_DIR}/include)
25
+target_include_directories(helpers PRIVATE ${FREETYPE_INCLUDE_DIRS})
26
+target_link_libraries(helpers PRIVATE glfw ${FREETYPE_LIBRARIES})
2427
 
2528
 # Fortran module output directory
2629
 set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules)
@@ -29,6 +32,8 @@ set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/modules)
2932
 set(FORTRAN_SOURCES
3033
     src/core/types.f90
3134
     src/gl/gl_bindings.f90
35
+    src/text/glyph.f90
36
+    src/text/font.f90
3237
     src/window/glfw_bindings.f90
3338
     src/window/window.f90
3439
     src/fortty.f90
@@ -37,7 +42,7 @@ set(FORTRAN_SOURCES
3742
 # Main executable
3843
 add_executable(fortty ${FORTRAN_SOURCES})
3944
 target_include_directories(fortty PRIVATE ${CMAKE_BINARY_DIR}/modules)
40
-target_link_libraries(fortty PRIVATE glad glfw OpenGL::GL)
45
+target_link_libraries(fortty PRIVATE helpers glfw OpenGL::GL ${FREETYPE_LIBRARIES})
4146
 
4247
 # For Linux, we need dl for GLAD's dlopen
4348
 if(UNIX AND NOT APPLE)
c_src/freetype_helpers.cadded
@@ -0,0 +1,101 @@
1
+/*
2
+ * FreeType helper functions for Fortran binding.
3
+ * Provides simplified C wrappers around FreeType's complex structs.
4
+ */
5
+
6
+#include <ft2build.h>
7
+#include FT_FREETYPE_H
8
+#include <stdlib.h>
9
+#include <string.h>
10
+
11
+/* Initialize FreeType library */
12
+int fortty_ft_init(FT_Library *lib) {
13
+    return FT_Init_FreeType(lib);
14
+}
15
+
16
+/* Cleanup FreeType library */
17
+void fortty_ft_done(FT_Library lib) {
18
+    FT_Done_FreeType(lib);
19
+}
20
+
21
+/* Load a font face at specified pixel size */
22
+int fortty_ft_load_font(FT_Library lib, const char *path, int size_px, FT_Face *face) {
23
+    FT_Error err = FT_New_Face(lib, path, 0, face);
24
+    if (err) return err;
25
+
26
+    err = FT_Set_Pixel_Sizes(*face, 0, size_px);
27
+    if (err) {
28
+        FT_Done_Face(*face);
29
+        return err;
30
+    }
31
+
32
+    return 0;
33
+}
34
+
35
+/* Cleanup font face */
36
+void fortty_ft_done_face(FT_Face face) {
37
+    FT_Done_Face(face);
38
+}
39
+
40
+/* Get font metrics for terminal cell sizing */
41
+void fortty_ft_get_metrics(FT_Face face, int *cell_width, int *cell_height,
42
+                           int *ascender, int *descender) {
43
+    /* For monospace fonts, use the max advance width */
44
+    if (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
45
+        *cell_width = face->max_advance_width >> 6;
46
+    } else {
47
+        /* Fallback: measure 'M' character */
48
+        if (FT_Load_Char(face, 'M', FT_LOAD_DEFAULT) == 0) {
49
+            *cell_width = face->glyph->advance.x >> 6;
50
+        } else {
51
+            *cell_width = face->size->metrics.max_advance >> 6;
52
+        }
53
+    }
54
+
55
+    /* Height from font metrics (in 1/64 pixels, convert to pixels) */
56
+    *ascender = face->size->metrics.ascender >> 6;
57
+    *descender = -(face->size->metrics.descender >> 6); /* Make positive */
58
+    *cell_height = *ascender + *descender;
59
+}
60
+
61
+/* Render a glyph and return its bitmap data
62
+ * Returns 0 on success, non-zero on error.
63
+ * Caller should NOT free the bitmap pointer - it points to FreeType's internal buffer.
64
+ */
65
+int fortty_ft_render_glyph(FT_Face face, unsigned int codepoint,
66
+                           unsigned char **bitmap, int *width, int *height,
67
+                           int *bearing_x, int *bearing_y, int *advance) {
68
+    FT_Error err = FT_Load_Char(face, codepoint, FT_LOAD_RENDER);
69
+    if (err) return err;
70
+
71
+    FT_GlyphSlot g = face->glyph;
72
+
73
+    *bitmap = g->bitmap.buffer;
74
+    *width = g->bitmap.width;
75
+    *height = g->bitmap.rows;
76
+    *bearing_x = g->bitmap_left;
77
+    *bearing_y = g->bitmap_top;
78
+    *advance = g->advance.x >> 6;  /* Convert from 26.6 fixed point */
79
+
80
+    return 0;
81
+}
82
+
83
+/* Get size of glyph bitmap without rendering (for atlas planning) */
84
+int fortty_ft_get_glyph_size(FT_Face face, unsigned int codepoint,
85
+                              int *width, int *height) {
86
+    FT_Error err = FT_Load_Char(face, codepoint, FT_LOAD_BITMAP_METRICS_ONLY);
87
+    if (err) return err;
88
+
89
+    *width = face->glyph->bitmap.width;
90
+    *height = face->glyph->bitmap.rows;
91
+
92
+    /* If metrics-only didn't give us size, do full load */
93
+    if (*width == 0 && *height == 0) {
94
+        err = FT_Load_Char(face, codepoint, FT_LOAD_DEFAULT);
95
+        if (err) return err;
96
+        *width = face->glyph->bitmap.width;
97
+        *height = face->glyph->bitmap.rows;
98
+    }
99
+
100
+    return 0;
101
+}
src/text/font.f90added
@@ -0,0 +1,158 @@
1
+module font_mod
2
+  use, intrinsic :: iso_c_binding
3
+  use glyph_mod
4
+  implicit none
5
+  private
6
+
7
+  public :: font_t
8
+  public :: font_load, font_destroy, font_render_glyph
9
+
10
+  ! Font handle and metrics
11
+  type :: font_t
12
+    type(c_ptr) :: ft_library = c_null_ptr
13
+    type(c_ptr) :: ft_face = c_null_ptr
14
+    integer :: size_px = 0
15
+    integer :: cell_width = 0
16
+    integer :: cell_height = 0
17
+    integer :: ascender = 0
18
+    integer :: descender = 0
19
+    logical :: loaded = .false.
20
+  end type font_t
21
+
22
+  ! C interface to FreeType helpers
23
+  interface
24
+    integer(c_int) function fortty_ft_init(lib) bind(C, name="fortty_ft_init")
25
+      import :: c_int, c_ptr
26
+      type(c_ptr), intent(out) :: lib
27
+    end function fortty_ft_init
28
+
29
+    subroutine fortty_ft_done(lib) bind(C, name="fortty_ft_done")
30
+      import :: c_ptr
31
+      type(c_ptr), value :: lib
32
+    end subroutine fortty_ft_done
33
+
34
+    integer(c_int) function fortty_ft_load_font(lib, path, size_px, face) &
35
+        bind(C, name="fortty_ft_load_font")
36
+      import :: c_int, c_ptr, c_char
37
+      type(c_ptr), value :: lib
38
+      character(kind=c_char), intent(in) :: path(*)
39
+      integer(c_int), value :: size_px
40
+      type(c_ptr), intent(out) :: face
41
+    end function fortty_ft_load_font
42
+
43
+    subroutine fortty_ft_done_face(face) bind(C, name="fortty_ft_done_face")
44
+      import :: c_ptr
45
+      type(c_ptr), value :: face
46
+    end subroutine fortty_ft_done_face
47
+
48
+    subroutine fortty_ft_get_metrics(face, cell_width, cell_height, &
49
+                                      ascender, descender) &
50
+        bind(C, name="fortty_ft_get_metrics")
51
+      import :: c_int, c_ptr
52
+      type(c_ptr), value :: face
53
+      integer(c_int), intent(out) :: cell_width, cell_height
54
+      integer(c_int), intent(out) :: ascender, descender
55
+    end subroutine fortty_ft_get_metrics
56
+
57
+    integer(c_int) function fortty_ft_render_glyph(face, codepoint, &
58
+        bitmap, width, height, bearing_x, bearing_y, advance) &
59
+        bind(C, name="fortty_ft_render_glyph")
60
+      import :: c_int, c_ptr
61
+      type(c_ptr), value :: face
62
+      integer(c_int), value :: codepoint
63
+      type(c_ptr), intent(out) :: bitmap
64
+      integer(c_int), intent(out) :: width, height
65
+      integer(c_int), intent(out) :: bearing_x, bearing_y, advance
66
+    end function fortty_ft_render_glyph
67
+  end interface
68
+
69
+contains
70
+
71
+  ! Load a font from file
72
+  function font_load(path, size_px) result(font)
73
+    character(len=*), intent(in) :: path
74
+    integer, intent(in) :: size_px
75
+    type(font_t) :: font
76
+    character(len=256) :: c_path
77
+    integer(c_int) :: err
78
+    integer(c_int) :: cw, ch, asc, desc
79
+
80
+    ! Initialize FreeType
81
+    err = fortty_ft_init(font%ft_library)
82
+    if (err /= 0) then
83
+      print *, "Error: Failed to initialize FreeType, error ", err
84
+      return
85
+    end if
86
+
87
+    ! Load font face
88
+    c_path = trim(path) // c_null_char
89
+    err = fortty_ft_load_font(font%ft_library, c_path, int(size_px, c_int), font%ft_face)
90
+    if (err /= 0) then
91
+      print *, "Error: Failed to load font '", trim(path), "', error ", err
92
+      call fortty_ft_done(font%ft_library)
93
+      font%ft_library = c_null_ptr
94
+      return
95
+    end if
96
+
97
+    ! Get metrics
98
+    call fortty_ft_get_metrics(font%ft_face, cw, ch, asc, desc)
99
+    font%size_px = size_px
100
+    font%cell_width = cw
101
+    font%cell_height = ch
102
+    font%ascender = asc
103
+    font%descender = desc
104
+    font%loaded = .true.
105
+
106
+  end function font_load
107
+
108
+  ! Destroy font and free resources
109
+  subroutine font_destroy(font)
110
+    type(font_t), intent(inout) :: font
111
+
112
+    if (c_associated(font%ft_face)) then
113
+      call fortty_ft_done_face(font%ft_face)
114
+      font%ft_face = c_null_ptr
115
+    end if
116
+
117
+    if (c_associated(font%ft_library)) then
118
+      call fortty_ft_done(font%ft_library)
119
+      font%ft_library = c_null_ptr
120
+    end if
121
+
122
+    font%loaded = .false.
123
+  end subroutine font_destroy
124
+
125
+  ! Render a glyph and return its data
126
+  ! Returns bitmap pointer (valid until next render_glyph call)
127
+  function font_render_glyph(font, codepoint, bitmap_ptr) result(glyph)
128
+    type(font_t), intent(in) :: font
129
+    integer, intent(in) :: codepoint
130
+    type(c_ptr), intent(out) :: bitmap_ptr
131
+    type(glyph_t) :: glyph
132
+    integer(c_int) :: err, w, h, bx, by, adv
133
+
134
+    call glyph_init(glyph)
135
+
136
+    if (.not. font%loaded) then
137
+      bitmap_ptr = c_null_ptr
138
+      return
139
+    end if
140
+
141
+    err = fortty_ft_render_glyph(font%ft_face, int(codepoint, c_int), &
142
+                                  bitmap_ptr, w, h, bx, by, adv)
143
+    if (err /= 0) then
144
+      bitmap_ptr = c_null_ptr
145
+      return
146
+    end if
147
+
148
+    glyph%codepoint = codepoint
149
+    glyph%width = w
150
+    glyph%height = h
151
+    glyph%bearing_x = bx
152
+    glyph%bearing_y = by
153
+    glyph%advance = adv
154
+    glyph%valid = .true.
155
+
156
+  end function font_render_glyph
157
+
158
+end module font_mod
src/text/glyph.f90added
@@ -0,0 +1,39 @@
1
+module glyph_mod
2
+  use, intrinsic :: iso_c_binding
3
+  implicit none
4
+  public
5
+
6
+  ! Glyph information for a single character
7
+  type :: glyph_t
8
+    integer :: codepoint = 0         ! Unicode codepoint
9
+    integer :: tex_x = 0, tex_y = 0  ! Position in texture atlas
10
+    integer :: width = 0, height = 0 ! Bitmap dimensions
11
+    integer :: bearing_x = 0         ! Offset from cursor X to left edge
12
+    integer :: bearing_y = 0         ! Offset from baseline to top edge
13
+    integer :: advance = 0           ! Horizontal advance to next character
14
+    real(c_float) :: u0 = 0.0, v0 = 0.0  ! UV coords (top-left)
15
+    real(c_float) :: u1 = 0.0, v1 = 0.0  ! UV coords (bottom-right)
16
+    logical :: valid = .false.       ! Whether glyph data is loaded
17
+  end type glyph_t
18
+
19
+contains
20
+
21
+  ! Initialize a glyph with default values
22
+  subroutine glyph_init(g)
23
+    type(glyph_t), intent(out) :: g
24
+    g%codepoint = 0
25
+    g%tex_x = 0
26
+    g%tex_y = 0
27
+    g%width = 0
28
+    g%height = 0
29
+    g%bearing_x = 0
30
+    g%bearing_y = 0
31
+    g%advance = 0
32
+    g%u0 = 0.0
33
+    g%v0 = 0.0
34
+    g%u1 = 0.0
35
+    g%v1 = 0.0
36
+    g%valid = .false.
37
+  end subroutine glyph_init
38
+
39
+end module glyph_mod