font_atlas_generator.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. * Copyright (c) 2012-2026 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #include <stdbool.h>
  6. #include <stdint.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <ft2build.h>
  10. #include FT_FREETYPE_H
  11. #define STB_RECT_PACK_IMPLEMENTATION
  12. #include <stb_rect_pack.h>
  13. #define CROWN_RECT_MARGIN 1 ///< Right and bottom glyph rect margin in pixels.
  14. struct GlyphData
  15. {
  16. int id; ///< Codepoint.
  17. int x; ///< X-position inside the atlas.
  18. int y; ///< Y-position inside the atlas.
  19. int width; ///< In pixels.
  20. int height; ///< In pixels.
  21. int x_offset; ///< In pixels.
  22. int y_offset; ///< In pixels.
  23. int x_advance; ///< In pixels.
  24. };
  25. struct FontAtlas
  26. {
  27. uint8_t *image_data; ///< 8-bits alpha channel.
  28. int atlas_size; ///< In pixels.
  29. struct GlyphData *glyphs;
  30. int num_glyphs;
  31. };
  32. /// Frees the @a atlas returned from crown_font_atlas_generate().
  33. void crown_font_atlas_free(struct FontAtlas *atlas)
  34. {
  35. if (atlas != NULL) {
  36. free(atlas->image_data);
  37. free(atlas->glyphs);
  38. }
  39. free(atlas);
  40. }
  41. /// Returns a FontAtlas structure.
  42. /// Call crown_font_atlas_free() when you are done with it.
  43. void *crown_font_atlas_generate(const char *font_path, int font_size, int range_min, int range_max)
  44. {
  45. FT_Library ft_library;
  46. if (FT_Init_FreeType(&ft_library))
  47. return NULL;
  48. FT_Face ft_face;
  49. if (FT_New_Face(ft_library, font_path, 0, &ft_face))
  50. goto err_done_freetype;
  51. if (FT_Set_Pixel_Sizes(ft_face, 0, font_size))
  52. goto err_done_face;
  53. struct FontAtlas *atlas = malloc(sizeof(*atlas));
  54. if (atlas == NULL)
  55. goto err_done_face;
  56. atlas->num_glyphs = range_max - range_min + 1;
  57. atlas->glyphs = malloc(sizeof(*atlas->glyphs) * atlas->num_glyphs);
  58. // Load and render all the glyphs.
  59. for (int codepoint = range_min; codepoint <= range_max; ++codepoint) {
  60. FT_UInt glyph_index = FT_Get_Char_Index(ft_face, codepoint);
  61. if (glyph_index == 0) // Glyph not available.
  62. continue;
  63. if (FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_RENDER))
  64. continue;
  65. FT_Bitmap *glyph_bitmap = &ft_face->glyph->bitmap;
  66. struct GlyphData *glyph = &atlas->glyphs[codepoint - range_min];
  67. glyph->id = codepoint;
  68. glyph->x = 0;
  69. glyph->y = 0;
  70. glyph->width = glyph_bitmap->width;
  71. glyph->height = glyph_bitmap->rows;
  72. glyph->x_advance = ft_face->glyph->advance.x / 64.0f;
  73. glyph->x_offset = ft_face->glyph->bitmap_left;
  74. glyph->y_offset = ft_face->glyph->bitmap_top;
  75. }
  76. // Find the optimal size for the atlas.
  77. int atlas_width = 32;
  78. int atlas_height = 32;
  79. struct stbrp_rect *rects = NULL;
  80. struct stbrp_node *nodes = NULL;
  81. bool all_packed;
  82. do {
  83. // Pack glyph rects into the atlas box.
  84. struct stbrp_context ctx;
  85. free(nodes);
  86. nodes = (stbrp_node *)malloc(sizeof(*nodes) * atlas_width);
  87. stbrp_init_target(&ctx, atlas_width, atlas_height, nodes, atlas_width);
  88. free(rects);
  89. rects = (stbrp_rect *)malloc(sizeof(*rects) * atlas->num_glyphs);
  90. for (int ii = 0; ii < atlas->num_glyphs; ++ii) {
  91. rects[ii].id = ii;
  92. rects[ii].w = CROWN_RECT_MARGIN + atlas->glyphs[ii].width;
  93. rects[ii].h = CROWN_RECT_MARGIN + atlas->glyphs[ii].height;
  94. }
  95. all_packed = true;
  96. stbrp_pack_rects(&ctx, rects, atlas->num_glyphs);
  97. if (atlas_width >= 1024)
  98. break;
  99. // Up-size the atlas box if some rects failed to pack.
  100. for (int ii = 0; ii < atlas->num_glyphs; ++ii) {
  101. if (rects[ii].was_packed == 0) {
  102. all_packed = false;
  103. atlas_width *= 2;
  104. atlas_height *= 2;
  105. break;
  106. }
  107. }
  108. } while (!all_packed);
  109. // Allocate the atlas image.
  110. atlas->atlas_size = atlas_width;
  111. atlas->image_data = (uint8_t *)calloc(atlas_width * atlas_height, sizeof(*atlas->image_data));
  112. if (!atlas->image_data)
  113. goto err_free_atlas;
  114. // Pack the glyphs into the atlas image.
  115. for (int codepoint = range_min; codepoint <= range_max; ++codepoint) {
  116. FT_UInt glyph_index = FT_Get_Char_Index(ft_face, codepoint);
  117. if (glyph_index == 0) // Glyph not available.
  118. continue;
  119. FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_RENDER);
  120. FT_Bitmap *glyph_bitmap = &ft_face->glyph->bitmap;
  121. stbrp_rect *rect = &rects[codepoint - range_min];
  122. if (rect->was_packed == 0)
  123. continue;
  124. struct GlyphData *glyph = &atlas->glyphs[rect->id];
  125. glyph->x = rect->x;
  126. glyph->y = rect->y;
  127. for (int y = 0; y < glyph_bitmap->rows; ++y) {
  128. for (int x = 0; x < glyph_bitmap->width; ++x) {
  129. uint8_t intensity = glyph_bitmap->buffer[y * glyph_bitmap->pitch + x];
  130. atlas->image_data[(rect->y + y) * atlas_width + (rect->x + x)] = intensity;
  131. }
  132. }
  133. }
  134. free(rects);
  135. free(nodes);
  136. return atlas;
  137. err_free_atlas:
  138. crown_font_atlas_free(atlas);
  139. err_free_rects:
  140. free(rects);
  141. free(nodes);
  142. err_done_face:
  143. FT_Done_Face(ft_face);
  144. err_done_freetype:
  145. FT_Done_FreeType(ft_library);
  146. return NULL;
  147. }