LCOV - code coverage report
Current view: top level - engine/render - rendertext.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 59.2 % 125 74
Test Date: 2025-02-21 06:59:27 Functions: 52.6 % 19 10

            Line data    Source code
       1              : /* rendertext.cpp: core text functionality for console/ui rendering
       2              :  *
       3              :  * libprimis uses a font image generated by the `tessfont` utility which has
       4              :  * letters sampled from it by reading small sub-rectangles of the overall font
       5              :  * image
       6              :  *
       7              :  * rendertext supports loading, recoloring, resizing, and rendering text to the
       8              :  * screen from these font images, which contain raster images of the font to be
       9              :  * rendered
      10              :  *
      11              :  * only a single font can be used at a time, though it can be scaled as needed
      12              :  * for various different purposes in UIs (e.g. titles vs body text)
      13              :  *
      14              :  * fonts are generated by the external `tessfont` utility and consist of a
      15              :  * raster image containing the acceptable characters which are sampled as needed
      16              :  */
      17              : #include "../libprimis-headers/cube.h"
      18              : #include "../../shared/geomexts.h"
      19              : #include "../../shared/glemu.h"
      20              : #include "../../shared/glexts.h"
      21              : 
      22              : #include "rendergl.h"
      23              : #include "rendertext.h"
      24              : #include "shaderparam.h"
      25              : #include "texture.h"
      26              : 
      27              : #include "interface/control.h"
      28              : 
      29              : static std::unordered_map<std::string, font> fonts;
      30              : static font *fontdef = nullptr;
      31              : static int fontdeftex = 0;
      32              : 
      33              : font *curfont = nullptr;
      34              : 
      35              : //adds a new font to the hashnameset "fonts" given the parameters passed
      36            2 : static void newfont(char *name, char *tex, int *defaultw, int *defaulth, int *scale)
      37              : {
      38            2 :     auto insert = fonts.insert( {name, font()} ).first;
      39            2 :     font *f = &((*insert).second);
      40            2 :     f->name = std::string(name);
      41            2 :     f->texs.clear();
      42            2 :     f->texs.push_back(textureload(tex));
      43            2 :     f->chars.clear();
      44            2 :     f->charoffset = '!';
      45            2 :     f->defaultw = *defaultw;
      46            2 :     f->defaulth = *defaulth;
      47            2 :     f->scale = *scale > 0 ? *scale : f->defaulth;
      48            2 :     f->bordermin = 0.49f;
      49            2 :     f->bordermax = 0.5f;
      50            2 :     f->outlinemin = -1;
      51            2 :     f->outlinemax = 0;
      52              : 
      53            2 :     fontdef = f;
      54            2 :     fontdeftex = 0;
      55            2 : }
      56              : 
      57              : //sets the fontdef gvar's bordermin/max to the values passed
      58            1 : static void fontborder(float *bordermin, float *bordermax)
      59              : {
      60            1 :     if(!fontdef)
      61              :     {
      62            0 :         return;
      63              :     }
      64            1 :     fontdef->bordermin = *bordermin;
      65            1 :     fontdef->bordermax = std::max(*bordermax, *bordermin+0.01f);
      66              : }
      67              : 
      68              : //sets the fontdef gvar's outlinemin/max to the values passed
      69            1 : static void fontoutline(float *outlinemin, float *outlinemax)
      70              : {
      71            1 :     if(!fontdef)
      72              :     {
      73            0 :         return;
      74              :     }
      75            1 :     fontdef->outlinemin = std::min(*outlinemin, *outlinemax-0.01f);
      76            1 :     fontdef->outlinemax = *outlinemax;
      77              : }
      78              : 
      79              : /* fontoffset
      80              :  * sets the character offset for the currently loaded font
      81              :  *
      82              :  * Arguments:
      83              :  *    c: a pointer to a char * array representing the character to be offset to
      84              :  *
      85              :  * Only the first element of the array is accepted, all others are ignored
      86              :  *
      87              :  */
      88            1 : static void fontoffset(char *c)
      89              : {
      90            1 :     if(!fontdef)
      91              :     {
      92            0 :         return;
      93              :     }
      94            1 :     fontdef->charoffset = c[0];
      95              : }
      96              : 
      97              : /* fontscale
      98              :  * sets the global scale for fonts
      99              :  *
     100              :  * Arguments:
     101              :  *    scale: a pointer to an integer representing the new scale to set the font to
     102              :  *
     103              :  * If the scale parameter points to the value 0, the font scale is et to its default value.
     104              :  *
     105              :  */
     106            1 : static void fontscale(int *scale)
     107              : {
     108            1 :     if(!fontdef)
     109              :     {
     110            0 :         return;
     111              :     }
     112              : 
     113            1 : fontdef->scale = *scale > 0 ? *scale : fontdef->defaulth;
     114              : }
     115              : 
     116              : 
     117              : /* fonttex
     118              :  * adds a texture for fonts to be loaded from
     119              :  *
     120              :  * Arguments:
     121              :  *    s: a pointer to a char * array representing the path to a font texture file
     122              :  */
     123            1 : static void fonttex(char *s)
     124              : {
     125            1 :     if(!fontdef)
     126              :     {
     127            1 :         return;
     128              :     }
     129            1 :     Texture *t = textureload(s);
     130            1 :     for(uint i = 0; i < fontdef->texs.size(); i++)
     131              :     {
     132            1 :         if(fontdef->texs[i] == t)
     133              :         {
     134            1 :             fontdeftex = i;
     135            1 :             return;
     136              :         }
     137              :     }
     138            0 :     fontdeftex = fontdef->texs.size();
     139            0 :     fontdef->texs.push_back(t);
     140              : }
     141              : 
     142              : /* fontchar
     143              :  * adds an entry to the fontdef vector
     144              :  * sets the new entry in the vector to have the parameters passed
     145              :  *
     146              :  */
     147            1 : static void fontchar(float *x, float *y, float *w, float *h, float *offsetx, float *offsety, float *advance)
     148              : {
     149            1 :     if(!fontdef)
     150              :     {
     151            0 :         return;
     152              :     }
     153            1 :     fontdef->chars.emplace_back();
     154            1 :     font::charinfo &c = fontdef->chars.back();
     155            1 :     c.x = *x;
     156            1 :     c.y = *y;
     157            1 :     c.w = *w ? *w : fontdef->defaultw;
     158            1 :     c.h = *h ? *h : fontdef->defaulth;
     159            1 :     c.offsetx = *offsetx;
     160            1 :     c.offsety = *offsety;
     161            1 :     c.advance = *advance ? *advance : c.offsetx + c.w;
     162            1 :     c.tex = fontdeftex;
     163              : }
     164              : 
     165              : /* fontskip
     166              :  * addes an entry to the fontdef vector, which is empty
     167              :  */
     168            1 : static void fontskip(int *n)
     169              : {
     170            1 :     if(!fontdef)
     171              :     {
     172            0 :         return;
     173              :     }
     174            2 :     for(int i = 0; i < std::max(*n, 1); ++i)
     175              :     {
     176            1 :         fontdef->chars.emplace_back();
     177            1 :         font::charinfo &c = fontdef->chars.back();
     178            1 :         c.x = c.y = c.w = c.h = c.offsetx = c.offsety = c.advance = 0;
     179            1 :         c.tex = 0;
     180              :     }
     181              : }
     182              : 
     183            1 : bool setfont(const char *name)
     184              : {
     185            1 :     auto itr = fonts.find(name);
     186            1 :     if(itr == fonts.end())
     187              :     {
     188            0 :         return false;
     189              :     }
     190            1 :     curfont = &(*itr).second;
     191            1 :     return true;
     192              : }
     193              : 
     194              : static std::stack<font *> fontstack;
     195              : 
     196            0 : void pushfont()
     197              : {
     198            0 :     fontstack.push(curfont);
     199            0 : }
     200              : 
     201            0 : bool popfont()
     202              : {
     203            0 :     if(fontstack.empty())
     204              :     {
     205            0 :         return false;
     206              :     }
     207            0 :     curfont = fontstack.top();
     208            0 :     fontstack.pop();
     209            0 :     return true;
     210              : }
     211              : 
     212            0 : void gettextres(int &w, int &h)
     213              : {
     214            0 :     if(w < minreswidth || h < minresheight)
     215              :     {
     216            0 :         if(minreswidth > w*minresheight/h)
     217              :         {
     218            0 :             h = h*minreswidth/w;
     219            0 :             w = minreswidth;
     220              :         }
     221              :         else
     222              :         {
     223            0 :             w = w*minresheight/h;
     224            0 :             h = minresheight;
     225              :         }
     226              :     }
     227            0 : }
     228              : 
     229            0 : float text_widthf(const char *str)
     230              : {
     231              :     float width, height;
     232            0 :     text_boundsf(str, width, height);
     233            0 :     return width;
     234              : }
     235              : 
     236            0 : static int texttab(float x)
     237              : {
     238            0 :     return (static_cast<int>((x)/(4*fontwidth()))+1.0f)*(4*fontwidth());
     239              : }
     240              : 
     241              : float textscale = 1;
     242              : 
     243              : #define TEXTSKELETON \
     244              :     float y = 0, \
     245              :           x = 0, \
     246              :           scale = curfont->scale/static_cast<float>(curfont->defaulth);\
     247              :     int i;\
     248              :     for(i = 0; str[i]; i++)\
     249              :     {\
     250              :         TEXTINDEX(i) /*textindex *must* be defined before runtime, it is not defined above here*/ \
     251              :         int c = static_cast<uchar>(str[i]);\
     252              :         if(c=='\t')\
     253              :         {\
     254              :             x = texttab(x);\
     255              :             TEXTWHITE(i)\
     256              :         }\
     257              :         else if(c==' ') \
     258              :         { \
     259              :             x += scale*curfont->defaultw; \
     260              :             TEXTWHITE(i) /*textwhite *must* be defined before runtime, it is not defined above here*/ \
     261              :         }\
     262              :         else if(c=='\n') \
     263              :         { \
     264              :             TEXTLINE(i) x = 0; \
     265              :             y += FONTH; \
     266              :         }\
     267              :         else if(c=='\f') \
     268              :         { \
     269              :             if(str[i+1]) \
     270              :             { \
     271              :                 i++; \
     272              :                 TEXTCOLOR(i) /*textcolor *must* be defined before runtime, it is not defined above here*/ \
     273              :             } \
     274              :         }\
     275              :         else if(curfont->chars.size() > static_cast<uint>(c-curfont->charoffset))\
     276              :         {\
     277              :             float cw = scale*curfont->chars[c-curfont->charoffset].advance;\
     278              :             if(cw <= 0) \
     279              :             { \
     280              :                 continue; \
     281              :             } \
     282              :             if(maxwidth >= 0)\
     283              :             {\
     284              :                 int j = i;\
     285              :                 float w = cw;\
     286              :                 for(; str[i+1]; i++)\
     287              :                 {\
     288              :                     int c = static_cast<uchar>(str[i+1]);\
     289              :                     if(c=='\f') \
     290              :                     { \
     291              :                         if(str[i+2]) \
     292              :                         { \
     293              :                             i++; \
     294              :                         } \
     295              :                         continue; \
     296              :                     } \
     297              :                     if(!(curfont->chars.size() > static_cast<uint>(c-curfont->charoffset))) \
     298              :                     { \
     299              :                         break; \
     300              :                     } \
     301              :                     float cw = scale*curfont->chars[c-curfont->charoffset].advance; \
     302              :                     if(cw <= 0 || w + cw > maxwidth) \
     303              :                     { \
     304              :                         break; \
     305              :                     } \
     306              :                     w += cw; \
     307              :                 } \
     308              :                 if(x + w > maxwidth && x > 0) \
     309              :                 { \
     310              :                     static_cast<void>(j); \
     311              :                     TEXTLINE(j-1); \
     312              :                     x = 0; \
     313              :                     y += FONTH; } \
     314              :                 TEXTWORD \
     315              :             } \
     316              :             else \
     317              :             { \
     318              :                 TEXTCHAR(i) \
     319              :             }\
     320              :         }\
     321              :     }
     322              : 
     323              : //all the chars are guaranteed to be either drawable or color commands
     324              : #define TEXTWORDSKELETON \
     325              :     for(; j <= i; j++)\
     326              :     {\
     327              :         TEXTINDEX(j) /*textindex *must* be defined before runtime, it is not defined above here*/ \
     328              :         int c = static_cast<uchar>(str[j]);\
     329              :         if(c=='\f') \
     330              :         { \
     331              :             if(str[j+1]) \
     332              :             { \
     333              :                 j++; \
     334              :                 TEXTCOLOR(j) /*textcolor *must* be defined before runtime, it is not defined above here*/ \
     335              :             } \
     336              :         }\
     337              :         else \
     338              :         { \
     339              :             float cw = scale*curfont->chars[c-curfont->charoffset].advance; \
     340              :             TEXTCHAR(j); \
     341              :         }\
     342              :     }
     343              : 
     344              : #define TEXTEND(cursor) \
     345              :     if(cursor >= i) \
     346              :     { \
     347              :         do \
     348              :         { \
     349              :             TEXTINDEX(cursor); /*textindex *must* be defined before runtime, it is not defined above here*/ \
     350              :         } while(0); \
     351              :     } \
     352              : 
     353            0 : int text_visible(const char *str, float hitx, float hity, int maxwidth)
     354              : {
     355              :     #define TEXTINDEX(idx)
     356              :     #define TEXTWHITE(idx) \
     357              :     { \
     358              :         if(y+FONTH > hity && x >= hitx) \
     359              :         { \
     360              :             return idx; \
     361              :         } \
     362              :     }
     363              :     #define TEXTLINE(idx) \
     364              :     { \
     365              :         if(y+FONTH > hity) \
     366              :         { \
     367              :             return idx; \
     368              :         } \
     369              :     }
     370              :     #define TEXTCOLOR(idx)
     371              :     #define TEXTCHAR(idx) \
     372              :     { \
     373              :         x += cw; \
     374              :         TEXTWHITE(idx) \
     375              :     }
     376              :     #define TEXTWORD TEXTWORDSKELETON
     377            0 :     TEXTSKELETON
     378              :     #undef TEXTINDEX
     379              :     #undef TEXTWHITE
     380              :     #undef TEXTLINE
     381              :     #undef TEXTCOLOR
     382              :     #undef TEXTCHAR
     383              :     #undef TEXTWORD
     384            0 :     return i;
     385              : }
     386              : 
     387              : //inverse of text_visible
     388            0 : void text_posf(const char *str, int cursor, float &cx, float &cy, int maxwidth)
     389              : {
     390              :     #define TEXTINDEX(idx) \
     391              :     { \
     392              :         if(idx == cursor) \
     393              :         { \
     394              :             cx = x; \
     395              :             cy = y; \
     396              :             break; \
     397              :         } \
     398              :     }
     399              :     #define TEXTWHITE(idx)
     400              :     #define TEXTLINE(idx)
     401              :     #define TEXTCOLOR(idx)
     402              :     #define TEXTCHAR(idx) x += cw;
     403              :     #define TEXTWORD TEXTWORDSKELETON if(i >= cursor) break;
     404            0 :     cx = cy = 0;
     405            0 :     TEXTSKELETON
     406            0 :     TEXTEND(cursor)
     407              :     #undef TEXTINDEX
     408              :     #undef TEXTWHITE
     409              :     #undef TEXTLINE
     410              :     #undef TEXTCOLOR
     411              :     #undef TEXTCHAR
     412              :     #undef TEXTWORD
     413            0 : }
     414              : 
     415            0 : void text_boundsf(const char *str, float &width, float &height, int maxwidth)
     416              : {
     417              :     #define TEXTINDEX(idx)
     418              :     #define TEXTWHITE(idx)
     419              :     #define TEXTLINE(idx) if(x > width) width = x;
     420              :     #define TEXTCOLOR(idx)
     421              :     #define TEXTCHAR(idx) x += cw;
     422              :     #define TEXTWORD x += w;
     423            0 :     width = 0;
     424            0 :     TEXTSKELETON
     425            0 :     height = y + FONTH;
     426            0 :     TEXTLINE(_)
     427              :     #undef TEXTINDEX
     428              :     #undef TEXTWHITE
     429              :     #undef TEXTLINE
     430              :     #undef TEXTCOLOR
     431              :     #undef TEXTCHAR
     432              :     #undef TEXTWORD
     433            0 : }
     434              : 
     435            0 : void reloadfonts()
     436              : {
     437            0 :     for(auto &[k, f] : fonts)
     438              :     {
     439            0 :         for(uint i = 0; i < f.texs.size(); i++)
     440              :         {
     441            0 :             if(!f.texs[i]->reload())
     442              :             {
     443            0 :                 fatal("failed to reload font texture");
     444              :             }
     445              :         }
     446              :     }
     447            0 : }
     448              : 
     449            1 : void initrendertextcmds()
     450              : {
     451            1 :     addcommand("font", reinterpret_cast<identfun>(newfont), "ssiii", Id_Command);
     452            1 :     addcommand("fontborder", reinterpret_cast<identfun>(fontborder), "ff", Id_Command);
     453            1 :     addcommand("fontoutline", reinterpret_cast<identfun>(fontoutline), "ff", Id_Command);
     454            1 :     addcommand("fontoffset", reinterpret_cast<identfun>(fontoffset), "s", Id_Command);
     455            1 :     addcommand("fontscale", reinterpret_cast<identfun>(fontscale), "i", Id_Command);
     456            1 :     addcommand("fonttex", reinterpret_cast<identfun>(fonttex), "s", Id_Command);
     457            1 :     addcommand("fontchar", reinterpret_cast<identfun>(fontchar), "fffffff", Id_Command);
     458            1 :     addcommand("fontskip", reinterpret_cast<identfun>(fontskip), "i", Id_Command);
     459            1 : }
        

Generated by: LCOV version 2.0-1