summaryrefslogtreecommitdiff
path: root/src/text.cc
blob: fab8cbb168bdd9e5a905b565279f77f4ef2a7634 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include "text.h"

#include <cstddef>
#include <cstring>

#include "atlas.h"
#include "cglm/types.h"
#include "cglm/vec2.h"
#include "freetype/freetype.h"
#include "freetype/fttypes.h"
#include "hash.h"
#include "shaders.h"
#include "utils.h"

static void wayc_font_context_sampler_desc(struct sg_sampler_desc* desc) {
  wayc_notnull(desc);

  desc->min_filter = SG_FILTER_LINEAR;
  desc->mag_filter = SG_FILTER_LINEAR;
  desc->wrap_u = SG_WRAP_CLAMP_TO_EDGE;
  desc->wrap_v = SG_WRAP_CLAMP_TO_EDGE;
}

static void wayc_font_context_layout(struct sg_vertex_layout_state* layout) {
  wayc_notnull(layout);

  layout->buffers[0].stride = sizeof(text_vertex_s);

  layout->attrs[ATTR_text_in_position].format = SG_VERTEXFORMAT_FLOAT2;
  layout->attrs[ATTR_text_in_position].buffer_index = 0;
  layout->attrs[ATTR_text_in_position].offset = offsetof(text_vertex_s, pos);

  layout->attrs[ATTR_text_in_uv].format = SG_VERTEXFORMAT_FLOAT2;
  layout->attrs[ATTR_text_in_uv].buffer_index = 0;
  layout->attrs[ATTR_text_in_uv].offset = offsetof(text_vertex_s, uv);
}

static void wayc_font_context_color(struct sg_color_target_state* color) {
  wayc_notnull(color);

  struct sg_blend_state blend = {};
  blend.enabled = true;
  blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
  blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
  blend.src_factor_alpha = SG_BLENDFACTOR_SRC_ALPHA;
  blend.dst_factor_alpha = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;

  color->blend = blend;
}

static void font_context_pipeline_desc(struct sg_pipeline_desc* desc,
                                       struct sg_shader shader) {
  wayc_notnull(desc);

  struct sg_vertex_layout_state vertex_layout = {};
  wayc_font_context_layout(&vertex_layout);

  struct sg_color_target_state color = {};
  wayc_font_context_color(&color);

  desc->layout = vertex_layout;
  desc->shader = shader;

  desc->colors[0] = color;
  desc->color_count = 1;
}

enum font_context_error_e wayc_font_context_init(
    struct font_context_s* context) {
  wayc_notnull(context);
  memset(context, 0, sizeof(*context));
  bool success = false;

  FT_Library ft;
  FT_Error fterr = FT_Init_FreeType(&ft);
  if (fterr) return FONT_CONTEXT_ERROR_LOAD_LIBRARY;
  wayc_defer_cond(FT_Done_FreeType(ft), success, true);

  struct sg_shader shader =
      sg_make_shader(text_shader_desc(sg_query_backend()));
  if (sg_query_shader_state(shader) != SG_RESOURCESTATE_VALID)
    return FONT_CONTEXT_ERROR_CREATE_SHADER;
  wayc_defer_cond(sg_destroy_shader(shader), success, true);

  struct sg_sampler_desc sampler_desc = {};
  wayc_font_context_sampler_desc(&sampler_desc);

  struct sg_pipeline_desc pipeline_desc = {};
  font_context_pipeline_desc(&pipeline_desc, shader);

  struct sg_sampler sampler = sg_make_sampler(&sampler_desc);
  if (sg_query_sampler_state(sampler) != SG_RESOURCESTATE_VALID)
    return FONT_CONTEXT_ERROR_CREATE_SAMPLER;

  wayc_defer_cond(sg_destroy_sampler(sampler), success, true);

  struct sg_pipeline pipeline = sg_make_pipeline(&pipeline_desc);
  if (sg_query_pipeline_state(pipeline) != SG_RESOURCESTATE_VALID)
    return FONT_CONTEXT_ERROR_CREATE_PIPELINE;

  context->ft = ft;
  context->sampler = sampler;
  context->pipeline = pipeline;

  success = true;

  return FONT_CONTEXT_ERROR_NONE;
}

void wayc_font_context_deinit(struct font_context_s* context) {
  wayc_notnull(context);

  sg_destroy_pipeline(context->pipeline);
  sg_destroy_sampler(context->sampler);
  FT_Done_FreeType(context->ft);
}

