Willkommen zum ersten Teil von Modern Linux Text Rendering. In jedem Artikel dieser Reihe werden wir ein in sich geschlossenes C-Programm entwickeln, um ein Zeichen oder eine Zeichenfolge zu visualisieren. Jedes dieser Programme implementiert eine Funktion, die ich für die moderne Textwiedergabe für notwendig halte.
Im ersten Teil werden wir FreeType konfigurieren und einen einfachen Symbol-Renderer in die Konsole schreiben.

Das werden wir schreiben. Und
hier ist der Code.
Systemeinrichtung
- Mein Betriebssystem:
Ubuntu 18.04.2 LTS (bionic)
- C-Compiler:
clang version 6.0.0-1ubuntu2
Installieren Sie FreeType
Unter Ubuntu müssen Sie FreeType und libpng installieren.
$ sudo apt install libfreetype6 libfreetype6-dev $ sudo apt install libpng16-16 libpng-dev
- Ich habe FreeType Version
2.8.1-2ubuntu2
, obwohl es zum Zeitpunkt des Schreibens der neuesten Version von FreeType-2.10.1
auch funktioniert.
- libpng version
(1.6.34-1ubuntu0.18.04.2)
Konsolen-Renderer
Erstellen Sie eine C-Datei (in meinem Fall main.c
)
#include <stdio.h> int main() { printf("Hello, world\n"); return 0; }
$ clang -Wall -Werror -o main main.c $ ./main Hello, world
Wir verbinden Bibliotheken von FreeType
Führen Sie Folgendes aus, um nach dem Include-Pfad (d. H. Verzeichnissen, die der Compiler bei der Suche nach Dateien in
#include
durchläuft) für FreeType zu suchen:
$ pkg-config --cflags freetype2 -I/usr/include/freetype2 -I/usr/include/libpng16
Die Zeile
-I/usr/include/freetype2 -I/usr/include/libpng16
enthält die Kompilierungsflags, die zum Aktivieren von FreeType im C-Programm erforderlich sind.
#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
Wir drucken die Version von FreeType
Initialisieren Sie in
main()
FreeType mit
FT_Init_FreeType(&ft)
und
FT_Init_FreeType(&ft)
Sie nach Fehlern (FreeType-Funktionen geben bei Erfolg 0 zurück).
(Von nun an werden alle Funktionen, die ich verwenden werde, aus der
Hilfe für die FreeType-API übernommen. )
FT_Library ft; FT_Error err = FT_Init_FreeType(&ft); if (err != 0) { printf("Failed to initialize FreeType\n"); exit(EXIT_FAILURE); }
Dann erhalten wir mit FT_Library_Version die Versionsnummer.
FT_Int major, minor, patch; FT_Library_Version(ft, &major, &minor, &patch); printf("FreeType's version is %d.%d.%d\n", major, minor, patch);
Wenn mit dem letzten Befehl kompiliert, wird ein Linkerfehler angezeigt:
/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)
Um dies zu beheben, fügen Sie
-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
Schriftart herunterladen
Der erste Schritt zum Rendern eines Zeichens besteht darin, eine Schriftartdatei herunterzuladen. Ich benutze
Ubuntu Mono .
Informationen zum genauen Unterschied zwischen einem Schriftartenkonstrukt, einer Schriftfamilie und einzelnen Schriftarten finden Sie in der
FreeType-Dokumentation .
Das dritte Argument heißt
Gesichtsindex . Es soll Schriftstellern ermöglichen, mehrere Flächen in dieselbe Schriftgröße einzufügen. Da jede Schriftart mindestens eine Fläche hat, funktioniert immer der Wert 0, wenn Sie die erste Option auswählen.
FT_Face face; err = FT_New_Face(ft, "./UbuntuMono.ttf", 0, &face); if (err != 0) { printf("Failed to load face\n"); exit(EXIT_FAILURE); }
Stellen Sie die Pixelgröße für das Gesicht ein
Mit dieser Anweisung teilen wir FreeType die gewünschte Breite und Höhe für die angezeigten Zeichen mit.
Wenn Sie für die Breite Null übergeben, interpretiert FreeType dies als "dasselbe wie die anderen", in diesem Fall 32px. Dies kann verwendet werden, um beispielsweise ein Zeichen mit einer Breite von 10 Pixel und einer Höhe von 16 Pixel anzuzeigen.
Dieser Vorgang kann bei einer Schriftart mit fester Größe fehlschlagen, wie im Fall von Emoji.
err = FT_Set_Pixel_Sizes(face, 0, 32); if (err != 0) { printf("Failed to set pixel size\n"); exit(EXIT_FAILURE); }
Index für Charakter abrufen
Kehren Sie zunächst zur
FreeType-Dokumentation zurück und legen Sie eine Namenskonvention fest. Ein Symbol ist nicht dasselbe wie eine
Glyphe . Ein Charakter ist das, was
char
sagt, und eine Glyphe ist ein Bild, das irgendwie mit diesem Charakter verbunden ist. Diese Beziehung ist ziemlich kompliziert, da char mehreren Glyphen entsprechen kann: d. H. Akzenten. Eine Glyphe kann vielen Zeichen entsprechen, dh Ligaturen, wobei -> als einzelnes Bild dargestellt wird.
Um den dem Zeichen entsprechenden
FT_Get_Char_Index
, verwenden wir
FT_Get_Char_Index
. Wie Sie verstehen können, müssen Zeichen und Glyphen nur eins zu eins abgeglichen werden. In einem zukünftigen Artikel dieser Reihe werden wir das Problem mithilfe der
HarfBuzz- Bibliothek lösen.
FT_UInt glyph_index = FT_Get_Char_Index(face, 'a');
Laden einer Glyphe aus dem Gesicht
Nachdem wir glyph_index erhalten haben, können wir die entsprechende Glyphe aus unserem Gesicht laden.
In einem zukünftigen Teil werden wir die verschiedenen Download-Flags ausführlich erläutern und erläutern, wie Sie Funktionen wie Hinweise und Bitmap-Schriftarten verwenden können.
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); }
Zeigen Sie eine Glyphe in ihrem Container an (Glyphensteckplatz).
Jetzt können wir endlich unsere Glyphe in ihrem Container (Slot)
face->glyph
in
face->glyph
.
Wir werden in Zukunft auch das Rendern von Flags diskutieren, da sie die Verwendung von LCD- (oder Subpixel-) Rendering und Graustufen-Antialiasing ermöglichen.
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); }
Zeichenausgabe an die Konsole
Die Bitmap des gerenderten Glyphen kann von
face->glyph->bitmap.buffer
, wo sie als Array von vorzeichenlosen
face->glyph->bitmap.buffer
dargestellt wird, sodass ihre Werte zwischen 0 und 255 liegen.
Der Puffer wird als eindimensionales Array zurückgegeben, ist jedoch ein 2D-Bild. Um auf die i-te Zeile der j-ten Spalte
column * row_width + row
, berechnen wir die
column * row_width + row
wie in
bitmap.buffer[i * face->glyph->bitmap.pitch + j]
.
Sie können sehen, dass wir beim Zugriff auf das Array
bitmap.width
in einer Schleife und
bitmap.pitch
, da die Länge jeder
bitmap.width
gleich
bitmap.width
ist, die „Breite“ des Puffers jedoch
bitmap.pitch
.
Im folgenden Code werden alle Zeilen und Spalten sortiert und je nach Helligkeit des Pixels unterschiedliche Zeichen gezeichnet.
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"); }
Konsolenausgabe.
$ clang -I/usr/include/freetype2 \ -I/usr/include/libpng16 \ -Wall -Werror \ -o main \ -lfreetype \ main.c && ./main FreeType's version is 2.8.1 .*****. .********. .********* . ***. *** *** .******** *********** .**. *** *** *** *** *** ***. *** .*********** *********** .*******..
→ Den vollständigen Code finden Sie
hierFazit
Wir haben einen grundlegenden Zeichenrenderer in der Konsole erstellt. Dieses Beispiel kann (und wird) erweitert werden, um Zeichen in OpenGL-Texturen zu rendern, um Emoji, Subpixel-Rendering, Ligaturen und mehr zu unterstützen. Im nächsten Teil werden wir uns mit der Glättung von LCD-Subpixeln im Vergleich zu Graustufen, ihren Vor- und Nachteilen befassen.
Bis bald.