microvg  8.0.0
microvg
LLVG_FONT_impl_freetype.c
Go to the documentation of this file.
1 /*
2  * C
3  *
4  * Copyright 2020-2026 MicroEJ Corp. All rights reserved.
5  * MicroEJ Corp. PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */
7 
15 #include "vg_configuration.h"
16 
17 #if defined VG_FEATURE_FONT && \
18  (defined VG_FEATURE_FONT_FREETYPE_VECTOR || defined VG_FEATURE_FONT_FREETYPE_BITMAP) && \
19  (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR || VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
20 
21 // -----------------------------------------------------------------------------
22 // Includes
23 // -----------------------------------------------------------------------------
24 
25 #include <math.h>
26 #include <string.h>
27 #include <aftypes.h>
28 
29 #include <LLVG_impl.h>
30 #include <LLVG_FONT_impl.h>
31 
32 #if defined VG_FEATURE_FONT_EXTERNAL && (VG_FEATURE_FONT_EXTERNAL == 1)
33 #include <LLEXT_RES_impl.h>
34 #include <freetype/internal/ftmemory.h>
35 #endif
36 
37 #include "vg_freetype.h"
38 #include "vg_helper.h"
39 #include "vg_trace.h"
40 #include "ui_util.h"
41 
42 // -----------------------------------------------------------------------------
43 // Macros and Defines
44 // -----------------------------------------------------------------------------
45 
46 /*
47  * @brief Computes the scale to apply to the font.
48  */
49 #define GET_SCALE(s, f) ((s) / (f)->units_per_EM)
50 
51 /*
52  * @brief Macro to add a FONT event and its type.
53  */
54 #define LOG_MICROVG_FONT_START(fn) LOG_MICROVG_START(LOG_MICROVG_FONT_ID, CONCAT_DEFINES(LOG_MICROVG_FONT_, fn))
55 #define LOG_MICROVG_FONT_END(fn) LOG_MICROVG_END(LOG_MICROVG_FONT_ID, CONCAT_DEFINES(LOG_MICROVG_FONT_, fn))
56 
57 // -----------------------------------------------------------------------------
58 // Types
59 // -----------------------------------------------------------------------------
60 
61 /*
62  * @brief Structure to load a resource by calling SNIX_get_resource()
63  */
64 typedef struct {
65  void *data;
66  uint32_t size;
67 } SNIX_resource;
68 
69 // -----------------------------------------------------------------------------
70 // Extern functions
71 // -----------------------------------------------------------------------------
72 
73 /*
74  * @brief SNIX_get_resource() available since MicroEJ Architecture version 7.13
75  */
76 extern int32_t SNIX_get_resource(jchar *path, SNIX_resource *resource);
77 
78 // -----------------------------------------------------------------------------
79 // Internal function definitions
80 // -----------------------------------------------------------------------------
81 
82 /*
83  * @brief Disposes Freetype font when the associated Java object VectorFont is
84  * garbaged collected.
85  */
86 static void __dispose_registered_font(void *faceHandle);
87 
88 /*
89  * @brief Disposes Freetype font.
90  */
91 static void __dispose_font(void *faceHandle);
92 
93 /*
94  * @brief Opens a font that has been loaded into memory.
95  *
96  * @see FT_New_Memory_Face in freetype.h
97  *
98  * @param[in] face: a handle to a new face object.
99  * @param[in] data: a pointer to the beginning of the font data.
100  * @param[in] length: the size of the memory chunk used by the font data.
101  *
102  * @return FreeType error code.
103  */
104 static FT_Error __load_memory_font(FT_Face *face, void *data, int length);
105 
106 /*
107  * @brief Opens a font that has been compiled with the application.
108  *
109  * @see FT_New_Memory_Face in freetype.h
110  *
111  * @param[in] face: a handle to a new face object.
112  * @param[in] font_name: a path to the font file.
113  *
114  * @return FreeType error code.
115  */
116 static FT_Error __load_internal_font(FT_Face *face, jchar *font_name);
117 
118 /*
119  * @brief Gets the description of the given native resource: a vector font.
120  * @see SNI_getDescriptionFunction
121  */
122 static void __register_font_description(void *resource, char *buffer, uint32_t bufferLength);
123 
124 #if defined VG_FEATURE_FONT_EXTERNAL && (VG_FEATURE_FONT_EXTERNAL == 1)
125 
126 /*
127  * @brief Opens a font that has not been compiled with the application.
128  *
129  * @see FT_Open_Face in freetype.h
130  *
131  * @param[in] face: a handle to a new face object.
132  * @param[in] font_name: a path to the font file.
133  *
134  * @return FreeType error code.
135  */
136 static FT_Error __load_external_font(FT_Face *face, jchar *font_name);
137 
138 /*
139  * @brief Reads a chunk from an external resource.
140  *
141  * @see FT_Stream_IoFunc in ftsystem.h
142  *
143  * @param[in] stream: a handle to the source stream.
144  * @param[in] offset: the offset of read in stream.
145  * @param[in] buffer: the address of the read buffer.
146  * @param[in] count: the number of bytes to read from the stream.
147  *
148  * @return the number of bytes effectively read by the stream.
149  */
150 static unsigned long __read_external_resource(FT_Stream stream, unsigned long offset, unsigned char *buffer,
151  unsigned long count);
152 
153 /*
154  * @brief Closes a given input stream.
155  *
156  * @see FT_Stream_IoFunc in ftsystem.h
157  *
158  * @param[in] stream: a handle to the target stream.
159  */
160 static void __close_external_resource(FT_Stream stream);
161 
162 #endif // VG_FEATURE_FONT_EXTERNAL
163 
164 // -----------------------------------------------------------------------------
165 // Global Variables
166 // -----------------------------------------------------------------------------
167 
168 FT_Library library;
169 FT_Renderer renderer;
170 
171 #if defined VG_FEATURE_FONT && defined VG_FEATURE_FONT_FREETYPE_VECTOR && \
172  (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR)
173 const char *renderer_name = "microvg";
174 #endif
175 #if defined VG_FEATURE_FONT && defined VG_FEATURE_FONT_FREETYPE_BITMAP && \
176  (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
177 const char *renderer_name = "smooth";
178 #endif
179 
180 // -----------------------------------------------------------------------------
181 // vg_freetype.h functions
182 // -----------------------------------------------------------------------------
183 
184 // See the header file for the function documentation
185 void VG_FREETYPE_initialize(void) {
186  FT_Error error = FT_Init_FreeType(&library);
187  if (FT_ERR(Ok) == error) {
188  renderer = FT_RENDERER(FT_Get_Module(library, renderer_name));
189  if (0 != renderer) {
190  MEJ_LOG_INFO_MICROVG("Freetype renderer: %s\n", FT_MODULE_CLASS(renderer)->module_name);
191  } else {
192  MEJ_LOG_ERROR_MICROVG("No renderer found with name %s\n", renderer_name);
193  }
194  } else {
195  MEJ_LOG_ERROR_MICROVG("Internal freetype error initializing library, ID = %d\n", error);
196  }
197 }
198 
199 // See the header file for the function documentation
200 jfloat VG_FREETYPE_string_width(jchar *text, jint length, jint face_handle, jfloat size, jfloat letter_spacing) {
201  float scaled_width = 0.f;
202 
203  if (size >= 0) {
204  FT_Face face = (FT_Face)face_handle;
205  float scale = GET_SCALE(size, face);
206 
207  int nb_chars = 0;
208  long unscaled_width = 0;
209 
210  // Layout variables
211  FT_UInt glyph_index; // current glyph index
212  FT_UInt previous_glyph_index = 0; // previous glyph index for kerning
213 
214  int advance_x;
215  int previous_advance_x = 0;
216  int previous_offset_x = 0;
217  int advance_y;
218  int offset_x;
219  int offset_y;
220 
221  VG_HELPER_layout_configure(face_handle, text, length);
222 
223  while (VG_HELPER_layout_load_glyph(&glyph_index, &advance_x, &advance_y, &offset_x, &offset_y)) {
224  // At that point the current glyph has been loaded by Freetype
225  if (0 == previous_glyph_index) {
226  // first glyph: remove the first blank line
227  if (0 != face->glyph->metrics.width) {
228  unscaled_width -= face->glyph->metrics.horiBearingX;
229  } else {
230  unscaled_width -= face->glyph->advance.x;
231  }
232  }
233 
234  unscaled_width += advance_x;
235  previous_glyph_index = glyph_index;
236  nb_chars++;
237  // Last call to VG_HELPER_layout_load_glyph clear advance_x and offset_x.
238  // We need to keep them for last glyph measurement
239  previous_advance_x = advance_x;
240  previous_offset_x = offset_x;
241  }
242 
243  // last glyph: remove the last blank line
244  if (0 != face->glyph->metrics.width) {
245  unscaled_width -= previous_advance_x;
246  unscaled_width += face->glyph->metrics.horiBearingX; // glyph's left blank line
247  unscaled_width += face->glyph->metrics.width; // glyph's width
248  unscaled_width += previous_offset_x; // glyph's offset_x
249  } else {
250  if (0 != unscaled_width) {
251  unscaled_width -= previous_advance_x;
252  }
253  }
254  scaled_width = ((scale * (float)unscaled_width) + (((float)nb_chars - 1) * letter_spacing));
255  }
256 
257  return scaled_width;
258 }
259 
260 // -----------------------------------------------------------------------------
261 // LLVG_FONT_impl.h functions
262 // -----------------------------------------------------------------------------
263 
264 // See the header file for the function documentation
265 jint LLVG_FONT_IMPL_load_font(jchar *font_name, jboolean complex_layout) {
266  LOG_MICROVG_FONT_START(load);
267 
268  FT_Face face = 0;
269  jint ret = LLVG_FONT_UNLOADED;
270 
271  // Check that complex layouter is available for complex text layout
272  if (complex_layout && !LLVG_FONT_IMPL_has_complex_layouter()) {
273  ret = LLVG_FONT_UNLOADED;
274  } else {
275  // try to load an internal resource
276  FT_Error error = __load_internal_font(&face, font_name);
277 
278 #if defined VG_FEATURE_FONT_EXTERNAL && (VG_FEATURE_FONT_EXTERNAL == 1)
279  if (FT_ERR(Cannot_Open_Resource) == error) {
280  // try to load an external resource
281  error = __load_external_font(&face, font_name);
282  }
283 #endif // VG_FEATURE_FONT_EXTERNAL
284 
285  if (FT_ERR(Ok) == error) {
286  // Use Unicode
287  FT_Select_Charmap(face, ft_encoding_unicode);
288  MEJ_LOG_INFO_MICROVG("Freetype font loaded: %s\n", (const char *)font_name);
289 
290  SNI_registerResource((void *)face, (SNI_closeFunction) & __dispose_registered_font,
291  &__register_font_description);
292 
293 #if defined VG_FEATURE_FONT_COMPLEX_LAYOUT && (VG_FEATURE_FONT_COMPLEX_LAYOUT == 1)
294  if (JTRUE == complex_layout) {
295  face->face_flags |= FT_FACE_FLAG_COMPLEX_LAYOUT;
296  }
297 #endif // VG_FEATURE_FONT_COMPLEX_LAYOUT
298 
299  ret = (jint)face;
300  } else if (FT_ERR(Cannot_Open_Resource) != error) {
301  MEJ_LOG_ERROR_MICROVG("An error occurred during font loading: 0x%x, refer to fterrdef.h\n", error);
302  } else {
303  MEJ_LOG_ERROR_MICROVG("Resource not found: %s\n", (const char *)font_name);
304  }
305  }
306 
307  LOG_MICROVG_FONT_END(load);
308  return ret;
309 }
310 
311 // See the header file for the function documentation
312 jfloat LLVG_FONT_IMPL_string_width(jchar *text, jint faceHandle, jfloat size, jfloat letterSpacing) {
313  LOG_MICROVG_FONT_START(stringWidth);
314  jfloat ret;
315 
316  if (LLVG_FONT_UNLOADED == faceHandle) {
317  ret = (jfloat)LLVG_RESOURCE_CLOSED;
318  } else {
319  int length = (int)SNI_getArrayLength(text);
320  ret = VG_FREETYPE_string_width(text, length, faceHandle, size, letterSpacing);
321  }
322 
323  LOG_MICROVG_FONT_END(stringWidth);
324  return ret;
325 }
326 
327 // See the header file for the function documentation
328 jfloat LLVG_FONT_IMPL_string_height(jchar *text, jint faceHandle, jfloat size) {
329  LOG_MICROVG_FONT_START(stringHeight);
330  jfloat ret;
331 
332  if (LLVG_FONT_UNLOADED == faceHandle) {
333  ret = (jfloat)LLVG_RESOURCE_CLOSED;
334  } else {
335  FT_Face face = (FT_Face)faceHandle;
336  float scaled_height = 0.f;
337 
338  if (size >= 0) {
339  float scale = GET_SCALE(size, face);
340 
341  FT_Pos horiBearingYTop = 0;
342  FT_Pos horiBearingYBottom = 0;
343 
344  // Layout variables
345  FT_UInt glyph_index; // current glyph index
346  FT_UInt previous_glyph_index = 0; // previous glyph index for kerning
347 
348  int advance_x;
349  int advance_y;
350  int offset_x;
351  int offset_y;
352 
353  int length = (int)SNI_getArrayLength(text);
354  VG_HELPER_layout_configure(faceHandle, text, length);
355 
356  while (VG_HELPER_layout_load_glyph(&glyph_index, &advance_x, &advance_y, &offset_x, &offset_y)) {
357  // At that point the current glyph has been loaded by Freetype
358 
359  FT_Pos yBottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
360  horiBearingYBottom = (0 == previous_glyph_index) ? yBottom : MIN(yBottom, horiBearingYBottom);
361  horiBearingYTop = MAX(face->glyph->metrics.horiBearingY, horiBearingYTop);
362 
363  previous_glyph_index = glyph_index;
364  }
365 
366  scaled_height = scale * (float)(horiBearingYTop - horiBearingYBottom);
367  }
368 
369  ret = scaled_height;
370  }
371 
372  LOG_MICROVG_FONT_END(stringHeight);
373  return ret;
374 }
375 
376 // See the header file for the function documentation
377 jfloat LLVG_FONT_IMPL_get_baseline_position(jint faceHandle, jfloat size) {
378  LOG_MICROVG_FONT_START(baseline);
379  jfloat ret;
380 
381  if (LLVG_FONT_UNLOADED == faceHandle) {
382  ret = (jfloat)LLVG_RESOURCE_CLOSED;
383  } else {
384  FT_Face face = (FT_Face)faceHandle;
385  float advance_y = 0.f;
386 
387  if (size >= 0) {
388  float scale = GET_SCALE(size, face);
389  advance_y = (face->ascender * scale);
390  }
391  ret = advance_y;
392  }
393 
394  LOG_MICROVG_FONT_END(baseline);
395  return ret;
396 }
397 
398 // See the header file for the function documentation
399 jfloat LLVG_FONT_IMPL_get_height(jint faceHandle, jfloat size) {
400  LOG_MICROVG_FONT_START(height);
401  jfloat ret;
402 
403  if (LLVG_FONT_UNLOADED == faceHandle) {
404  ret = (jfloat)LLVG_RESOURCE_CLOSED;
405  } else {
406  FT_Face face = (FT_Face)faceHandle;
407  float scale = GET_SCALE(size, face);
408  ret = face->height * scale;
409  }
410 
411  LOG_MICROVG_FONT_END(height);
412  return ret;
413 }
414 
415 // See the header file for the function documentation
416 void LLVG_FONT_IMPL_dispose(jint faceHandle) {
417  // unregister the resource since the VEE does not need to call it anymore
418  SNI_unregisterResource((void *)faceHandle, (SNI_closeFunction) & __dispose_registered_font);
419  __dispose_font((void *)faceHandle);
420 }
421 
422 // See the header file for the function documentation
423 bool LLVG_FONT_IMPL_has_complex_layouter(void) {
424 #ifdef VG_FEATURE_FONT_COMPLEX_LAYOUT
425  return true;
426 #else
427  return false;
428 #endif
429 }
430 
431 // -----------------------------------------------------------------------------
432 // Internal functions
433 // -----------------------------------------------------------------------------
434 
435 static void __dispose_font(void *faceHandle) {
436  FT_Face face = (FT_Face)faceHandle;
437 
438 #if defined VG_FEATURE_FONT_EXTERNAL && (VG_FEATURE_FONT_EXTERNAL == 1)
439  // FT_Done_Face() sets the stream to NULL: have to save it to close the
440  // external resource
441  FT_Stream stream = face->stream;
442 #endif // VG_FEATURE_FONT_EXTERNAL
443 
444  FT_Done_Face(face);
445 
446 #if defined VG_FEATURE_FONT_EXTERNAL && (VG_FEATURE_FONT_EXTERNAL == 1)
447  // frees the stream when the font is external (Freetype doesn't recommend
448  // to read the flag FT_FACE_FLAG_EXTERNAL_STREAM)
449  if (&__close_external_resource == stream->close) {
450  ft_mem_free(library->memory, stream);
451  }
452 #endif // VG_FEATURE_FONT_EXTERNAL
453 }
454 
455 /*
456  * Only called when killing a KF feature (automatic SNI resource management): have
457  * to wait the end of GPU before closing the image.
458  */
459 static void __dispose_registered_font(void *faceHandle) {
460  // ensure GPU is not working in our font...
462  // ... and close it
463  __dispose_font(faceHandle);
464 }
465 
466 static FT_Error __load_memory_font(FT_Face *face, void *data, int length) {
467  return FT_New_Memory_Face(library, data, length, 0, face);
468 }
469 
470 static FT_Error __load_internal_font(FT_Face *face, jchar *font_name) {
471  SNIX_resource font_resource;
472  FT_Error error;
473 
474  if (0 == SNIX_get_resource(font_name, &font_resource)) {
475  error = __load_memory_font(face, font_resource.data, font_resource.size);
476  } else {
477  error = FT_ERR(Cannot_Open_Resource);
478  }
479  return error;
480 }
481 
482 #if defined VG_FEATURE_FONT_EXTERNAL && (VG_FEATURE_FONT_EXTERNAL == 1)
483 
484 static FT_Error __load_external_font(FT_Face *face, jchar *font_name) {
485  FT_Error error;
486 
487  // try to load an external resource (respect LLEXT_RES_open path naming convention)
488  char const *ext_path = &(((char const *)(font_name))[1]) /* first '/' */;
489  RES_ID resource_id = LLEXT_RES_open(ext_path);
490 
491  if (0 <= resource_id) {
492  int32_t resource_size = LLEXT_RES_available(resource_id);
493 
494  // check if the external resource is byte addressable
495  int32_t base_address = LLEXT_RES_getBaseAddress(resource_id);
496 
497  if (-1 != base_address) {
498  // load the font as "memory" font
499  // cppcheck-suppress [misra-c2012-11.6] base_address is an address
500  error = __load_memory_font(face, (void *)base_address, resource_size);
501 
502  // we can close the resource because we know its address
503  LLEXT_RES_close(resource_id);
504  } else {
505  // load the font as external font
506 
507  FT_Open_Args args;
508  args.flags = FT_OPEN_STREAM;
509  args.driver = NULL;
510  args.stream = (FT_StreamRec *)ft_mem_alloc(library->memory, sizeof(FT_StreamRec), &error);
511 
512  if (FT_ERR(Ok) == error) {
513  args.stream->base = NULL;
514  args.stream->size = resource_size;
515  args.stream->pos = 0;
516  args.stream->descriptor.value = resource_id;
517  // args.stream->pathname = NULL; // not used
518  args.stream->read = &__read_external_resource;
519  args.stream->close = &__close_external_resource;
520 
521  error = FT_Open_Face(library, &args, 0, face);
522  } else {
523  error = FT_ERR(Out_Of_Memory);
524  }
525  }
526  } else {
527  error = FT_ERR(Cannot_Open_Resource);
528  }
529 
530  return error;
531 }
532 
533 static unsigned long __read_external_resource(FT_Stream stream, unsigned long offset, unsigned char *buffer,
534  unsigned long count) {
535  int32_t size = count;
536  RES_ID resource_id = (RES_ID)(stream->descriptor.value);
537  LLEXT_RES_seek(resource_id, offset);
538  LLEXT_RES_read(resource_id, buffer, &size);
539  return size;
540 }
541 
542 static void __close_external_resource(FT_Stream stream) {
543  RES_ID resource_id = (RES_ID)(stream->descriptor.value);
544  LLEXT_RES_close(resource_id);
545 }
546 
547 #endif // VG_FEATURE_FONT_EXTERNAL
548 
549 static void __register_font_description(void *resource, char *buffer, uint32_t bufferLength) {
550  (void)resource;
551  REGISTERDESC("VectorFont", buffer, bufferLength);
552 }
553 
554 // -----------------------------------------------------------------------------
555 // TestResource functions
556 // -----------------------------------------------------------------------------
557 
558 jlong Java_com_microej_microvg_test_TestResource_getSNICloseFunctionForFont(void) {
559  return (jlong) & __dispose_registered_font;
560 }
561 
562 // cppcheck-suppress [misra-c2012-3.2]
563 #endif \
564  // defined VG_FEATURE_FONT && \
565  // (defined VG_FEATURE_FONT_FREETYPE_VECTOR || defined VG_FEATURE_FONT_FREETYPE_BITMAP) && \
566  // (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR || VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
567 
568 // -----------------------------------------------------------------------------
569 // EOF
570 // -----------------------------------------------------------------------------
MicroEJ MicroVG library low level API: enable some features according to the hardware capacities.
MicroEJ MicroVG library low level API: implementation over FreeType.
MicroEJ MicroVG library low level API: helper to implement library natives methods.
#define REGISTERDESC(desc, buf, buf_len)
Registers the description of a SNI close function.
Definition: vg_helper.h:81
void LLUI_DISPLAY_waitAsynchronousDrawingEnd(void)
Waits until the end of current asynchronous drawing.
Definition: vg_helper.c:150
#define FT_FACE_FLAG_COMPLEX_LAYOUT
Freetype supplementary flag for complex layout Uses a free bit in freetype face flags to convey the c...
Definition: vg_helper.h:66