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