Skip to content

Commit

Permalink
Merge pull request #320 from igrr/feat/freetype_improvements
Browse files Browse the repository at this point in the history
FreeType: improve library dependency handling, add example
igrr authored Jun 7, 2024
2 parents ebfce1a + 16435e6 commit 46213e9
Showing 10 changed files with 261 additions and 9 deletions.
19 changes: 11 additions & 8 deletions freetype/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
idf_component_register()

set(CMAKE_SYSTEM_IGNORE_PATH "/usr/include;/usr/lib/x86_64-linux-gnu")
set(BUILD_SHARED_LIBS OFF)

function(install)
endfunction()
# Override options defined in freetype/CMakeLists.txt.
# We could have used normal set(...) here if freetype enabled CMake policy CMP0077.
option(FT_DISABLE_HARFBUZZ "" ON)
option(FT_DISABLE_BZIP2 "" ON)
option(FT_DISABLE_BROTLI "" ON)
option(FT_DISABLE_PNG "" ON)
option(FT_DISABLE_ZLIB "" ON)

function(export)
endfunction()
# These are regular CMake variables, so we can set them directly.
set(SKIP_INSTALL_ALL TRUE)
set(BUILD_SHARED_LIBS OFF)

add_subdirectory(freetype output)
target_compile_options(freetype PRIVATE "-Wno-dangling-pointer")

target_link_libraries(${COMPONENT_LIB} INTERFACE freetype)
target_link_libraries(${COMPONENT_LIB} INTERFACE freetype)
5 changes: 5 additions & 0 deletions freetype/examples/freetype-example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.17)

set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(freetype-example)
51 changes: 51 additions & 0 deletions freetype/examples/freetype-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# FreeType Example

This is a simple example of initializing FreeType library, loading a font from a filesystem, and rendering a line of text.

The font file (DejaVu Sans) is downloaded at compile time and is added into a SPIFFS filesystem image. The filesystem is flashed to the board together with the application. The example loads the font file and renders "FreeType" text into the console as ASCII art.

This example doesn't require any special hardware and can run on any development board.

## Building and running

Run the application as usual for an ESP-IDF project. For example, for ESP32:
```
idf.py set-target esp32
idf.py -p PORT flash monitor
```

## Example output

The example should output the following:

```
I (468) main_task: Calling app_main()
I (538) example: FreeType library initialized
I (1258) example: Font loaded
I (1268) example: Rendering char: 'F'
I (1388) example: Rendering char: 'r'
I (1528) example: Rendering char: 'e'
I (1658) example: Rendering char: 'e'
I (1798) example: Rendering char: 'T'
I (1938) example: Rendering char: 'y'
I (2078) example: Rendering char: 'p'
I (2208) example: Rendering char: 'e'
######. #########
## +#
## ##### +###+ +###+ +# +# ######## +###+
## ##+ +#. .#+ +#. .#+ +# #+ #+##+ .#+ +#. .#+
###### ## #+ +# #+ +# +# ## +# ## +# #+ +#
## ## .####### .####### +# .# ## ## .# .#######
## ## .#. .#. +# #+.#. ## .# .#.
## ## #+ #+ +# +### ## +# #+
## ## ##+ ++ ##+ ++ +# ##+ ##+ .#+ ##+ ++
## ## +#### +#### +# ## ###### +####
## ##
+#. ##
##+ ##
```
13 changes: 13 additions & 0 deletions freetype/examples/freetype-example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
idf_component_register(SRCS "freetype-example.c"
INCLUDE_DIRS "."
PRIV_REQUIRES spiffs)

# Download the example font into a directory "spiffs" in the build directory
set(URL "https://github.com/espressif/esp-docs/raw/f036a337d8bee5d1a93b2264ecd29255baec4260/src/esp_docs/fonts/DejaVuSans.ttf")
set(FILE "DejaVuSans.ttf")
set(SPIFFS_DIR "${CMAKE_BINARY_DIR}/spiffs")
file(MAKE_DIRECTORY ${SPIFFS_DIR})
file(DOWNLOAD ${URL} ${SPIFFS_DIR}/${FILE} SHOW_PROGRESS)

# Create a partition named "fonts" with the example font
spiffs_create_partition_image(fonts ${SPIFFS_DIR} FLASH_IN_PROJECT)
150 changes: 150 additions & 0 deletions freetype/examples/freetype-example/main/freetype-example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/


#include <stdlib.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_spiffs.h"
#include "ft2build.h"
#include FT_FREETYPE_H

static const char *TAG = "example";

static void init_filesystem(void);
static void init_freetype(void);
static void load_font(void);
static void render_text(void);

#define BITMAP_WIDTH 80
#define BITMAP_HEIGHT 18

