Perpustakaan header saja untuk merender teks di SDL2 murni dengan STB_Truetype. Caches ini melambai karena digambar memungkinkan rendering teks cepat. Ini juga menyediakan beberapa cara mudah untuk membuat string ke tekstur untuk rendering teks yang lebih cepat.
Baru (2022)! Can Prerender dalam lingkungan multithreaded dengan producerConsumerFrontend.h !
Baru (2021)! Juga dapat membuat bgfx dengan bgfxFrontend.h !

Contoh teks dari http://www.columbia.edu/~fdc/utf8/index.html

Liam Twigger - @snapperthetwig
fc.drawText(x, y, string)fc.renderTextToTexture(string, &widthOut, &heightOut)Di Intel I7-8750H:
Contoh gambar membutuhkan 0,7ms untuk render (~ 1400 fps) jika menggunakan tekstur ( renderTextToTexture / renderTextToObject ). Kecepatan akan menjadi maksimum yang SDL2 memungkinkan Anda membalik tekstur.
Contoh gambar membutuhkan ~ 5ms untuk merender (~ 200 fps) jika rendering langsung ( drawText )
Untuk teks yang bertahan lebih dari satu bingkai, Anda harus men -cache dengan renderTextToTexture atau renderTextToObject .
Teks yang diformat:
Non-SDL Frontends:
Ini adalah pustaka header saja - tidak diperlukan sistem build
Di header apa pun:
# include " path/to/sdlStbFont/sdlStbFont.h "Dalam satu .cpp:
# define SDL_STB_FONT_IMPL
# include " path/to/sdlStbFont/sdlStbFont.h " Perpustakaan ini memiliki ketergantungan pada STB_Truetype. Ini akan secara otomatis memasukkan ini. Jika Anda tidak ingin disertakan secara otomatis, gunakan #define STB_TRUETYPE_INCLUDE_HANDLED , dan tangani termasuk sendiri dari stb_truetype.h sendiri.
# define SDL_STB_FONT_IMPL
# include " sdlStbFont.h "
...
// Load font
char * ttfFontFromMemory = loadFontFileSomehow( " path/to/file.ttf " );
sdl_stb_font_cache fc;
fc.faceSize = 24 ; // Must be set before loadFont()!
fc.tabWidthInSpaces = 8 ; // Optional
fc.loadFont(ttfFontFromMemory);
...
// Setup renderer
SDL_Renderer * mSdlRenderer = SDL_CreateRenderer( mWindow , SDL_RENDERER_SOFTWARE, 0 );
fc.bindRenderer( mSdlRenderer ); // Must bind a renderer before generating any glyphs
...
// Main loop
SDL_SetRenderDrawColor ( mSdlRenderer , 125 , 125 , 125 , 255 );
SDL_RenderClear ( mSdlRenderer );
fc.drawText( 5 , 5 , " Hello world! " );
SDL_RenderPresent ( mSdlRenderer );
// fc will clean itself up when it falls out of scope
delete[] ttfFontFromMemory; // You must manually remove the char buff when done
// if you want auto management use fc.loadFontManaged(), which will transfer
// ownership of ttfFontFromMemory to fc and be freed when done.Anda juga bisa mendapatkan lebar dan tinggi dari string yang diberikan:
fc.drawText(5, 5, widthOut, heightOut, "Hello world!");
// Draws "hello world" and sets widthOut and heightOut to the size of the string
class sdl_stb_font_cache {
...
public:
int ascent;
int descent;
int lineGap;
int baseline;
int rowSize;
int tabWidth; // May be changed later
float scale;
float underlineThickness;
float strikethroughThickness;
float underlinePosition;
float strikethroughPosition;
// Must be set before loadFont() is called. Cannot be changed after
int faceSize; // Default is 20. All the parameters are calcualted based on this
int tabWidthInSpaces; // Default is 8, set this before loadFont(). Max 128. Sets tabWidth when font is loaded
...
}
fc.loadFont(ttfFontFromMemory);
fc.addFont(someSecondFontFromMemory);
fc.addFont(someThirdFontFromMemory);
...
etc
Font pertama yang dimuat harus dengan "Loadfont".
Perhatikan bahwa semua font harus dimuat sebelum fungsi gambar dipanggil. Jika mesin terbang tidak ditemukan di font pertama maka font kedua akan dicari, dll.
int w, h;
fc.getTextSize(w, h, " Text " ); // Get width and height at same time
// Also
h = fc.getTextHeight( " Text " ); // Faster, if only height is needed
w = fc.getTextWidth( " Text " ); // Internally just a wrapper for getTextSize
int nRows = fc.getTextRows( " Text n More text " ); // UTF8 safe, returns the number of rows of text - here it's 2
int numNewlines = fc.getNumNewLines Tip: drawText() Mengembalikan koordinat x dari ujung string yang ditarik atau objek teks yang diformat.
Untuk pembungkus baris, gunakan fungsi fc.breakString(stringIn, arrayOut, width) . Ini mengubah string atau objek teks yang diformat menjadi vektor string atau objek teks yang diformat masing -masing yang kurang dari lebar. Ini akan mencoba untuk memecahkan Newline dan Whitespace terlebih dahulu maka itu akan membagi kata jika lebih panjang dari lebar yang ditentukan. Ini aman UTF8.
Manajemen Manual:
char * mFontData = loadFontFromFileSomehow( " path/to/file.ttf " );
fc.loadFont( mFontData );
// You will have to free mFontData when you are done with it
// fc does not copy mFontData internally
// using fc after free'ing mFontData will result in undefined behaviourManajemen Otomatis:
filep * file = openFile( " path/to/file " );
sttfont_memory mFontData ;
mFontData .alloc(file_size);
fread (file, & mFontData .data);
fc.loadFontManaged( mFontData );
// fc now owns mFontData's contents and will free it when fc is destroyed
// You can safely let mFontData fall out of scope
// Also addFontManaged is avaliable for adding fallback fonts Cara pertama:
// creating
int RTw, RTh;
SDL_Texture * RT;
RT = fc.renderTextToTexture ( " Text " , &RTw, &RTh);
// Rendering
SDL_Rect r;
r.x = 5 ;
r.y = 5 ;
r.w = RTw;
r.h = RTh;
SDL_RenderCopy ( mSdlRenderer , RT , NULL , &r); Cara kedua (efek yang sama, tetapi lebih bersih)
// creating
sdl_stb_prerendered_text prt;
prt. mRenderer = your__SDL_Render__instance;
fc.renderTextToObject(&prt, " Text " );
// Rendering
prt.draw(x, y);
// Rendering in colour & alpha
prt.drawWithColor(x, y, 255 , 185 , 85 , 255 );
// Cleanup
prt.freeTexture(); Tip: prt.draw() mengembalikan koordinat x dari ujung objek.
Gunakan SDL_SetTextureColorMod dengan tekstur yang di -cache. Atau gunakan sdl_stb_prerendered_text::drawWithColor .
Gunakan getCaretPos(text, relativeMouseX, relativeMouseY)
Perhatikan bahwa ini hanya saat ini mendukung pencarian Carret di string tanpa baru - jika Anda mencoba ini dengan string multiline maka dapat mengembalikan nilai yang salah.
Lebar tab ditangani oleh variabel fc.tabWidth . Karakter setelah tab akan menyelaraskan ke lokasi tab berikutnya.
Anda dapat mengatur fc.tabWidthInSpaces = X sebelum memanggil fc.loadFont(...) untuk secara otomatis mengatur fc.tabWidth ke beberapa kelipatan lebar ruang. Secara default, font disetel ke 8 ruang lebar. Nilai yang lebih rendah lebih baik untuk font monospace, nilai yang lebih tinggi lebih baik untuk font non-monospace.
Perpustakaan ini terdiri dari dua bagian - font penanganan backend (kelas bernama sttfont_* ) dan frontend rendering sdl ( sdl_stb_* ).
Untuk membuat Rendering Frontend Anda sendiri memperluas kelas sttfont_* yang relevan. Lihat implementasi SDL untuk detailnya. ~ 200 baris kode, yang harus Anda lakukan adalah mengambil barang spesifik SDL dan memasukkan barang -barang spesifik renderer Anda. Dalam aplikasi Anda, sertakan sttFont.h alih -alih sdlStbFont.h
Anda dapat menggunakan pregenGlyphs function :
std::vector<sttfont_uint32_t_range> codepoint_ranges;
sttfont_uint32_t_range customRange;
customRange.start = 1337 ; customRange.end = 1444 ; // end is inclusive
sttfont_uint32_t_range::populateRangesLatin (codepoint_ranges); // add the Latin character set
sttfont_uint32_t_range::populateRangesCyrillic (codepoint_ranges); // add the Cyrillic character set
fc.pregenGlyphs(codepoint_ranges, 0 ); // generatess the glypsJika Anda rendering perangkat lunak di SDL ini tidak diperlukan , dan hanya akan memperlambat startup. Jika Anda menggunakan frontend kustom yang menggunakan Atlas Tekstur (seperti BGFX) maka ini disarankan.
Gunakan producerConsumerExample.h . Lihat producerConsumerExample.cpp untuk contoh yang dikerjakan.
Idenya adalah Anda membuat objek producer_consumer_font_cache yang dibagikan antara produsen dan utas konsumen Anda. Objek ini memiliki anggota yang menunjuk ke frontend aktual yang digunakan oleh konsumen:
Initivalising:
producer_consumer_font_cache mPcCache ;
sdl_stb_font_cache mSdlFontCache ;
mPcCache .consumer_font_cache = & mSdlFontCache ;
sttfont_memory m;
m.data = &fontData[ 0 ];
m.size = fontData.size();
m.ownsData = false ;
mPcCache .faceSize = 24 ;
mPcCache .loadFontManagedBoth(m); // Loads the font into both frontendsMenghasilkan:
pcfc_prerendered_text prt;
mPcCache .renderTextToObject(&prt, " Prerendered text from Producer Thread! " ); // prt.handle now holds a handle
pcfc_handle h = mPcCache .pushText( 5 , 5 , " Hello World! " ); // use this instead of "drawText"
mPcCache .submitToConsumer(); // sends to consumerMengkonsumsi:
// <somehow send prt.handle and h to consumer thread>
// Suggestion: use a concurrentqueue (https://github.com/cameron314/concurrentqueue)
// and/or some kind of command buffer (https://github.com/SnapperTT/nanovg_command_buffer)
mPcCache .receiveFromProducer();
mPcCache .dispatchPrerenderJobs<sdl_stb_prerendered_text>(); // takes the prerended text and creates sdl_stb_prerended_text objects (invokes mPcCache.consumer_font_cache->renderTextToObject)
mPcCache .dispatchSinglePrerendered(prt.handle, 5 , 5 ); // actually draws the prerendered text
mPcCache .dispatchSingleText(h); // Renders "hello world" at 5,5Pembersihan:
// Cleanup - just let mPcCache fall out of scope
mPcCache .freeStoredPrerenderedText( true ); // deletes all prerendered text objects stored. true == also calls prt->freeTexture() for all prerendered text
// this is manual destruction as destroying a large number of objects can be expensive, esp. when you want to exit quickly
// Don't forget to delete mPcCache.consumer_font_cache if it was heap allocated
delete mPcCache .consumer_font_cache;UserData: Anda dapat mengirimkan pointer mentah bersama dengan teks Anda.
mPcCahce.pushUserdata(void*); // producer
void* foo = mPcCahce.getUserdata(); // consumer
Jika Anda tidak melakukan ini A std::mutex digunakan untuk melewati status dari produsen ke konsumen. Hanya ada satu slot dalam buffer sementara. Anda harus memiliki beberapa mekanisme untuk berhenti, misalnya, produsen berjalan lebih cepat dari konsumen.
Jika Anda ingin menggunakan antrian, saya merekomendasikan MoodyCamel :: ReaderWriterQueue. Anda dapat mengaktifkannya dengan producer_consumer_font_cache oleh:
#define SSF_CONCURRENT_QUEUE moodycamel::ReaderWriterQueue
Polling untuk Perubahan:
if (mPcCache.receiveFromProducer()) { // wraps moodycamel::ReaderWriterQueue::try_dequeue()
// have dequeued!
}
Pembersihan (siram antrian)
while (mPcCache.receiveFromProducer()) {
// pulls stuff from the queue until its empty
delete mPcCache.getUserdata(); // if we're using heap allocated userdata here is how to clear it
}

