feat(freetype): support bold and italic (#2824)

* feat(freetype):support bold and italic

* fix(format): replace code-format.sh with code-format.py

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
guoweilkd
2021-11-25 03:19:09 +08:00
committed by GitHub
parent 6791522588
commit 81f7d50c45
9 changed files with 227 additions and 20 deletions

View File

@@ -18,7 +18,7 @@ jobs:
sudo apt-get update -y -qq sudo apt-get update -y -qq
sudo apt-get install astyle sudo apt-get install astyle
- name: Format code - name: Format code
run: ./code-format.sh run: python code-format.py
working-directory: scripts working-directory: scripts
- name: Check that repository is clean - name: Check that repository is clean
run: git diff --exit-code || (echo "Please apply the preceding diff to your code or run scripts/code-format.sh"; false) run: git diff --exit-code || (echo "Please apply the preceding diff to your code or run scripts/code-format.sh"; false)

View File

@@ -1,6 +1,6 @@
```eval_rst ```eval_rst
.. include:: /header.rst .. include:: /header.rst
:github_url: |github_link_base|/libs/bmp.md :github_url: |github_link_base|/libs/freetype.md
``` ```
# FreeType support # FreeType support
@@ -19,11 +19,33 @@ Interface to [FreeType](https://www.freetype.org/) to generate font bitmaps run
Enable `LV_USE_FREETYPE` in `lv_conf.h`. Enable `LV_USE_FREETYPE` in `lv_conf.h`.
See the examples below. To cache the glyphs from the opened fonts set `LV_FREETYPE_CACHE_SIZE >= 0` and then use the following macros for detailed configuration:
1. `LV_FREETYPE_CACHE_SIZE`:maximum memory(bytes) used to cache font bitmap, outline, character maps, etc. 0 means use the system default value, less than 0 means disable cache.Note: that this value does not account for managed FT_Face and FT_Size objects.
1. `LV_FREETYPE_CACHE_FT_FACES`:maximum number of opened FT_Face objects managed by this cache instance.0 means use the system default value.Only useful when LV_FREETYPE_CACHE_SIZE >= 0.
1. `LV_FREETYPE_CACHE_FT_SIZES`:maximum number of opened FT_Size objects managed by this cache instance. 0 means use the system default value.Only useful when LV_FREETYPE_CACHE_SIZE >= 0.
When you are sure that all the used fonts size will not be greater than 256, you can enable `LV_FREETYPE_SBIT_CACHE`, which is much more memory efficient for small bitmaps.
You can use `lv_ft_font_init()` to create FreeType fonts. It returns `true` to indicate success, at the same time, the `font` member of `lv_ft_info_t` will be filled with a pointer to an lvgl font, and you can use it like any lvgl font.
Font style supports bold and italic, you can use the following macro to set:
1. `FT_FONT_STYLE_NORMAL`:default style.
1. `FT_FONT_STYLE_ITALIC`:Italic style
1. `FT_FONT_STYLE_BOLD`:bold style
They can be combined.eg:`FT_FONT_STYLE_BOLD | FT_FONT_STYLE_ITALIC`.
Note that, the FreeType extension doesn't use LVGL's file system. Note that, the FreeType extension doesn't use LVGL's file system.
You can simply pass the path to the font as usual on your operating system or platform. You can simply pass the path to the font as usual on your operating system or platform.
## Example
```eval_rst
.. include:: ../../examples/libs/freetype/index.rst
```
## Learn more ## Learn more
- FreeType [tutorial](https://www.freetype.org/freetype2/docs/tutorial/step1.html) - FreeType [tutorial](https://www.freetype.org/freetype2/docs/tutorial/step1.html)
- LVGL's [font interface](https://docs.lvgl.io/v7/en/html/overview/font.html#add-a-new-font-engine) - LVGL's [font interface](https://docs.lvgl.io/v7/en/html/overview/font.html#add-a-new-font-engine)

View File

@@ -12,7 +12,10 @@ void lv_example_freetype_1(void)
info.name = "./lvgl/examples/libs/freetype/arial.ttf"; info.name = "./lvgl/examples/libs/freetype/arial.ttf";
info.weight = 24; info.weight = 24;
info.style = FT_FONT_STYLE_NORMAL; info.style = FT_FONT_STYLE_NORMAL;
lv_ft_font_init(&info); info.mem = NULL;
if(!lv_ft_font_init(&info)) {
LV_LOG_ERROR("create failed.");
}
/*Create style with the new font*/ /*Create style with the new font*/
static lv_style_t style; static lv_style_t style;

View File

@@ -576,6 +576,16 @@
#if LV_USE_FREETYPE #if LV_USE_FREETYPE
/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/ /*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/
#define LV_FREETYPE_CACHE_SIZE (16 * 1024) #define LV_FREETYPE_CACHE_SIZE (16 * 1024)
#if LV_FREETYPE_CACHE_SIZE >= 0
/* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */
/* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */
/* if font size >= 256, must be configured as image cache */
#define LV_FREETYPE_SBIT_CACHE 0
/* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */
/* (0:use system defaults) */
#define LV_FREETYPE_CACHE_FT_FACES 0
#define LV_FREETYPE_CACHE_FT_SIZES 0
#endif
#endif #endif
/*Rlottie library*/ /*Rlottie library*/

5
scripts/code-format.py Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env python3
import os
os.system('astyle --options=code-format.cfg "../src/*.c,*.h"')

View File

@@ -1 +0,0 @@
astyle --options=code-format.cfg "../src/*.c,*.h"

View File

@@ -14,6 +14,8 @@
#include FT_GLYPH_H #include FT_GLYPH_H
#include FT_CACHE_H #include FT_CACHE_H
#include FT_SIZES_H #include FT_SIZES_H
#include FT_IMAGE_H
#include FT_OUTLINE_H
/********************* /*********************
* DEFINES * DEFINES
@@ -67,8 +69,16 @@ static FT_Library library;
#if LV_FREETYPE_CACHE_SIZE >= 0 #if LV_FREETYPE_CACHE_SIZE >= 0
static FTC_Manager cache_manager; static FTC_Manager cache_manager;
static FTC_CMapCache cmap_cache; static FTC_CMapCache cmap_cache;
static FT_Face current_face = NULL;
#if LV_FREETYPE_SBIT_CACHE
static FTC_SBitCache sbit_cache; static FTC_SBitCache sbit_cache;
static FTC_SBit sbit; static FTC_SBit sbit;
#else
static FTC_ImageCache image_cache;
static FT_Glyph image_glyph;
#endif
#else #else
static lv_faces_control_t face_control; static lv_faces_control_t face_control;
#endif #endif
@@ -104,11 +114,19 @@ bool lv_freetype_init(uint16_t max_faces, uint16_t max_sizes, uint32_t max_bytes
goto Fail; goto Fail;
} }
#if LV_FREETYPE_SBIT_CACHE
error = FTC_SBitCache_New(cache_manager, &sbit_cache); error = FTC_SBitCache_New(cache_manager, &sbit_cache);
if(error) { if(error) {
LV_LOG_ERROR("Failed to open sbit cache"); LV_LOG_ERROR("Failed to open sbit cache");
goto Fail; goto Fail;
} }
#else
error = FTC_ImageCache_New(cache_manager, &image_cache);
if(error) {
LV_LOG_ERROR("Failed to open image cache");
goto Fail;
}
#endif
return true; return true;
Fail: Fail:
@@ -176,6 +194,36 @@ static FT_Error font_face_requester(FTC_FaceID face_id,
return FT_Err_Ok; return FT_Err_Ok;
} }
static bool get_bold_glyph(const lv_font_t * font, FT_Face face,
FT_UInt glyph_index, lv_font_glyph_dsc_t * dsc_out)
{
if(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT)) {
return false;
}
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
if(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
if(dsc->style & FT_FONT_STYLE_BOLD) {
int strength = 1 << 6;
FT_Outline_Embolden(&face->glyph->outline, strength);
}
}
if(FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) {
return false;
}
dsc_out->adv_w = (face->glyph->metrics.horiAdvance >> 6);
dsc_out->box_h = face->glyph->bitmap.rows; /*Height of the bitmap in [px]*/
dsc_out->box_w = face->glyph->bitmap.width; /*Width of the bitmap in [px]*/
dsc_out->ofs_x = face->glyph->bitmap_left; /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = face->glyph->bitmap_top -
face->glyph->bitmap.rows; /*Y offset of the bitmap measured from the as line*/
dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/
return true;
}
static bool get_glyph_dsc_cb_cache(const lv_font_t * font, static bool get_glyph_dsc_cb_cache(const lv_font_t * font,
lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next) lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next)
{ {
@@ -192,20 +240,51 @@ static bool get_glyph_dsc_cb_cache(const lv_font_t * font,
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc); lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
FT_Face face;
FTC_ImageTypeRec desc_sbit_type;
FTC_FaceID face_id = (FTC_FaceID)dsc->face_id; FTC_FaceID face_id = (FTC_FaceID)dsc->face_id;
FTC_Manager_LookupFace(cache_manager, face_id, &face); FT_Size face_size;
struct FTC_ScalerRec_ scaler;
scaler.face_id = face_id;
scaler.width = dsc->height;
scaler.height = dsc->height;
scaler.pixel = 1;
if(FTC_Manager_LookupSize(cache_manager, &scaler, &face_size) != 0) {
return false;
}
desc_sbit_type.face_id = face_id; FT_Face face = face_size->face;
desc_sbit_type.flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
desc_sbit_type.height = dsc->height;
desc_sbit_type.width = dsc->height;
FT_UInt charmap_index = FT_Get_Charmap_Index(face->charmap); FT_UInt charmap_index = FT_Get_Charmap_Index(face->charmap);
FT_UInt glyph_index = FTC_CMapCache_Lookup(cmap_cache, face_id, charmap_index, unicode_letter); FT_UInt glyph_index = FTC_CMapCache_Lookup(cmap_cache, face_id, charmap_index, unicode_letter);
FT_Error error = FTC_SBitCache_Lookup(sbit_cache, &desc_sbit_type, glyph_index, &sbit, NULL); dsc_out->is_placeholder = glyph_index == 0;
if(dsc->style & FT_FONT_STYLE_ITALIC) {
FT_Matrix italic_matrix;
italic_matrix.xx = 1 << 16;
italic_matrix.xy = 0x5800;
italic_matrix.yx = 0;
italic_matrix.yy = 1 << 16;
FT_Set_Transform(face, &italic_matrix, NULL);
}
if(dsc->style & FT_FONT_STYLE_BOLD) {
current_face = face;
if(!get_bold_glyph(font, face, glyph_index, dsc_out)) {
current_face = NULL;
return false;
}
goto end;
}
FTC_ImageTypeRec desc_type;
desc_type.face_id = face_id;
desc_type.flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
desc_type.height = dsc->height;
desc_type.width = dsc->height;
#if LV_FREETYPE_SBIT_CACHE
FT_Error error = FTC_SBitCache_Lookup(sbit_cache, &desc_type, glyph_index, &sbit, NULL);
if(error) { if(error) {
LV_LOG_ERROR("SBitCache_Lookup error"); LV_LOG_ERROR("SBitCache_Lookup error");
return false;
} }
dsc_out->adv_w = sbit->xadvance; dsc_out->adv_w = sbit->xadvance;
@@ -214,16 +293,53 @@ static bool get_glyph_dsc_cb_cache(const lv_font_t * font,
dsc_out->ofs_x = sbit->left; /*X offset of the bitmap in [pf]*/ dsc_out->ofs_x = sbit->left; /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = sbit->top - sbit->height; /*Y offset of the bitmap measured from the as line*/ dsc_out->ofs_y = sbit->top - sbit->height; /*Y offset of the bitmap measured from the as line*/
dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/ dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/
dsc_out->is_placeholder = glyph_index == 0; #else
FT_Error error = FTC_ImageCache_Lookup(image_cache, &desc_type, glyph_index, &image_glyph, NULL);
if(error) {
LV_LOG_ERROR("ImageCache_Lookup error");
return false;
}
if(image_glyph->format != FT_GLYPH_FORMAT_BITMAP) {
LV_LOG_ERROR("Glyph_To_Bitmap error");
return false;
}
FT_BitmapGlyph glyph_bitmap = (FT_BitmapGlyph)image_glyph;
dsc_out->adv_w = (glyph_bitmap->root.advance.x >> 16);
dsc_out->box_h = glyph_bitmap->bitmap.rows; /*Height of the bitmap in [px]*/
dsc_out->box_w = glyph_bitmap->bitmap.width; /*Width of the bitmap in [px]*/
dsc_out->ofs_x = glyph_bitmap->left; /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = glyph_bitmap->top -
glyph_bitmap->bitmap.rows; /*Y offset of the bitmap measured from the as line*/
dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/
#endif
end:
if((dsc->style & FT_FONT_STYLE_ITALIC) && (unicode_letter_next == '\0')) {
dsc_out->adv_w = dsc_out->box_w + dsc_out->ofs_x;
}
return true; return true;
} }
static const uint8_t * get_glyph_bitmap_cb_cache(const lv_font_t * font, uint32_t unicode_letter) static const uint8_t * get_glyph_bitmap_cb_cache(const lv_font_t * font, uint32_t unicode_letter)
{ {
LV_UNUSED(font);
LV_UNUSED(unicode_letter); LV_UNUSED(unicode_letter);
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
if(dsc->style & FT_FONT_STYLE_BOLD) {
if(current_face && current_face->glyph->format == FT_GLYPH_FORMAT_BITMAP) {
return (const uint8_t *)(current_face->glyph->bitmap.buffer);
}
return NULL;
}
#if LV_FREETYPE_SBIT_CACHE
return (const uint8_t *)sbit->buffer; return (const uint8_t *)sbit->buffer;
#else
FT_BitmapGlyph glyph_bitmap = (FT_BitmapGlyph)image_glyph;
return (const uint8_t *)glyph_bitmap->bitmap.buffer;
#endif
} }
static bool lv_ft_font_init_cache(lv_ft_info_t * info) static bool lv_ft_font_init_cache(lv_ft_info_t * info)
@@ -297,6 +413,7 @@ void lv_ft_font_destroy_cache(lv_font_t * font)
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc); lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
if(dsc) { if(dsc) {
FTC_Manager_RemoveFaceID(cache_manager, (FTC_FaceID)dsc->face_id);
lv_mem_free(dsc->face_id); lv_mem_free(dsc->face_id);
lv_mem_free(dsc->font); lv_mem_free(dsc->font);
lv_mem_free(dsc); lv_mem_free(dsc);
@@ -380,6 +497,22 @@ static bool get_glyph_dsc_cb_nocache(const lv_font_t * font,
return false; return false;
} }
if(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
if(dsc->style & FT_FONT_STYLE_BOLD) {
int strength = 1 << 6;
FT_Outline_Embolden(&face->glyph->outline, strength);
}
if(dsc->style & FT_FONT_STYLE_ITALIC) {
FT_Matrix italic_matrix;
italic_matrix.xx = 1 << 16;
italic_matrix.xy = 0x5800;
italic_matrix.yx = 0;
italic_matrix.yy = 1 << 16;
FT_Outline_Transform(&face->glyph->outline, &italic_matrix);
}
}
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
if(error) { if(error) {
return false; return false;
@@ -393,6 +526,10 @@ static bool get_glyph_dsc_cb_nocache(const lv_font_t * font,
face->glyph->bitmap.rows; /*Y offset of the bitmap measured from the as line*/ face->glyph->bitmap.rows; /*Y offset of the bitmap measured from the as line*/
dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/ dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/
if((dsc->style & FT_FONT_STYLE_ITALIC) && (unicode_letter_next == '\0')) {
dsc_out->adv_w = dsc_out->box_w + dsc_out->ofs_x;
}
return true; return true;
} }

View File

@@ -71,9 +71,12 @@ void lv_extra_init(void)
#endif #endif
#if LV_USE_FREETYPE #if LV_USE_FREETYPE
/*Init freetype library /*Init freetype library*/
*Cache max 64 faces and 1 size*/ # if LV_FREETYPE_CACHE_SIZE >= 0
lv_freetype_init(0, 0, LV_FREETYPE_CACHE_SIZE); lv_freetype_init(LV_FREETYPE_CACHE_FT_FACES, LV_FREETYPE_CACHE_FT_SIZES, LV_FREETYPE_CACHE_SIZE);
# else
lv_freetype_init(0, 0, 0);
# endif
#endif #endif
#if LV_USE_FFMPEG #if LV_USE_FFMPEG

View File

@@ -1867,6 +1867,34 @@
#define LV_FREETYPE_CACHE_SIZE (16 * 1024) #define LV_FREETYPE_CACHE_SIZE (16 * 1024)
#endif #endif
#endif #endif
#if LV_FREETYPE_CACHE_SIZE >= 0
/* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */
/* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */
/* if font size >= 256, must be configured as image cache */
#ifndef LV_FREETYPE_SBIT_CACHE
#ifdef CONFIG_LV_FREETYPE_SBIT_CACHE
#define LV_FREETYPE_SBIT_CACHE CONFIG_LV_FREETYPE_SBIT_CACHE
#else
#define LV_FREETYPE_SBIT_CACHE 0
#endif
#endif
/* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */
/* (0:use system defaults) */
#ifndef LV_FREETYPE_CACHE_FT_FACES
#ifdef CONFIG_LV_FREETYPE_CACHE_FT_FACES
#define LV_FREETYPE_CACHE_FT_FACES CONFIG_LV_FREETYPE_CACHE_FT_FACES
#else
#define LV_FREETYPE_CACHE_FT_FACES 0
#endif
#endif
#ifndef LV_FREETYPE_CACHE_FT_SIZES
#ifdef CONFIG_LV_FREETYPE_CACHE_FT_SIZES
#define LV_FREETYPE_CACHE_FT_SIZES CONFIG_LV_FREETYPE_CACHE_FT_SIZES
#else
#define LV_FREETYPE_CACHE_FT_SIZES 0
#endif
#endif
#endif
#endif #endif
/*Rlottie library*/ /*Rlottie library*/