Rendu de texte moderne sous Linux: partie 1

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 ici

Conclusion


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.

Source: https://habr.com/ru/post/fr461497/


All Articles