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