مكتبة رأس فقط لتقديم النص في SDL2 النقي مع STB_TRUETYPE. هذه المخابئين تختبئ لأنها مرسومة مما يسمح بتقديم النص السريع. كما يوفر عدة طرق سهلة لتقديم سلسلة إلى نسيج حتى عرض نص أسرع.
جديد (2022)! يمكن prerender في enviroments multitheddergended مع producerConsumerFrontend.h !
جديد (2021)! يمكن أيضًا تقديمها في BGFX مع bgfxFrontend.h !

نموذج النص من http://www.columbia.edu/~fdc/utf8/index.html

Liam Twigger - snapperthetwig
fc.drawText(x, y, string)fc.renderTextToTexture(string, &widthOut, &heightOut)على Intel I7-8750H:
مثال على الصورة يستغرق 0.7 مللي ثانية لتقديم (~ 1400 إطار في الثانية) إذا كان باستخدام نسيج ( renderTextToTexture / renderTextToObject ). ستكون السرعة هي الحد الأقصى الذي يتيح لك SDL2 قلبه.
مثال على الصورة ~ 5 مللي ثانية لتقديم (~ 200 إطار في الثانية) إذا تم تقديمها مباشرة ( drawText )
بالنسبة للنص الذي يستمر أكثر من إطار واحد ، يجب عليك تخزينه إما باستخدام renderTextToTexture أو renderTextToObject .
النص المنسق:
الجبهة غير SDL:
هذه مكتبة رأس فقط - لا يوجد نظام بناء مطلوب
في أي رأس:
# include " path/to/sdlStbFont/sdlStbFont.h "في واحد .CPP:
# define SDL_STB_FONT_IMPL
# include " path/to/sdlStbFont/sdlStbFont.h " هذه المكتبة لديها اعتماد على STB_TRUETYPE. وسوف تشمل هذا تلقائيا. إذا كنت لا تريد تضمينه تلقائيًا ، فاستخدم #define STB_TRUETYPE_INCLUDE_HANDLED ، والتعامل مع STB_TRUETYPE.H بنفسك.
# 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.يمكنك أيضًا الحصول على عرض وارتفاع السلسلة المقدمة:
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
يجب أن يكون الخط الأول المحمّل مع "loadfont".
لاحظ أنه يجب تحميل جميع الخطوط قبل استدعاء أي وظائف رسم. إذا لم يتم العثور على الرسول الرمزية في الخط الأول ، فسيتم البحث في الخط الثاني ، إلخ.
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 نصيحة: drawText() إرجاع إحداثيات X لنهاية السلسلة المرسومة أو كائن نص منسق.
لالتفاف الخط ، استخدم وظيفة fc.breakString(stringIn, arrayOut, width) . هذا يحول سلسلة أو كائن نص منسق إلى متجه من السلاسل أو كائنات نصية منسقة كل منها أقل من العرض. سيحاول هذا الانفصال عن الخطوط الجديدة والمساحة البيضاء أولاً ، ثم ستقسم الكلمات إذا كانت أطول من العرض المحدد. هذا هو UTF8 آمن.
الإدارة اليدوية:
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 behaviourالإدارة التلقائية:
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 أول طريقة:
// 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); الطريقة الثانية (نفس التأثير ، ولكن أنظف)
// 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(); نصيحة: prt.draw() إرجاع الإحداثيات x لنهاية الكائن.
استخدم SDL_SetTextureColorMod مع نسيج مخبأة. أو استخدم sdl_stb_prerendered_text::drawWithColor .
استخدم getCaretPos(text, relativeMouseX, relativeMouseY)
لاحظ أن هذا يدعم حاليًا فقط البحث في الأوتار بدون خطوط جديدة - إذا حاولت ذلك بسلسلة متعددة ، فقد يعيد قيمة غير صحيحة.
تتم معالجة عرض علامة التبويب بواسطة متغير fc.tabWidth . سيتم محاذاة الأحرف بعد علامة التبويب مع موقع علامة التبويب التالية.
يمكنك تعيين fc.tabWidthInSpaces = X قبل استدعاء fc.loadFont(...) لتعيين fc.tabWidth تلقائيًا على بعض مضاعف عرض المساحة. بشكل افتراضي يتم تعيين الخطوط على 8 مسافات في العرض. تعد القيم المنخفضة أفضل بالنسبة لخطوط Monospace ، والقيم الأعلى أفضل بالنسبة للخطوط غير المونوسباس.
تتكون هذه المكتبة من جزأين - خلفية معالجة الخط (الفصول المسماة sttfont_* ) و SDL Rendering Frontend ( sdl_stb_* ).
لجعل الواجهة الأمامية الخاصة بك تقديم فئات sttfont_* ذات الصلة. انظر تطبيق SDL للحصول على التفاصيل. ~ 200 خط من الكود ، كل ما عليك فعله هو إخراج الأشياء المحددة SDL ووضعها في الأشياء الخاصة بالعارض. في التطبيق الخاص بك ، قم بتضمين sttFont.h بدلاً من sdlStbFont.h
يمكنك استخدام 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 glypsإذا كنت تقدم برامج في SDL ، فهذا ليس من الضروري ، وسوف يتباطأ بدء التشغيل. إذا كنت تستخدم الواجهة الأمامية المخصصة التي تستخدم أطلس الملمس (مثل BGFX) ، يوصى بذلك.
استخدام producerConsumerExample.h . انظر producerConsumerExample.cpp للحصول على مثال عمل.
والفكرة هي أنك تقوم بتثبيت كائن producer_consumer_font_cache الذي يتم مشاركته بين منتجك ومواضيع المستهلك. يحتوي هذا الكائن على عضو يشير إلى الواجهة الفعلية التي يستخدمها المستهلك:
initalising:
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 frontendsإنتاج:
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 consumerالاستهلاك:
// <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,5تنظيف:
// 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: يمكنك إرسال مؤشر RAW مع النص الخاص بك.
mPcCahce.pushUserdata(void*); // producer
void* foo = mPcCahce.getUserdata(); // consumer
إذا لم تفعل هذا std::mutex لتمرير الحالة من المنتج إلى المستهلك. لا يوجد سوى فتحة واحدة في المخزن المؤقت المؤقت. يجب أن يكون لديك بعض الميكانيكية للتوقف ، على سبيل المثال ، المنتج يعمل بشكل أسرع من المستهلك.
إذا كنت ترغب في استخدام قائمة انتظار أوصي MoodyCamel :: ReaderWriterqueue. يمكنك تمكينه باستخدام producer_consumer_font_cache بواسطة:
#define SSF_CONCURRENT_QUEUE moodycamel::ReaderWriterQueue
الاقتراع من أجل التغييرات:
if (mPcCache.receiveFromProducer()) { // wraps moodycamel::ReaderWriterQueue::try_dequeue()
// have dequeued!
}
التنظيف (قم بتدفق قائمة الانتظار)
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
}

