LCOV - code coverage report
Current view: top level - engine/render - rendertext.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 74 125 59.2 %
Date: 2024-11-22 05:07:59 Functions: 10 19 52.6 %

          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 1.14