summaryrefslogtreecommitdiff
path: root/src/text.cc
blob: 6de35065d4656c574819e89b0216939d7bb68a4d (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
#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);

  FT_Set_Pixel_Sizes(face, 0, font_size);

  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->face = face;
  font->font_size = font_size;
  font->atlas = atlas;
  wayc_hashmap_init(&font->cached);

  success = true;
  return FONT_ERROR_NONE;
}

enum font_error_e wayc_font_render(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);
  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);
}