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