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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
// Copyright (c) 2018-2026 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
//
// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
#ifndef VKVG_FONTS_H
#define VKVG_FONTS_H
// disable warning on iostream functions on windows
#define _CRT_SECURE_NO_WARNINGS
#include "vkvg_internal.h"
#include "vkh_buffer.h"
#ifdef VKVG_USE_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_SIZES_H
#include FT_MULTIPLE_MASTERS_H
#if defined(VKVG_LCD_FONT_FILTER) && defined(FT_CONFIG_OPTION_SUBPIXEL_RENDERING)
#include <freetype/ftlcdfil.h>
#endif
#define FT_CHECK_RESULT(f) \
{ \
FT_Error res = (f); \
if (res != 0) { \
fprintf(stderr, "Fatal : FreeType error is %x in %s at line %d\n", res, __FILE__, __LINE__); \
assert(res == 0); \
} \
}
#else
#include "stb_truetype.h"
#endif
#ifdef VKVG_USE_HARFBUZZ
#include <harfbuzz/hb.h>
#if VKVG_USE_FREETYPE
#include <harfbuzz/hb-ft.h>
#endif
#else
#endif
#ifdef VKVG_USE_FONTCONFIG
#include <fontconfig/fontconfig.h>
#endif
#define FONT_PAGE_SIZE 1024
#define FONT_CACHE_INIT_LAYERS 2
#define FONT_FILE_NAME_MAX_SIZE 1024
#define FONT_NAME_MAX_SIZE 128
// Bjoern Hoehrmann's UTF-8 State Magic Constants
#define UTF8_ACCEPT 0
#define UTF8_REJECT 1
static const uint8_t utf8d[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
};
/**
* @brief Decodes a single byte stream step into a Unicode code point.
* @param state Pointer to the persistent decoder state tracker (initialize to UTF8_ACCEPT).
* @param codep Pointer to the resulting uint32_t Unicode scalar value.
* @param byte The raw next byte from the UTF-8 stream.
* @return uint32_t Returns UTF8_ACCEPT when a character is fully decoded.
*/
static inline uint32_t decode_utf8_byte(uint32_t *const restrict state, uint32_t *const restrict codep, uint8_t byte) {
uint32_t type = utf8d[byte];
*codep = (*state != UTF8_ACCEPT) ?
(byte & 0x3fu) | (*codep << 6) :
(0xffu >> type) & (byte);
*state = utf8d[256 + *state * 16 + type];
return *state;
}
static uint32_t inline decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
uint32_t type = utf8d[byte];
*codep = (*state != UTF8_ACCEPT) ?
(byte & 0x3fu) | (*codep << 6) :
(0xff >> type) & (byte);
*state = utf8d[256 + *state*16 + type];
return *state;
}
// A perfectly optimized, branchless UTF-8 length lookup table
static const uint8_t utf8_len_table[256] = {
// 0x00 to 0x7F: Standard ASCII (1 byte)
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
// 0x80 to 0xBF: Continuation bytes (invalid as first byte, but default to 1 to prevent infinite loops)
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
// 0xC0 to 0xDF: Multi-byte leaders (2 bytes)
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
// 0xE0 to 0xEF: Multi-byte leaders (3 bytes)
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
// 0xF0 to 0xF7: Multi-byte leaders (4 bytes)
4,4,4,4,4,4,4,4,
// 0xF8 to 0xFF: Invalid UTF-8 (safely fallback to 1)
1,1,1,1,1,1,1,1
};
static inline uint32_t decode_unicode_codepoint(const uint8_t* text) {
if (text == NULL || *text == '\0')
return 0;
uint32_t state = UTF8_ACCEPT;
uint32_t codepoint = 0;
int i = 0;
while (text[i] != '\0') {
decode_utf8_byte(&state, &codepoint, text[i]);
if (state == UTF8_ACCEPT)
return codepoint;
if (state == UTF8_REJECT) {
LOG(VKVG_LOG_DEBUG, "Malformed utf8 string.\n");
return 0;
}
i++;
}
// If the loop finished but we never reached UTF8_ACCEPT, the string cut off abruptly
if (state == UTF8_ACCEPT)
return codepoint;
LOG(VKVG_LOG_DEBUG, "Error: Incomplete UTF-8 character sequence.\n");
return 0;
}
static inline int get_utf8_char_length(uint8_t first_byte) {
return utf8_len_table[first_byte];
}
typedef struct _tex_ref_t tex_ref_t;
typedef struct _tex_ref_t* TexRef;
// texture coordinates of one character in font cache array texture.
typedef struct _char_ref {
TexRef texRef; /* glyph bitmap ref */
vec2i16 bmpDiff; /* Difference in pixel between char bitmap top left corner and char glyph*/
vec4 bounds; /* normalized float bounds of character bitmap in font cache texture. */
#ifdef VKVG_USE_FREETYPE
FT_Vector advance; /* horizontal or vertical advance */
#else
vec2 advance;
#endif
} glyph_ref;
// Current location in font cache texture array for new character addition. Each font holds such structure to locate
// where to upload new chars.
typedef struct _tex_ref_t {
uint8_t pageIdx; /* Current page number in font cache */
int penY; /* Current Y in cache for next char addition */
int height; /* Height of current line pointed by this structure in pixel */
bool released; /* True after font destroy, may be reused for another font */
} tex_ref_t;
DIAGNOSTIC_DISABLE_UNUSED
CTOR_ARRAY(TexRef)
CTOR_ARRAY(uint64_t)
CTOR_ARRAY(VkvgFont)
ARRAY_DEL_ELT(VkvgFont)
typedef struct _tex_page_t {
array_TexRef lines;
float penY;
} tex_page_t;
CTOR_ARRAY(tex_page_t)
typedef struct _vkvg_font_buffer_t *FontBuffer;
typedef struct _vkvg_font_face_t *FontFace;
/* Font identification structure */
typedef struct _vkvg_font_face_t{
FontBuffer fontBuffer;
array_uint64_t queryHashes;
int faceIndex;
#ifdef VKVG_USE_FREETYPE
FT_Face face; /* FreeType face*/
mtx_t mutex; /* Only one font size at a time may use this face */
signed long* coords; /* Active coordinates for variable fonts */
unsigned int numAxis; /* face axis count */
#else
stbtt_fontinfo stbInfo; /* stb_truetype structure */
int ascent; /* unscalled stb font metrics */
int descent;
int lineGap;
#endif
#ifdef VKVG_USE_HARFBUZZ
hb_font_t* hb_font; /* HarfBuzz font instance*/
#endif
array_VkvgFont sizes; /* loaded font size array */
} vkvg_font_face_t;
CTOR_ARRAY(FontFace)
ARRAY_DEL_ELT(FontFace)
typedef struct _vkvg_font_buffer_t {
uint64_t fontPathHash;
size_t bufferSize;
unsigned char* buffer;
array_FontFace faces;
} vkvg_font_buffer_t;
CTOR_ARRAY(FontBuffer)
ARRAY_DEL_ELT(FontBuffer)
DIAGNOSTIC_RESTORE_UNUSED
typedef struct _vkvg_font_t {
vkvg_status_t status;
atomic_int references; // reference count
FontFace face;
VkvgDevice dev; // to access font cache when required
#ifdef VKVG_USE_FREETYPE
FT_F26Dot6 charSize; /* Font size in Point as fixed float 26.6 */
FT_Size ftSize; /* FT size rec */
signed long* coords; /* Variable fonts axes coordinates */
#else
float charSize; /* Font size in pixel */
float scale; /* scale factor for the given size */
int ascent; /* unscalled stb font metrics */
int descent;
int lineGap;
#endif
glyph_ref* charLookup;/* Lookup table of characteres in cache, if not found, upload is queued*/
array_TexRef texLines; /* texture reference where to add new glyph bmp's in cache */
int penX; /* Current X in cache for next char addition */
float height; /* Height in pixel */
} vkvg_font_t;
// Font cache global structure, entry point for all font related operations.
typedef struct _font_cache_t {
#ifdef VKVG_USE_FREETYPE
FT_Library library; /* FreeType library*/
#else
#endif
#ifdef VKVG_USE_FONTCONFIG
FcConfig* config; /* Font config, used to find font files by font names*/
//FcPattern* fcPattern; /* patter used for all Font config queries */
FcObjectSet* fcReqElts; /* Font config requested metadata elements */
#endif
int stagingX; /* x pen in host buffer */
uint8_t* hostBuff; /* host memory where bitmaps are first loaded */
VkCommandBuffer cmd; /* vulkan command buffer for font textures upload */
vkh_buffer_t buff; /* stagin buffer */
VkhImage texture; /* 2d array texture used by contexts to draw characteres */
VkDescriptorPool descriptorPool;
VkDescriptorSet dsFont; /* fonts glyphs texture atlas descriptor set */
VkFormat texFormat; /* Format of the fonts texture array */
uint8_t texPixelSize; /* Size in byte of a single pixel in a font texture */
VkFence uploadFence; /* Signaled when upload is finished */
mtx_t mutex; /* font cache global mutex, used only if device is in thread aware mode (see:
vkvg_device_set_thread_aware) */
array_FontBuffer fontBuffers;
array_tex_page_t texPages;
} font_cache_t;
#define LOCK_FONTCACHE(dev) \
if (dev->threadAware) \
mtx_lock(&dev->fontCache->mutex);
#define UNLOCK_FONTCACHE(dev) \
if (dev->threadAware) \
mtx_unlock(&dev->fontCache->mutex);
// Precompute everything necessary to measure and draw one line of text, usefull to draw the same text multiple times.
typedef struct _vkvg_text_run_t {
VkvgFont font; /* vkvg font structure pointer */
vkvg_direction_t textDirection; /* horizontal or vertical */
vkvg_text_extents_t extents; /* store computed text extends */
const char* text; /* utf8 char array of text*/
unsigned int glyph_count;/* Total glyph count */
#ifdef VKVG_USE_HARFBUZZ
hb_buffer_t* hbBuf; /* HarfBuzz buffer of text */
hb_glyph_position_t* glyphs; /* HarfBuzz computed glyph positions array */
#else
vkvg_glyph_info_t* glyphs; /* computed glyph positions array */
#endif
} vkvg_text_run_t;
void _fonts_cache_create(VkvgDevice dev, const char *fontDirs);
void _font_cache_destroy(VkvgDevice dev);
bool _font_cache_load_font_file_in_memory(vkvg_font_face_t* fontId);
void _font_cache_show_text(VkvgContext ctx, const char* text);
void _font_cache_text_extents(VkvgContext ctx, const char* text, int length, vkvg_text_extents_t* extents);
void _font_cache_font_extents(VkvgFont font, vkvg_font_extents_t* extents);
void _font_cache_update_context_descset(VkvgContext ctx);
void text_run_init(VkvgText textRun, VkvgFont font, vkvg_direction_t textDirection, const char* text, int length);
void text_run_term(VkvgText textRun);
void text_run_render(VkvgText tr, VkvgContext ctx); // Draw text run
#endif
|