Selamat datang di bagian pertama Rendering Teks Linux Modern. Di setiap artikel dalam seri ini, kami akan mengembangkan program C mandiri untuk memvisualisasikan karakter atau urutan karakter. Setiap program ini akan mengimplementasikan fungsi yang saya anggap perlu untuk rendering teks modern.
Pada bagian pertama kita akan mengkonfigurasi FreeType dan menulis renderer simbol sederhana di konsol.

Inilah yang akan kami tulis. Dan
ini kodenya.
Pengaturan sistem
- Sistem operasi saya:
Ubuntu 18.04.2 LTS (bionic)
- C compiler:
clang version 6.0.0-1ubuntu2
Instal FreeType
Di Ubuntu, Anda perlu menginstal FreeType dan libpng.
$ sudo apt install libfreetype6 libfreetype6-dev $ sudo apt install libpng16-16 libpng-dev
- Saya memiliki FreeType versi
2.8.1-2ubuntu2
, walaupun pada saat penulisan, FreeType-2.10.1
versi terbaru FreeType-2.10.1
, ia juga berfungsi.
- versi libpng
(1.6.34-1ubuntu0.18.04.2)
Penyaji konsol
Buat file C ( main.c
dalam kasus saya)
#include <stdio.h> int main() { printf("Hello, world\n"); return 0; }
$ clang -Wall -Werror -o main main.c $ ./main Hello, world
Kami menghubungkan pustaka FreeType
Untuk mencari jalur sertakan (mis., Direktori yang dilewati kompiler saat mencari file di
#include
) untuk FreeType, jalankan:
$ pkg-config --cflags freetype2 -I/usr/include/freetype2 -I/usr/include/libpng16
Baris
-I/usr/include/freetype2 -I/usr/include/libpng16
berisi flag kompilasi yang diperlukan untuk mengaktifkan FreeType dalam program 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
Kami mencetak versi FreeType
Di dalam
main()
inisialisasi FreeType dengan
FT_Init_FreeType(&ft)
dan periksa kesalahan (fungsi FreeType mengembalikan 0 jika berhasil).
(Mulai sekarang, semua fungsi yang akan saya gunakan diambil dari
bantuan untuk API FreeType ).
FT_Library ft; FT_Error err = FT_Init_FreeType(&ft); if (err != 0) { printf("Failed to initialize FreeType\n"); exit(EXIT_FAILURE); }
Kemudian menggunakan FT_Library_Version kita mendapatkan nomor versi.
FT_Int major, minor, patch; FT_Library_Version(ft, &major, &minor, &patch); printf("FreeType's version is %d.%d.%d\n", major, minor, patch);
Jika dikompilasi menggunakan perintah terakhir, kesalahan tautan akan muncul:
/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)
Untuk memperbaikinya, tambahkan
-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
Unduh Font
Langkah pertama untuk merender karakter adalah mengunduh file font. Saya menggunakan
ubuntu mono .
Untuk memahami perbedaan yang tepat antara konstruksi wajah font, keluarga font, dan font tersendiri, lihat
dokumentasi FreeType .
Argumen ketiga disebut
indeks wajah . Ini dirancang untuk memungkinkan pembuat font untuk memasukkan beberapa wajah ke dalam ukuran font yang sama. Karena setiap font memiliki setidaknya satu wajah, nilai 0 akan selalu berfungsi, memilih opsi pertama.
FT_Face face; err = FT_New_Face(ft, "./UbuntuMono.ttf", 0, &face); if (err != 0) { printf("Failed to load face\n"); exit(EXIT_FAILURE); }
Tetapkan ukuran piksel untuk wajah
Dengan menggunakan instruksi ini, kami memberi tahu FreeType lebar dan tinggi yang diinginkan untuk karakter yang ditampilkan.
Jika Anda melewati nol untuk lebar, FreeType mengartikan ini sebagai "sama seperti yang lain," dalam hal ini 32px. Ini dapat digunakan untuk menampilkan karakter, misalnya, dengan lebar 10px dan tinggi 16px.
Operasi ini mungkin gagal pada font berukuran tetap, seperti dalam kasus emoji.
err = FT_Set_Pixel_Sizes(face, 0, 32); if (err != 0) { printf("Failed to set pixel size\n"); exit(EXIT_FAILURE); }
Mendapatkan indeks untuk karakter
Pertama-tama, kembali ke
dokumentasi FreeType dan buat konvensi penamaan. Simbol tidak sama dengan
mesin terbang . Karakter adalah apa yang dikatakan
char
, dan mesin terbang adalah gambar yang entah bagaimana terkait dengan karakter itu. Hubungan ini agak rumit karena char dapat berhubungan dengan beberapa mesin terbang: yaitu aksen. Mesin terbang dapat berhubungan dengan banyak karakter: yaitu, ligatur, di mana -> direpresentasikan sebagai gambar tunggal.
Untuk mendapatkan indeks mesin terbang yang sesuai dengan karakter, kami menggunakan
FT_Get_Char_Index
. Seperti yang dapat Anda pahami, ini melibatkan karakter yang cocok dan mesin terbang hanya satu lawan satu. Dalam artikel mendatang di seri ini, kami akan menyelesaikan masalah menggunakan perpustakaan
HarfBuzz .
FT_UInt glyph_index = FT_Get_Char_Index(face, 'a');
Memuat mesin terbang dari wajah
Setelah menerima glyph_index, kita dapat memuat glyph yang sesuai dari wajah kita.
Dalam angsuran di masa mendatang, kami akan membahas secara rinci berbagai flag unduhan dan bagaimana mereka memungkinkan Anda untuk menggunakan fitur-fitur seperti petunjuk dan font 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); }
Tampilkan mesin terbang dalam wadahnya (slot mesin terbang)
Sekarang kita akhirnya dapat menampilkan mesin terbang kami dalam wadahnya (slot) yang ditentukan di
face->glyph
.
Kami juga akan membahas render flag di masa depan, karena mereka memungkinkan kami untuk menggunakan rendering LCD (atau sub-pixel) dan antialiasing grayscale.
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); }
Output karakter ke konsol
Bitmap dari glyph yang diberikan dapat diperoleh dari
face->glyph->bitmap.buffer
, di mana ia disajikan sebagai array nilai char yang tidak ditandatangani, sehingga nilainya berkisar dari 0 hingga 255.
Buffer dikembalikan sebagai array satu dimensi, tetapi merupakan gambar 2D. Untuk mengakses baris ke-i dari kolom ke-j, kita menghitung
column * row_width + row
, seperti dalam
bitmap.buffer[i * face->glyph->bitmap.pitch + j]
.
Anda dapat melihat bahwa ketika mengakses array kami menggunakan
bitmap.width
dalam satu loop dan
bitmap.pitch
, karena panjang setiap baris piksel sama dengan
bitmap.width
, tetapi "lebar" buffer adalah
bitmap.pitch
.
Dalam kode berikut, semua baris dan kolom diurutkan, dan karakter yang berbeda diambil tergantung pada kecerahan piksel.
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"); }
Output konsol.
$ clang -I/usr/include/freetype2 \ -I/usr/include/libpng16 \ -Wall -Werror \ -o main \ -lfreetype \ main.c && ./main FreeType's version is 2.8.1 .*****. .********. .********* . ***. *** *** .******** *********** .**. *** *** *** *** *** ***. *** .*********** *********** .*******..
→ Kode lengkap dapat dilihat
di siniKesimpulan
Kami telah membuat renderer karakter dasar di konsol. Contoh ini dapat (dan akan) diperluas untuk membuat karakter menjadi tekstur OpenGL untuk mendukung emoji, rendering sub-pixel, pengikat, dan banyak lagi. Pada bagian selanjutnya, kita akan berbicara tentang smoothing subpixel LCD dibandingkan dengan gradasi abu-abu, pro dan kontra mereka.
Sampai ketemu lagi.