static FT_Library s_library;
static FT_Face s_face;
static uint8_t s_bitmap[BITMAP_HEIGHT][BITMAP_WIDTH];


void app_main(void)
{
init_filesystem();
init_freetype();
load_font();
render_text();
}

static void init_filesystem(void)
{
esp_vfs_spiffs_conf_t conf = {
.base_path = "/fonts",
.partition_label = "fonts",
.max_files = 1,
};

ESP_ERROR_CHECK(esp_vfs_spiffs_register(&conf));
}

static void init_freetype(void)
{
FT_Error error = FT_Init_FreeType( &s_library );
if (error) {
ESP_LOGE(TAG, "Error initializing FreeType library: %d", error);
abort();
}

ESP_LOGI(TAG, "FreeType library initialized");
}

static void load_font(void)
{
FT_Error error = FT_New_Face( s_library,
"/fonts/DejaVuSans.ttf",
0,
&s_face );
if (error) {
ESP_LOGE(TAG, "Error loading font: %d", error);
abort();
}

ESP_LOGI(TAG, "Font loaded");

}

static void render_text(void)
{
/* Configure character size. */
const int font_size = 14;
const int freetype_scale = 64;
FT_Error error = FT_Set_Char_Size(s_face, 0, font_size * freetype_scale, 0, 0 );
if (error) {
ESP_LOGE(TAG, "Error setting font size: %d", error);
abort();
}

const char *text = "FreeType";
int num_chars = strlen(text);

/* current drawing position */
int x = 0;
int y = 12;

for (int n = 0; n < num_chars; n++) {
ESP_LOGI(TAG, "Rendering char: '%c'", text[n]);

/* retrieve glyph index from character code */
FT_UInt glyph_index = FT_Get_Char_Index( s_face, text[n] );

/* load glyph image into the slot (erase previous one) */
error = FT_Load_Glyph( s_face, glyph_index, FT_LOAD_DEFAULT );
if (error) {
ESP_LOGE(TAG, "Error loading glyph: %d", error);
abort();
}

/* convert to a bitmap */
error = FT_Render_Glyph( s_face->glyph, FT_RENDER_MODE_NORMAL );
if (error) {
ESP_LOGE(TAG, "Error rendering glyph: %d", error);
abort();
}

/* copy the glyph bitmap into the overall bitmap */
FT_GlyphSlot slot = s_face->glyph;
for (int iy = 0; iy < slot->bitmap.rows; iy++) {
for (int ix = 0; ix < slot->bitmap.width; ix++) {
/* bounds check */
int res_x = ix + x;
int res_y = y + iy - slot->bitmap_top;
if (res_x >= BITMAP_WIDTH || res_y >= BITMAP_HEIGHT) {
continue;
}
s_bitmap[res_y][res_x] = slot->bitmap.buffer[ix + iy * slot->bitmap.width];
}
}

/* increment horizontal position */
x += slot->advance.x / 64;
if (x >= BITMAP_WIDTH) {
break;
}
}

/* output the resulting bitmap to console */
for (int iy = 0; iy < BITMAP_HEIGHT; iy++) {
for (int ix = 0; ix < x; ix++) {
int val = s_bitmap[iy][ix];
if (val > 127) {
putchar('#');
} else if (val > 64) {
putchar('+');
} else if (val > 32) {
putchar('.');
} else {
putchar(' ');
}
}
putchar('\n');
}
}
4 changes: 4 additions & 0 deletions freetype/examples/freetype-example/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dependencies:
espressif/freetype:
version: "*"
override_path: "../../.."
4 changes: 4 additions & 0 deletions freetype/examples/freetype-example/partitions.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
fonts, data, spiffs, , 0xF0000,
17 changes: 17 additions & 0 deletions freetype/examples/freetype-example/pytest_freetype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
from __future__ import unicode_literals

import textwrap

import pytest
from pytest_embedded import Dut


@pytest.mark.supported_targets
@pytest.mark.generic
def test_freetype_example(dut: Dut) -> None:
dut.expect_exact('FreeType library initialized')
dut.expect_exact('Font loaded')
for c in 'FreeType':
dut.expect_exact(f'Rendering char: \'{c}\'')
5 changes: 5 additions & 0 deletions freetype/examples/freetype-example/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"

CONFIG_ESP_MAIN_TASK_STACK_SIZE=20000
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
2 changes: 1 addition & 1 deletion freetype/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "2.13.0~2"
version: "2.13.0~3"
description: freetype C library
url: https://github.com/espressif/idf-extra-components/tree/master/freetype
repository: "https://github.com/espressif/idf-extra-components.git"

0 comments on commit 46213e9

Please sign in to comment.