Pertama -tama buat sttfont_formatted_text . Contoh di atas dibuat dengan:
sttfont_formatted_text formattedText;
formattedText << sttfont_format::black << " Plain text "
<< sttfont_format:: bold << " bold text "
<< sttfont_format:: italic << " italic text n "
<< sttfont_format:: underline << sttfont_format::green << " underline text t "
<< sttfont_format::strikethrough << " strikethrough text n "
<< sttfont_format::red << sttfont_format:: bold << " red bold t "
<< sttfont_format:: bold << " not red bold t "
<< sttfont_format::red << " red not bold n "
<< sttfont_format:: bold << sttfont_format:: italic << sttfont_format::colour( 255 , 127 , 50 ) << " custom colour t "
<< sttfont_format:: bold << sttfont_format::strikethrough << sttfont_format::colour( 127 , 255 , 50 ) << " bold strikethrough n "
<< sttfont_format:: bold << sttfont_format:: underline << sttfont_format::colour( 0 , 50 , 200 ) << " bold underline t "
<< sttfont_format:: italic << sttfont_format::strikethrough << sttfont_format::colour( 255 , 255 , 50 ) << " italic strikethrough n "
<< sttfont_format:: italic << sttfont_format:: underline << sttfont_format::colour( 127 , 50 , 255 ) << " italic underline " Anda dapat menggabungkan opsi pemformatan dengan << operator. Pemformatan diatur ulang setelah string dimasukkan.
Kemudian Anda dapat membuat menggunakan fungsi yang sama yang digunakan untuk string sederhana ( drawText(x, y, formattedText) , dll). Itu termasuk rendering ke tekstur dengan renderTextToObject . Segala sesuatu yang dapat Anda lakukan dengan string sederhana yang harus Anda lakukan dengan fungsi yang sama ( getTextSize , getNumRows , getCaretPos , dll)
Agar varian tebal/miring untuk bekerja, Anda harus memuat varian font tebal/miring. Untuk tebal+miring Anda harus memuat varian font yang tebal+miring! Underlines dan Strikethroughs dihasilkan secara otomatis oleh perpustakaan ini.
Untuk memuat varian, gunakan addFormatFont atau addFormatFontManaged setelah memuat font dasar. Ini juga harus dilakukan untuk font fallback (untuk dukungan multibahasa) jika Anda peduli dengan font yang tersedia dalam huruf tebal/miring.
fc.loadFontManaged(notoSans); // First font - loadFont
fc.addFormatFontManaged(sttfont_format::FORMAT_BOLD, notoSansBold);
fc.addFormatFontManaged(sttfont_format::FORMAT_ITALIC, notoSansItalic);
fc.addFormatFontManaged(sttfont_format::FORMAT_BOLD | sttfont_format::FORMAT_ITALIC, notoSansBoldItalic);
fc.addFontManaged(notoSansArmenian); // Fallback fonts - addFont
fc.addFormatFontManaged(sttfont_format::FORMAT_BOLD, notoSansArmenianBold);
fc.addFormatFontManaged(sttfont_format::FORMAT_ITALIC, notoSansArmenianItalic);
fc.addFormatFontManaged(sttfont_format::FORMAT_BOLD | sttfont_format::FORMAT_ITALIC, notoSansArmenianBoldItalic);Jika Anda meminta string yang tebal atau miring dan tidak ada varian yang berani atau miring yang tersedia, varian reguler akan digunakan. Jika Anda meminta string bold+miring dan hanya ada satu dimuat (tetapi bukan kombinasi) varian yang terakhir dimuat akan digunakan - jadi jika Anda meminta huruf tebal+miring dan Anda telah dimuat dengan huruf tebal maka miring (tetapi tidak tebal+miring) maka miring akan diterjemahkan.
Perpustakaan akan menarik garis bawah dan varian yang menyerang itu sendiri, Anda tidak perlu menyediakannya.
Sertakan bgfxFrontend.h dan buat instance bgfx_stb_font_cache .
Lihat bgfxExample.cpp untuk melihat cara menggunakan frontend ini.
Beberapa catatan:
sttfont_uint32_t_range::populateRangesLatin() dan mirip dengan mengisi atlas sebelum rendering. Jika atlas diisi dengan mesin terbang maka frontend akan membuat halaman Atlas baru. Pastikan untuk membuat/mengedit file .lzz , bukan file .h yang dihasilkan. Alat + skrip untuk membuat file header dari sumber .lzz disediakan.
Berkat kontribitor untuk proyek SDL dan STB!
Domain publik
STB_Truetype adalah domain publik
Noto font adalah (c) Google dan dirilis di bawah lisensi SIL Open Font, versi 1.1. Lihat https://www.google.com/get/noto/