Bienvenue dans la première partie du rendu de texte Linux moderne. Dans chaque article de cette série, nous développerons un programme C autonome pour visualiser un caractère ou une séquence de caractères. Chacun de ces programmes implémentera une fonction que je considère nécessaire pour le rendu de texte moderne.
Dans la première partie, nous allons configurer FreeType et écrire un simple rendu de symboles dans la console.

C'est ce que nous écrirons. Et
voici le code.
Configuration du système
- Mon système d'exploitation:
Ubuntu 18.04.2 LTS (bionic)
- Compilateur C:
clang version 6.0.0-1ubuntu2
Installez FreeType
Sur Ubuntu, vous devez installer FreeType et libpng.
$ sudo apt install libfreetype6 libfreetype6-dev $ sudo apt install libpng16-16 libpng-dev
- J'ai FreeType version
2.8.1-2ubuntu2
, bien qu'au moment d'écrire ces 2.8.1-2ubuntu2
, la dernière version de FreeType-2.10.1
, elle fonctionne également.
- version libpng
(1.6.34-1ubuntu0.18.04.2)
Rendu de console
Créer un fichier C ( main.c
dans mon cas)
#include <stdio.h> int main() { printf("Hello, world\n"); return 0; }
$ clang -Wall -Werror -o main main.c $ ./main Hello, world
Nous connectons les bibliothèques de FreeType
Pour rechercher le chemin d'inclusion (c'est-à-dire les répertoires que le compilateur parcourt lors de la recherche de fichiers dans
#include
) pour FreeType, exécutez:
$ pkg-config --cflags freetype2 -I/usr/include/freetype2 -I/usr/include/libpng16
La ligne
-I/usr/include/freetype2 -I/usr/include/libpng16
contient les drapeaux de compilation nécessaires pour activer FreeType dans le programme C.
#include <stdio.h> #include <freetype2/ft2build.h> #include FT_FREETYPE_H int main() { printf("Hello, world\n"); return 0; }
$ clang -I/usr/include/freetype2 \ -I/usr/include/libpng16 \ -Wall -Werror \ -o main \ main.c $ ./main Hello, world
Nous imprimons la version de FreeType
Dans
main()
initialisez FreeType avec
FT_Init_FreeType(&ft)
et vérifiez les erreurs (les fonctions FreeType renvoient 0 en cas de succès).
(Désormais, toutes les fonctions que j'utiliserai sont extraites de l'
aide de l'API FreeType ).
FT_Library ft; FT_Error err = FT_Init_FreeType(&ft); if (err != 0) { printf("Failed to initialize FreeType\n"); exit(EXIT_FAILURE); }
Ensuite, en utilisant FT_Library_Version, nous obtenons le numéro de version.
FT_Int major, minor, patch; FT_Library_Version(ft, &major, &minor, &patch); printf("FreeType's version is %d.%d.%d\n", major, minor, patch);
Si compilé à l'aide de la dernière commande, une erreur de l'éditeur de liens apparaîtra:
/tmp/main-d41304.o: In function `main': main.c:(.text+0x14): undefined reference to `FT_Init_FreeType' main.c:(.text+0x54): undefined reference to `FT_Library_Version' clang: error: linker command failed with exit code 1 (use -v to see invocation)
Pour résoudre ce problème, ajoutez
-lfreetype
.
$ clang -I/usr/include/freetype2 \ -I/usr/include/libpng16 \ -Wall -Werror \ -o main \ -lfreetype \ main.c $ ./main FreeType's version is 2.8.1
Téléchargement de polices
La première étape du rendu d'un caractère consiste à télécharger un fichier de police. J'utilise
Ubuntu mono .
Pour comprendre la différence exacte entre une construction de visage de police, une famille de polices et des polices individuelles, consultez la
documentation FreeType .
Le troisième argument est appelé
indice de face . Il est conçu pour permettre aux créateurs de polices d'insérer plusieurs faces dans la même taille de police. Étant donné que chaque police a au moins une face, une valeur de 0 fonctionnera toujours, en choisissant la première option.
FT_Face face; err = FT_New_Face(ft, "./UbuntuMono.ttf", 0, &face); if (err != 0) { printf("Failed to load face\n"); exit(EXIT_FAILURE); }
Définir la taille des pixels pour le visage
En utilisant cette instruction, nous indiquons à FreeType la largeur et la hauteur souhaitées pour les caractères affichés.
Si vous passez zéro pour la largeur, FreeType interprète cela comme «le même que les autres», dans ce cas 32px. Cela peut être utilisé pour afficher un caractère, par exemple, avec une largeur de 10 pixels et une hauteur de 16 pixels.
Cette opération peut échouer sur une police de taille fixe, comme dans le cas des emoji.
err = FT_Set_Pixel_Sizes(face, 0, 32); if (err != 0) { printf("Failed to set pixel size\n"); exit(EXIT_FAILURE); }
Obtenir l'index pour le personnage
Tout d'abord, revenons à la
documentation FreeType et établissez une convention de nommage. Un symbole n'est pas la même chose qu'un
glyphe . Un personnage est ce que dit
char
, et un glyphe est une image qui est en quelque sorte associée à ce personnage. Cette relation est assez compliquée car l'omble peut correspondre à plusieurs glyphes: c'est-à-dire aux accents. Un glyphe peut correspondre à de nombreux caractères: c'est-à-dire des ligatures, où -> est représenté comme une seule image.
Pour obtenir l'index de glyphe correspondant au caractère, nous utilisons
FT_Get_Char_Index
. Comme vous pouvez le comprendre, cela implique de faire correspondre les caractères et les glyphes un à un. Dans un prochain article de cette série, nous allons résoudre le problème en utilisant la bibliothèque
HarfBuzz .
FT_UInt glyph_index = FT_Get_Char_Index(face, 'a');
Chargement d'un glyphe à partir du visage
Après avoir reçu glyph_index, nous pouvons charger le glyphe correspondant de notre visage.
Dans un prochain épisode, nous discuterons en détail des différents indicateurs de téléchargement et de la façon dont ils vous permettent d'utiliser des fonctionnalités telles que les polices d'indication et bitmap.
FT_Int32 load_flags = FT_LOAD_DEFAULT; err = FT_Load_Glyph(face, glyph_index, load_flags); if (err != 0) { printf("Failed to load glyph\n"); exit(EXIT_FAILURE); }
Afficher un glyphe dans son conteneur (emplacement de glyphe)
Maintenant, nous pouvons enfin afficher notre glyphe dans son conteneur (slot) spécifié dans
face->glyph
.
Nous discuterons également des indicateurs de rendu à l'avenir, car ils permettent l'utilisation du rendu LCD (ou sous-pixel) et de l'anticrénelage en niveaux de gris.
FT_Int32 render_flags = FT_RENDER_MODE_NORMAL; err = FT_Render_Glyph(face->glyph, render_flags); if (err != 0) { printf("Failed to render the glyph\n"); exit(EXIT_FAILURE); }
Sortie de caractères sur la console
Le bitmap du glyphe rendu peut être obtenu à partir de
face->glyph->bitmap.buffer
, où il est présenté comme un tableau de valeurs char non signées, donc ses valeurs vont de 0 à 255.
Le tampon est renvoyé sous forme de tableau unidimensionnel, mais est une image 2D. Pour accéder à la i-ème ligne de la j-ème colonne, nous calculons la
column * row_width + row
, comme dans
bitmap.buffer[i * face->glyph->bitmap.pitch + j]
.
Vous pouvez voir que lors de l'accès au tableau, nous avons utilisé
bitmap.width
dans une boucle et
bitmap.pitch
, car la longueur de chaque ligne de pixels est égale à
bitmap.width
, mais la «largeur» du tampon est
bitmap.pitch
.
Dans le code suivant, toutes les lignes et colonnes sont triées et différents caractères sont dessinés en fonction de la luminosité du pixel.
for (size_t i = 0; i < face->glyph->bitmap.rows; i++) { for (size_t j = 0; j < face->glyph->bitmap.width; j++) { unsigned char pixel_brightness = face->glyph->bitmap.buffer[i * face->glyph->bitmap.pitch + j]; if (pixel_brightness > 169) { printf("*"); } else if (pixel_brightness > 84) { printf("."); } else { printf(" "); } } printf("\n"); }
Sortie console.
$ clang -I/usr/include/freetype2 \ -I/usr/include/libpng16 \ -Wall -Werror \ -o main \ -lfreetype \ main.c && ./main FreeType's version is 2.8.1 .*****. .********. .********* . ***. *** *** .******** *********** .**. *** *** *** *** *** ***. *** .*********** *********** .*******..
→ Le code complet peut être vu
iciConclusion
Nous avons créé un rendu de caractères de base dans la console. Cet exemple peut (et sera) étendu pour rendre les caractères dans des textures OpenGL afin de prendre en charge les emoji, le rendu sous-pixel, les ligatures, etc. Dans la partie suivante, nous parlerons du lissage des sous-pixels LCD par rapport aux nuances de gris, leurs avantages et leurs inconvénients.
A bientôt.