Modernes Text-Rendering unter Linux: Teil 1

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 hier

Fazit


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.

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


All Articles