enum font_error_e wayc_font_init(struct font_s* font,
                                 struct font_context_s* context,
                                 const char* path, u32 font_size,
                                 u32 atlas_width, u32 atlas_height) {
  wayc_notnull(font);
  wayc_notnull(context);
  memset(font, 0, sizeof(*font));
  bool success = false;

  FT_Face face;
  FT_Error fterr = FT_New_Face(context->ft, path, 0, &face);
  if (fterr) return FONT_ERROR_LOAD_FACE;
  wayc_defer_cond(FT_Done_Face(face), success, true);

  fterr = FT_Set_Pixel_Sizes(face, 0, font_size);
  if (fterr) return FONT_ERROR_LOAD_FACE;

  struct atlas_s atlas;
  enum atlas_error_e aterr =
      wayc_atlas_init(&atlas, atlas_width, atlas_height, SG_PIXELFORMAT_R8);
  if (aterr) return FONT_ERROR_ATLAS_FAILED;

  font->context = context;
  font->face = face;
  font->atlas = atlas;
  wayc_hashmap_init(&font->cached);

  success = true;
  return FONT_ERROR_NONE;
}

enum font_error_e wayc_font_raster(struct font_s* font, codepoint_t codepoint,
                                   struct glyph_s* glyph) {
  wayc_notnull(font);
  wayc_notnull(glyph);

  struct glyph_s got;
  if (wayc_hashmap_get(&font->cached, &codepoint, &got)) {
    *glyph = got;
    return FONT_ERROR_NONE;
  }

  u32 index = FT_Get_Char_Index(font->face, codepoint);
  if (index == 0) return FONT_ERROR_LOAD_GLYPH;

  FT_Error fterr = FT_Load_Glyph(font->face, index, FT_LOAD_RENDER);
  if (fterr) return FONT_ERROR_LOAD_GLYPH;

  FT_GlyphSlot slot = font->face->glyph;
  FT_Bitmap bitmap = slot->bitmap;

  vec2 uv0, uv1;

  enum atlas_error_e aterr = wayc_atlas_insert(
      &font->atlas, bitmap.width, bitmap.rows, bitmap.buffer, &uv0, &uv1);
  if (aterr) return FONT_ERROR_ATLAS_FAILED;

  vec2 bearing = {(f32)slot->bitmap_left, (f32)slot->bitmap_top};
  f32 advance = (f32)slot->advance.x / WAYC_SCALE;

  glm_vec2_copy(uv0, got.uv0);
  glm_vec2_copy(uv1, got.uv1);
  glm_vec2_copy(bearing, got.bearing);
  got.advance = advance;

  wayc_hashmap_insert(&font->cached, &codepoint, &got);

  *glyph = got;
  return FONT_ERROR_NONE;
}

enum font_error_e wayc_font_raster(struct font_s* font, const char* text,
                                   usize text_len, struct glyph_s* glyphs,
                                   usize max_glyphs) {
  wayc_notnull(font);
  wayc_notnull(text);
  wayc_notnull(glyphs);
  memset(glyphs, 0, sizeof(*glyphs) * max_glyphs);

  usize count = 0;
  for (usize i = 0; i < text_len && count < max_glyphs; i++) {
    codepoint_t codepoint = (codepoint_t)text[i];  // TODO: handle UTF-8
    struct glyph_s glyph;
    enum font_error_e err = wayc_font_raster(font, codepoint, &glyph);
    if (err) return err;

    glyphs[count++] = glyph;
  }

  return FONT_ERROR_NONE;
}

enum font_error_e wayc_font_raster_text(struct font_s* font, const char* text,
                                        usize text_len, struct glyph_s* glyphs,
                                        usize max_glyphs) {
  wayc_notnull(font);
  wayc_notnull(text);
  wayc_notnull(glyphs);
  memset(glyphs, 0, sizeof(*glyphs) * max_glyphs);

  usize count = 0;
  for (usize i = 0; i < text_len && count < max_glyphs; i++) {
    codepoint_t codepoint = (codepoint_t)text[i];  // TODO: handle utf-8

    struct glyph_s glyph;
    enum font_error_e err = wayc_font_raster(font, codepoint, &glyph);
    if (err != FONT_ERROR_NONE) return err;

    glyphs[count++] = glyph;
  }

  return FONT_ERROR_NONE;
}

void wayc_font_deinit(struct font_s* font) {
  wayc_notnull(font);

  wayc_hashmap_deinit(&font->cached);
  wayc_atlas_deinit(&font->atlas);
  FT_Done_Face(font->face);
}