microvg  7.0.0
microvg
LLVG_FONT_impl_freetype.c
Go to the documentation of this file.
1 /*
2  * C
3  *
4  * Copyright 2020-2024 MicroEJ Corp. All rights reserved.
5  * Use of this source code is governed by a BSD-style license that can be found with this software.
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)
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 Opens a font that has been loaded into memory.
90  *
91  * @see FT_New_Memory_Face in freetype.h
92  *
93  * @param[in] face: a handle to a new face object.
94  * @param[in] data: a pointer to the beginning of the font data.
95  * @param[in] length: the size of the memory chunk used by the font data.
96  *
97  * @return FreeType error code.
98  */
99 static FT_Error __load_memory_font(FT_Face *face, void *data, int length);
100 
101 /*
102  * @brief Opens a font that has been compiled with the application.
103  *
104  * @see FT_New_Memory_Face in freetype.h
105  *
106  * @param[in] face: a handle to a new face object.
107  * @param[in] font_name: a path to the font file.
108  *
109  * @return FreeType error code.
110  */
111 static FT_Error __load_internal_font(FT_Face *face, jchar *font_name);
112 
113 /*
114  * @brief Gets the description of the given native resource: a vector font.
115  * @see SNI_getDescriptionFunction
116  */
117 static void __register_font_description(void *resource, char *buffer, uint32_t bufferLength);
118 
119 #if defined(VG_FEATURE_FONT_EXTERNAL)
120 
121 /*
122  * @brief Opens a font that has not been compiled with the application.
123  *
124  * @see FT_Open_Face in freetype.h
125  *
126  * @param[in] face: a handle to a new face object.
127  * @param[in] font_name: a path to the font file.
128  *
129  * @return FreeType error code.
130  */
131 static FT_Error __load_external_font(FT_Face *face, jchar *font_name);
132 
133 /*
134  * @brief Reads a chunk from an external resource.
135  *
136  * @see FT_Stream_IoFunc in ftsystem.h
137  *
138  * @param[in] stream: a handle to the source stream.
139  * @param[in] offset: the offset of read in stream.
140  * @param[in] buffer: the address of the read buffer.
141  * @param[in] count: the number of bytes to read from the stream.
142  *
143  * @return the number of bytes effectively read by the stream.
144  */
145 static unsigned long __read_external_resource(FT_Stream stream, unsigned long offset, unsigned char *buffer,
146  unsigned long count);
147 
148 /*
149  * @brief Closes a given input stream.
150  *
151  * @see FT_Stream_IoFunc in ftsystem.h
152  *
153  * @param[in] stream: a handle to the target stream.
154  */
155 static void __close_external_resource(FT_Stream stream);
156 
157 #endif // VG_FEATURE_FONT_EXTERNAL
158 
159 // -----------------------------------------------------------------------------
160 // Global Variables
161 // -----------------------------------------------------------------------------
162 
163 FT_Library library;
164 FT_Renderer renderer;
165 
166 #if defined VG_FEATURE_FONT && defined VG_FEATURE_FONT_FREETYPE_VECTOR && \
167  (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR)
168 const char *renderer_name = "microvg";
169 #endif
170 #if defined VG_FEATURE_FONT && defined VG_FEATURE_FONT_FREETYPE_BITMAP && \
171  (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
172 const char *renderer_name = "smooth";
173 #endif
174 
175 // -----------------------------------------------------------------------------
176 // vg_freetype.h functions
177 // -----------------------------------------------------------------------------
178 
179 // See the header file for the function documentation
180 void VG_FREETYPE_initialize(void) {
181  FT_Error error = FT_Init_FreeType(&library);
182  if (FT_ERR(Ok) == error) {
183  renderer = FT_RENDERER(FT_Get_Module(library, renderer_name));
184  if (0 != renderer) {
185  MEJ_LOG_INFO_MICROVG("Freetype renderer: %s\n", FT_MODULE_CLASS(renderer)->module_name);
186  } else {
187  MEJ_LOG_ERROR_MICROVG("No renderer found with name %s\n", renderer_name);
188  }
189  } else {
190  MEJ_LOG_ERROR_MICROVG("Internal freetype error initializing library, ID = %d\n", error);
191  }
192 }
193 
194 // See the header file for the function documentation
195 jfloat VG_FREETYPE_string_width(jchar *text, jint length, jint face_handle, jfloat size, jfloat letter_spacing) {
196  float scaled_width = 0.f;
197 
198  if (size >= 0) {
199  FT_Face face = (FT_Face)face_handle;
200  float scale = GET_SCALE(size, face);
201 
202  int nb_chars = 0;
203  long unscaled_width = 0;
204 
205  // Layout variables
206  int glyph_index; // current glyph index
207  int previous_glyph_index = 0; // previous glyph index for kerning
208 
209  int advance_x;
210  int previous_advance_x;
211  int previous_offset_x;
212  int advance_y;
213  int offset_x;
214  int offset_y;
215 
216  VG_HELPER_layout_configure(face_handle, text, length);
217 
218  while (VG_HELPER_layout_load_glyph(&glyph_index, &advance_x, &advance_y, &offset_x, &offset_y)) {
219  // At that point the current glyph has been loaded by Freetype
220  if (0 == previous_glyph_index) {
221  // first glyph: remove the first blank line
222  if (0 != face->glyph->metrics.width) {
223  unscaled_width -= face->glyph->metrics.horiBearingX;
224  } else {
225  unscaled_width -= face->glyph->advance.x;
226  }
227  }
228 
229  unscaled_width += advance_x;
230  previous_glyph_index = glyph_index;
231  nb_chars++;
232  // Last call to VG_HELPER_layout_load_glyph clear advance_x and offset_x.
233  // We need to keep them for last glyph measurement
234  previous_advance_x = advance_x;
235  previous_offset_x = offset_x;
236  }
237 
238  // last glyph: remove the last blank line
239  if (0 != face->glyph->metrics.width) {
240  unscaled_width -= previous_advance_x;
241  unscaled_width += face->glyph->metrics.horiBearingX; // glyph's left blank line
242  unscaled_width += face->glyph->metrics.width; // glyph's width
243  unscaled_width += previous_offset_x; // glyph's offset_x
244  } else {
245  if (0 != unscaled_width) {
246  unscaled_width -= previous_advance_x;
247  }
248  }
249  scaled_width = ((scale * (float)unscaled_width) + (((float)nb_chars - 1) * letter_spacing));
250  }
251 
252  return scaled_width;
253 }
254 
255 // -----------------------------------------------------------------------------
256 // LLVG_FONT_impl.h functions
257 // -----------------------------------------------------------------------------
258 
259 // See the header file for the function documentation
260 jint LLVG_FONT_IMPL_load_font(jchar *font_name, jboolean complex_layout) {
261  LOG_MICROVG_FONT_START(load);
262 
263  FT_Face face = 0;
264  jint ret = LLVG_FONT_UNLOADED;
265 
266  // Check that complex layouter is available for complex text layout
267  if (complex_layout && !LLVG_FONT_IMPL_has_complex_layouter()) {
268  ret = LLVG_FONT_UNLOADED;
269  } else {
270  // try to load an internal resource
271  FT_Error error = __load_internal_font(&face, font_name);
272 
273 #if defined(VG_FEATURE_FONT_EXTERNAL)
274  if (FT_ERR(Cannot_Open_Resource) == error) {
275  // try to load an external resource
276  error = __load_external_font(&face, font_name);
277  }
278 #endif // VG_FEATURE_FONT_EXTERNAL
279 
280  if (FT_ERR(Ok) == error) {
281  // Use Unicode
282  FT_Select_Charmap(face, ft_encoding_unicode);
283  MEJ_LOG_INFO_MICROVG("Freetype font loaded: %s\n", (const char *)font_name);
284 
285  SNI_registerResource((void *)face, (SNI_closeFunction) & _dispose_registered_font,
286  &__register_font_description);
287 
288 #if defined(VG_FEATURE_FONT_COMPLEX_LAYOUT)
289  if (JTRUE == complex_layout) {
290  face->face_flags |= FT_FACE_FLAG_COMPLEX_LAYOUT;
291  }
292 #endif // VG_FEATURE_FONT_COMPLEX_LAYOUT
293 
294  ret = (jint)face;
295  } else if (FT_ERR(Cannot_Open_Resource) != error) {
296  MEJ_LOG_ERROR_MICROVG("An error occurred during font loading: 0x%x, refer to fterrdef.h\n", error);
297  } else {
298  MEJ_LOG_ERROR_MICROVG("Resource not found: %s\n", (const char *)font_name);
299  }
300  }
301 
302  LOG_MICROVG_FONT_END(load);
303  return ret;
304 }
305 
306 // See the header file for the function documentation
307 jfloat LLVG_FONT_IMPL_string_width(jchar *text, jint faceHandle, jfloat size, jfloat letterSpacing) {
308  LOG_MICROVG_FONT_START(stringWidth);
309  jfloat ret;
310 
311  if (LLVG_FONT_UNLOADED == faceHandle) {
312  ret = (jfloat)LLVG_RESOURCE_CLOSED;
313  } else {
314  int length = (int)SNI_getArrayLength(text);
315  ret = VG_FREETYPE_string_width(text, length, faceHandle, size, letterSpacing);
316  }
317 
318  LOG_MICROVG_FONT_END(stringWidth);
319  return ret;
320 }
321 
322 // See the header file for the function documentation
323 jfloat LLVG_FONT_IMPL_string_height(jchar *text, jint faceHandle, jfloat size) {
324  LOG_MICROVG_FONT_START(stringHeight);
325  jfloat ret;
326 
327  if (LLVG_FONT_UNLOADED == faceHandle) {
328  ret = (jfloat)LLVG_RESOURCE_CLOSED;
329  } else {
330  FT_Face face = (FT_Face)faceHandle;
331  float scaled_height = 0.f;
332 
333  if (size >= 0) {
334  float scale = GET_SCALE(size, face);
335 
336  FT_Pos horiBearingYTop = 0;
337  FT_Pos horiBearingYBottom = 0;
338 
339  // Layout variables
340  int glyph_index; // current glyph index
341  int previous_glyph_index = 0; // previous glyph index for kerning
342 
343  int advance_x;
344  int advance_y;
345  int offset_x;
346  int offset_y;
347 
348  int length = (int)SNI_getArrayLength(text);
349  VG_HELPER_layout_configure(faceHandle, text, length);
350 
351  while (VG_HELPER_layout_load_glyph(&glyph_index, &advance_x, &advance_y, &offset_x, &offset_y)) {
352  // At that point the current glyph has been loaded by Freetype
353 
354  FT_Pos yBottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
355  horiBearingYBottom = (0 == previous_glyph_index) ? yBottom : MIN(yBottom, horiBearingYBottom);
356  horiBearingYTop = MAX(face->glyph->metrics.horiBearingY, horiBearingYTop);
357 
358  previous_glyph_index = glyph_index;
359  }
360 
361  scaled_height = scale * (float)(horiBearingYTop - horiBearingYBottom);
362  }
363 
364  ret = scaled_height;
365  }
366 
367  LOG_MICROVG_FONT_END(stringHeight);
368  return ret;
369 }
370 
371 // See the header file for the function documentation
372 jfloat LLVG_FONT_IMPL_get_baseline_position(jint faceHandle, jfloat size) {
373  LOG_MICROVG_FONT_START(baseline);
374  jfloat ret;
375 
376  if (LLVG_FONT_UNLOADED == faceHandle) {
377  ret = (jfloat)LLVG_RESOURCE_CLOSED;
378  } else {
379  FT_Face face = (FT_Face)faceHandle;
380  float advance_y = 0.f;
381 
382  if (size >= 0) {
383  float scale = GET_SCALE(size, face);
384  advance_y = (face->ascender * scale);
385  }
386  ret = advance_y;
387  }
388 
389  LOG_MICROVG_FONT_END(baseline);
390  return ret;
391 }
392 
393 // See the header file for the function documentation
394 jfloat LLVG_FONT_IMPL_get_height(jint faceHandle, jfloat size) {
395  LOG_MICROVG_FONT_START(height);
396  jfloat ret;
397 
398  if (LLVG_FONT_UNLOADED == faceHandle) {
399  ret = (jfloat)LLVG_RESOURCE_CLOSED;
400  } else {
401  FT_Face face = (FT_Face)faceHandle;
402  float scale = GET_SCALE(size, face);
403  ret = face->height * scale;
404  }
405 
406  LOG_MICROVG_FONT_END(height);
407  return ret;
408 }
409 
410 // See the header file for the function documentation
411 void LLVG_FONT_IMPL_dispose(jint faceHandle) {
412  _dispose_registered_font((void *)faceHandle);
413 }
414 
415 // See the header file for the function documentation
416 bool LLVG_FONT_IMPL_has_complex_layouter(void) {
417 #ifdef VG_FEATURE_FONT_COMPLEX_LAYOUT
418  return true;
419 #else
420  return false;
421 #endif
422 }
423 
424 // -----------------------------------------------------------------------------
425 // Internal functions
426 // -----------------------------------------------------------------------------
427 
428 static void _dispose_registered_font(void *faceHandle) {
429  FT_Face face = (FT_Face)faceHandle;
430 
431  // unregister the resource since the VEE does not need to call it anymore
432  SNI_unregisterResource((void *)face, (SNI_closeFunction) & _dispose_registered_font);
433 
434 #if defined(VG_FEATURE_FONT_EXTERNAL)
435  // FT_Done_Face() sets the stream to NULL: have to save it to close the
436  // external resource
437  FT_Stream stream = face->stream;
438 #endif // VG_FEATURE_FONT_EXTERNAL
439 
440  FT_Done_Face(face);
441 
442 #if defined(VG_FEATURE_FONT_EXTERNAL)
443  // frees the stream when the font is external (Freetype doesn't recommend
444  // to read the flag FT_FACE_FLAG_EXTERNAL_STREAM)
445  if (&__close_external_resource == stream->close) {
446  ft_mem_free(library->memory, stream);
447  }
448 #endif // VG_FEATURE_FONT_EXTERNAL
449 }
450 
451 static FT_Error __load_memory_font(FT_Face *face, void *data, int length) {
452  return FT_New_Memory_Face(library, data, length, 0, face);
453 }
454 
455 static FT_Error __load_internal_font(FT_Face *face, jchar *font_name) {
456  SNIX_resource font_resource;
457  FT_Error error;
458 
459  if (0 == SNIX_get_resource(font_name, &font_resource)) {
460  error = __load_memory_font(face, font_resource.data, font_resource.size);
461  } else {
462  error = FT_ERR(Cannot_Open_Resource);
463  }
464  return error;
465 }
466 
467 #if defined(VG_FEATURE_FONT_EXTERNAL)
468 
469 static FT_Error __load_external_font(FT_Face *face, jchar *font_name) {
470  FT_Error error;
471 
472  // try to load an external resource (respect LLEXT_RES_open path naming convention)
473  char const *ext_path = &(((char const *)(font_name))[1]) /* first '/' */;
474  RES_ID resource_id = LLEXT_RES_open(ext_path);
475 
476  if (0 <= resource_id) {
477  int32_t resource_size = LLEXT_RES_available(resource_id);
478 
479  // check if the external resource is byte addressable
480  int32_t base_address = LLEXT_RES_getBaseAddress(resource_id);
481 
482  if (-1 != base_address) {
483  // load the font as "memory" font
484  // cppcheck-suppress [misra-c2012-11.6] base_address is an address
485  error = __load_memory_font(face, (void *)base_address, resource_size);
486 
487  // we can close the resource because we know its address
488  LLEXT_RES_close(resource_id);
489  } else {
490  // load the font as external font
491 
492  FT_Open_Args args;
493  args.flags = FT_OPEN_STREAM;
494  args.driver = NULL;
495  args.stream = (FT_StreamRec *)ft_mem_alloc(library->memory, sizeof(FT_StreamRec), &error);
496 
497  if (FT_ERR(Ok) == error) {
498  args.stream->base = NULL;
499  args.stream->size = resource_size;
500  args.stream->pos = 0;
501  args.stream->descriptor.value = resource_id;
502  // args.stream->pathname = NULL; // not used
503  args.stream->read = &__read_external_resource;
504  args.stream->close = &__close_external_resource;
505 
506  error = FT_Open_Face(library, &args, 0, face);
507  } else {
508  error = FT_ERR(Out_Of_Memory);
509  }
510  }
511  } else {
512  error = FT_ERR(Cannot_Open_Resource);
513  }
514 
515  return error;
516 }
517 
518 static unsigned long __read_external_resource(FT_Stream stream, unsigned long offset, unsigned char *buffer,
519  unsigned long count) {
520  int32_t size = count;
521  RES_ID resource_id = (RES_ID)(stream->descriptor.value);
522  LLEXT_RES_seek(resource_id, offset);
523  LLEXT_RES_read(resource_id, buffer, &size);
524  return size;
525 }
526 
527 static void __close_external_resource(FT_Stream stream) {
528  RES_ID resource_id = (RES_ID)(stream->descriptor.value);
529  LLEXT_RES_close(resource_id);
530 }
531 
532 #endif // VG_FEATURE_FONT_EXTERNAL
533 
534 static void __register_font_description(void *resource, char *buffer, uint32_t bufferLength) {
535  (void)resource;
536  const char descEF[] = "Vector Font";
537  if (bufferLength >= sizeof(descEF)) {
538  (void)memcpy(buffer, descEF, sizeof(descEF));
539  }
540 }
541 
542 // cppcheck-suppress [misra-c2012-3.2]
543 #endif \
544  // defined VG_FEATURE_FONT && \
545  // (defined VG_FEATURE_FONT_FREETYPE_VECTOR || defined VG_FEATURE_FONT_FREETYPE_BITMAP) && \
546  // (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR || VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
547 // -----------------------------------------------------------------------------
548 // EOF
549 // -----------------------------------------------------------------------------
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 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