أولا قم بإنشاء sttfont_formatted_text . تم إنشاء المثال أعلاه مع:
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 " يمكنك الجمع بين خيارات التنسيق مع << عامل. تتم إعادة تعيين التنسيق بعد إدخال سلسلة.
ثم يمكنك تقديم استخدام نفس الوظائف المستخدمة في سلاسل بسيطة ( drawText(x, y, formattedText) ، إلخ). ويشمل ذلك تقديم الملمس مع renderTextToObject . كل ما يمكنك القيام به بسلسلة بسيطة يجب أن تكون قادرًا على القيام به مع الوظيفة ذات الاسم نفسه ( getTextSize ، getNumRows ، getCaretPos ، إلخ)
بالنسبة للمتغيرات الجريئة/المائلة للعمل ، يجب تحميل متغير جريء/مائل للخط. بالنسبة إلى Bold+Italic ، يجب تحميل متغير Bold+Italic للخط! يتم إنشاء السلة والضربات تلقائيًا بواسطة هذه المكتبة.
لتحميل متغير ، استخدم addFormatFont أو addFormatFontManaged بعد تحميل خط أساسي. يجب أيضًا القيام بذلك من أجل خطوط احتياطية (للحصول على الدعم متعدد اللغات) إذا كنت تهتم بتلك الخطوط متاحة في Bold/Mitalic.
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);إذا طلبت سلسلة غامقة أو مائلة ولم يكن هناك متغير جريء أو مائل ، فسيتم استخدام المتغير العادي. إذا طلبت سلسلة Bold+Italic ولم يتم استخدام واحدة فقط (ولكن ليس المزيج) ، فسيتم استخدام آخر متغير تم تحميله - لذلك إذا طلبت Bold+Italic وكنت قد قمت بتحميله بشكل جريء ثم مائل (ولكن ليس Bold+Italic) ، فسيتم تقديم مائل.
ستقوم المكتبة برسم المتغيرات السطحية وتضربها نفسها ، فأنت لست بحاجة إلى تقديم هذه المتغيرات.
قم بتضمين bgfxFrontend.h وإنشاء مثيل من bgfx_stb_font_cache .
انظر bgfxExample.cpp لمعرفة كيفية استخدام هذه الواجهة الأمامية.
بعض الملاحظات:
sttfont_uint32_t_range::populateRangesLatin() وتشبه ملء الأطلس قبل التقديم. إذا تم ملء أطلس بالحبوب ، فستقوم الواجهة الأمامية بإنشاء صفحة أطلس جديدة. تأكد من إنشاء/تحرير ملفات .lzz ، وليس ملفات .h التي تم إنشاؤها. يتم توفير الأدوات + البرامج النصية لإنشاء ملفات الرأس من مصادر .lzz .
بفضل المساهمين لكل من مشاريع SDL و STB!
المجال العام
STB_TRUETYPE هو المجال العام
Noto Fonts هي (ج) Google ويتم إصدارها تحت ترخيص SIL Open Font ، الإصدار 1.1. راجع https://www.google.com/get/noto/