1 // rendertext.cpp: font rendering
2 
3 #include "cube.h"
4 
5 int VIRTW;
6 bool ignoreblinkingbit = false; // for remote-n-temp override of '\fb'
7 static hashtable<const char *, font> fonts;
8 static font *fontdef = NULL;
9 
10 font *curfont = NULL;
11 
12 VARP(allowblinkingtext, 0, 0, 1); // if you're so inclined
13 VARP(__fontsetting, 0, 0, 2);
14 
newfont(char * name,char * tex,int * defaultw,int * defaulth,int * offsetx,int * offsety,int * offsetw,int * offseth)15 void newfont(char *name, char *tex, int *defaultw, int *defaulth, int *offsetx, int *offsety, int *offsetw, int *offseth)
16 {
17     font *f = fonts.access(name);
18     if(!f)
19     {
20         name = newstring(name);
21         f = &fonts[name];
22         f->name = name;
23     }
24 
25     f->tex = textureload(tex);
26     f->chars.shrink(0);
27     f->defaultw = *defaultw;
28     f->defaulth = *defaulth;
29     f->offsetx = *offsetx;
30     f->offsety = *offsety;
31     f->offsetw = *offsetw;
32     f->offseth = *offseth;
33     f->skip = 33;
34 
35     fontdef = f;
36 }
37 
38 extern GLenum texformat(int bpp);
39 
fontchar(int * x,int * y,int * w,int * h)40 void fontchar(int *x, int *y, int *w, int *h)
41 {
42     if(!fontdef) return;
43 
44     font::charinfo &c = fontdef->chars.add();
45     c.x = *x;
46     c.y = *y;
47     c.w = *w ? *w : fontdef->defaultw;
48     c.h = *h ? *h : fontdef->defaulth;
49 }
50 
fontskip(int * n)51 void fontskip(int *n)
52 {
53     if(!fontdef) return;
54 
55     fontdef->skip = *n;
56 }
57 
58 COMMANDN(font, newfont, "ssiiiiii");
59 COMMAND(fontchar, "iiii");
60 COMMAND(fontskip, "i");
61 
62 string myfont = "default";
newsetfont(const char * name)63 void newsetfont(const char *name)
64 {
65     if ( setfont(name) ) copystring(myfont,name);
66 }
67 
setfont(const char * name)68 bool setfont(const char *name)
69 {
70     font *f = fonts.access(name);
71     if(!f) return false;
72     int v = -1;
73     if(strcmp(name, "default")==0)
74         v = 0;
75     else if(strcmp(name, "serif")==0)
76         v = 1;
77     else if(strcmp(name, "mono")==0)
78         v = 2;
79     if(v!=-1) __fontsetting = v;
80     curfont = f;
81     return true;
82 }
83 COMMANDN(setfont, newsetfont, "s");
84 
getfont(const char * name)85 font *getfont(const char *name)
86 {
87     return fonts.access(name);
88 }
89 
90 static vector<font *> fontstack;
91 
pushfont(const char * name)92 void pushfont(const char *name)
93 {
94     fontstack.add(curfont);
95     setfont(name);
96 }
97 
popfont()98 void popfont()
99 {
100     if(!fontstack.empty()) curfont = fontstack.pop();
101 }
102 
text_width(const char * str)103 int text_width(const char *str)
104 {
105     int width, height;
106     text_bounds(str, width, height);
107     return width;
108 }
109 
draw_textf(const char * fstr,int left,int top,...)110 void draw_textf(const char *fstr, int left, int top, ...)
111 {
112     defvformatstring(str, top, fstr);
113     draw_text(str, left, top);
114 }
115 
116 
117 
118 extern int shdsize, outline, win32msg;
119 
120 
121 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
122 #define RMASK 0xff000000
123 #define GMASK 0x00ff0000
124 #define BMASK 0x0000ff00
125 #define AMASK 0x000000ff
126 #else
127 #define RMASK 0x000000ff
128 #define GMASK 0x0000ff00
129 #define BMASK 0x00ff0000
130 #define AMASK 0xff000000
131 #endif
132 /*
133 // ringbuf for utf8 character storage
134 struct charringbuf : ringbuf<font::utf8charinfo, 32>
135 {
136     // find by character code
137     int findbycharcode(int code)
138     {
139         loopi(len)
140         {
141             if(data[i].code == code)
142                 return i;
143         }
144 
145         return -1;
146     }
147 };
148 
149 
150 TTF_Font *ttffont = NULL;
151 font utf8font;
152 charringbuf utf8chars;
153 
154 void initfont()
155 {
156     static bool initialized = false;
157     if(!initialized)
158     {
159         TTF_Init();
160 
161         int fsize = 64;
162         const char *fontname = "packages/misc/font.ttf";
163         ttffont = TTF_OpenFont(findfile(path(fontname, true), "r"), fsize);
164 
165         utf8font.defaulth = 0;
166         utf8font.defaultw = 0;
167         utf8font.offsetw = 0;
168         utf8font.offseth = 10;
169         utf8font.offsetx = 0;
170         utf8font.offsety = 0;
171 
172         initialized = true;
173     }
174 }
175 
176 void createutf8charset()
177 {
178     int isize = 512;
179 
180     SDL_Surface *charsetsurface = SDL_CreateRGBSurface(SDL_SWSURFACE, isize, isize, 32, RMASK, GMASK, BMASK, AMASK);
181 
182     if(charsetsurface)
183     {
184         SDL_Color color;
185         color.r = 255;
186         color.g = 255;
187         color.b = 255;
188 
189         int posx = 0;
190         int posy = 0;
191 
192         loopv(utf8chars)
193         {
194             font::utf8charinfo &charinfo = utf8chars[i];
195             int code = charinfo.code;
196 
197             char u[5] = {0,0,0,0,0};
198             utf8::append(code, u);
199 
200             SDL_Surface *fontsurface = TTF_RenderUTF8_Blended(ttffont, u, color);
201 
202             // update row/column info
203             if(posx + fontsurface->w > charsetsurface->w)
204             {
205                 posx = 0;
206                 posy += fontsurface->h; // fixme
207             }
208             if(posy + fontsurface->h > charsetsurface->h)
209                 break;
210 
211             // blit onto charset
212             SDL_SetAlpha(fontsurface, 0, 0);
213             blitsurface(charsetsurface, fontsurface, posx, posy);
214 
215             // update charinfo properties
216             charinfo.x = posx;
217             charinfo.y = posy;
218             charinfo.w = fontsurface->w;
219             charinfo.h = fontsurface->h;
220 
221             posx += fontsurface->w;
222             SDL_FreeSurface(fontsurface);
223         }
224 
225         utf8font.tex = createtexturefromsurface("utf8charset", charsetsurface);
226 
227         //extern void savepng(SDL_Surface *s, const char *name);
228         //savepng(t, "font.png");
229 
230         SDL_FreeSurface(charsetsurface);
231     }
232 }
233 
234 void addutf8char(int code)
235 {
236     // add to buf
237     font::utf8charinfo charinfo;
238     charinfo.code = code;
239     charinfo.x = charinfo.y = charinfo.w = charinfo.h = 0;
240     utf8chars.add(charinfo);
241 
242     // update charset
243     createutf8charset();
244 }
245 
246 font::charinfo *loadchar(int code)
247 {
248     int idx = utf8chars.findbycharcode(code);
249     if(idx >= 0)
250         return &utf8chars[idx];
251 
252     // add
253     addutf8char(code);
254 
255     idx = utf8chars.findbycharcode(code);
256     return &utf8chars[idx];
257 }
258 */
draw_char(font & f,font::charinfo & info,int charcode,int x,int y)259 int draw_char(font &f, font::charinfo &info, int charcode, int x, int y)
260 {
261 /*
262     // fixme
263     glEnd();
264     glBindTexture(GL_TEXTURE_2D, f.tex->id);
265     glBegin(GL_QUADS);
266 */
267     float tc_left    = (info.x + f.offsetx) / float(f.tex->xs);
268     float tc_top     = (info.y + f.offsety) / float(f.tex->ys);
269     float tc_right   = (info.x + info.w + f.offsetw) / float(f.tex->xs);
270     float tc_bottom  = (info.y + info.h + f.offseth) / float(f.tex->ys);
271 
272     glTexCoord2f(tc_left,  tc_top   ); glVertex2f(x,          y);
273     glTexCoord2f(tc_right, tc_top   ); glVertex2f(x + info.w, y);
274     glTexCoord2f(tc_right, tc_bottom); glVertex2f(x + info.w, y + info.h);
275     glTexCoord2f(tc_left,  tc_bottom); glVertex2f(x,          y + info.h);
276 
277     xtraverts += 4;
278     return info.w;
279 }
280 
281 /*
282 // fixme
283 font::charinfo &getcharinfo(int c)
284 {
285     if(curfont->chars.inrange(c-curfont->skip))
286     {
287         font::charinfo &info = curfont->chars[c-curfont->skip];
288         return info;
289     }
290     //else { font::charinfo &info = *loadchar(c); return info; }
291     //return NULL;
292     font::charinfo &info = curfont->chars[0]; // 0 || (FONTCHARS-1)
293     return info;
294 }
295 */
296 
draw_char(int c,int x,int y)297 static int draw_char(int c, int x, int y)
298 {
299     if(curfont->chars.inrange(c-curfont->skip))
300     {
301         font::charinfo &info = curfont->chars[c-curfont->skip];
302 
303         return draw_char(*curfont, info, c, x, y);
304     }
305     /*
306     else
307     {
308         // fixme
309         glEnd();
310         font::charinfo &info = *loadchar(c);
311         glBegin(GL_QUADS);
312 
313         return draw_char(utf8font, info, c, x, y);
314     }
315     */
316     return 0;
317 }
318 
319 
320 //stack[sp] is current color index
text_color(char c,char * stack,int size,int & sp,bvec color,int a)321 static void text_color(char c, char *stack, int size, int &sp, bvec color, int a)
322 {
323     if(c=='s') // save color
324     {
325         c = stack[sp];
326         if(sp<size-1) stack[++sp] = c;
327     }
328     else
329     {
330         if(c=='r') c = stack[(sp > 0) ? --sp : sp]; // restore color
331         else if(c == 'b') { if(allowblinkingtext && !ignoreblinkingbit) stack[sp] *= -1; } // blinking text - only if allowed
332         else stack[sp] = c;
333         switch(iabs(stack[sp]))
334         {
335             case '0': color = bvec( 2,  255,  128 ); break;   // green: player talk
336             case '1': color = bvec( 96,  160, 255 ); break;   // blue: team chat
337             case '2': color = bvec( 255, 192,  64 ); break;   // yellow: gameplay action messages, only actions done by players - 230 230 20 too bright
338             case '3': color = bvec( 255,  64,  64 ); break;   // red: important errors and notes
339             case '4': color = bvec( 128, 128, 128 ); break;   // gray
340             case '5': color = bvec( 255, 255, 255 ); break;   // white
341             case '6': color = bvec(  96,  48,   0 ); break;   // dark brown
342             case '7': color = bvec( 153,  51,  51 ); break;   // dark red: dead admin
343             case '8': color = bvec( 192,  64, 192 ); break;   // magenta
344             case '9': color = bvec( 255, 102,   0 ); break;   // orange
345             //extendeded color palette
346             //case 'a': case 'A':color = bvec( 0xFF, 0xCC, 0xCC); break;   // some lowercase seem to have special meaning like 'b' (flashing text) so not yet using them
347             case 'A':color = bvec( 0xff, 0xb7, 0xb7); break;   // red set
348             case 'B':color = bvec( 0xCC, 0x33, 0x33); break;   //
349             case 'C':color = bvec( 0x66, 0x33, 0x33); break;   //
350             case 'D':color = bvec( 0xF8, 0x98, 0x4E); break;   //
351 
352             case 'E':color = bvec( 0xFF, 0xFF, 0xB7); break;   // yellow set
353             case 'F':color = bvec( 0xCC, 0xCC, 0x33); break;   //
354             case 'G':color = bvec( 0x66, 0x66, 0x33); break;   //
355             case 'H':color = bvec( 0xCC, 0xFC, 0x58); break;   //
356 
357             case 'I':color = bvec( 0xB7, 0xFF, 0xB7); break;   // green set
358             case 'J':color = bvec( 0x33, 0xCC, 0x33); break;   //
359             case 'K':color = bvec( 0x33, 0x66, 0x33); break;   //
360             case 'L':color = bvec( 0x3F, 0xFF, 0x98); break;   //
361 
362             case 'M':color = bvec( 0xB7, 0xFF, 0xFF); break;   // cyan set
363             case 'N':color = bvec( 0x33, 0xCC, 0xCC); break;   //
364             case 'O':color = bvec( 0x33, 0x66, 0x66); break;   //
365             case 'P':color = bvec( 0x4F, 0xCC, 0xF8); break;   //
366 
367             case 'Q':color = bvec( 0xB7, 0xB7, 0xFF); break;   // blue set
368             case 'R':color = bvec( 0x33, 0x33, 0xCC); break;   //
369             case 'S':color = bvec( 0x33, 0x33, 0x66); break;   //
370             case 'T':color = bvec( 0xA0, 0x49, 0xFF); break;   //
371 
372             case 'U':color = bvec( 0xFF, 0xB7, 0xFF); break;   // magenta set
373             case 'V':color = bvec( 0xCC, 0x33, 0xCC); break;   //
374             case 'W':color = bvec( 0x66, 0x33, 0x66); break;   //
375             case 'X':color = bvec( 0xFF, 0x01, 0xD5); break;   //
376 
377             case 'Y':color = bvec( 0xC7, 0xD1, 0xE2); break;   // lt gray
378             case 'Z':color = bvec( 0x32, 0x32, 0x32); break;   // dark gray
379             // white (provided color): everything else
380             //default: color = bvec( 255, 255, 255 ); break;
381         }
382         int b = (int) (sinf(lastmillis / 200.0f) * 115.0f);
383         b = stack[sp] > 0 ? 100 : min(iabs(b), 100);
384         glColor4ub(color.x, color.y, color.z, (a * b) / 100);
385     }
386 }
387 
388 static vector<int> *columns = NULL;
389 
text_startcolumns()390 void text_startcolumns()
391 {
392     if(!columns) columns = new vector<int>;
393 }
394 
text_endcolumns()395 void text_endcolumns()
396 {
397     DELETEP(columns);
398 }
399 
400 #define TABALIGN(x) ((((x)+PIXELTAB)/PIXELTAB)*PIXELTAB)
401 
402 #define TEXTGETCOLUMN \
403     if(columns && col<columns->length()) \
404     { \
405         colx += (*columns)[col++]; \
406         x = colx; \
407     } \
408     else x = TABALIGN(x);
409 
410 #define TEXTSETCOLUMN \
411     if(columns) \
412     { \
413         while(col>=columns->length()) columns->add(0); \
414         int w = TABALIGN(x) - colx; \
415         w = max(w, (*columns)[col]); \
416         (*columns)[col] = w; \
417         col++; \
418         colx += w; \
419         x = colx; \
420     } \
421     else x = TABALIGN(x);
422 
423 
424 #define TEXTSKELETON \
425     int y = 0, x = 0, col = 0, colx = 0;\
426     int i;\
427     for(i = 0; str[i]; i++)\
428     {\
429         TEXTINDEX(i)\
430         int c = str[i];\
431         if(c=='\t')      { TEXTTAB(i); TEXTWHITE(i) }\
432         else if(c==' ')  { x += curfont->defaultw; TEXTWHITE(i) }\
433         else if(c=='\n') { TEXTLINE(i) x = 0; y += FONTH; }\
434         else if(c=='\f') { if(str[i+1]) { i++; TEXTCOLOR(i) }}\
435         else if(c=='\a') { if(str[i+1]) { i++; }}\
436         else if(curfont->chars.inrange(c-curfont->skip))\
437         {\
438             if(maxwidth != -1)\
439             {\
440                 int j = i;\
441                 int w = curfont->chars[c-curfont->skip].w;\
442                 for(; str[i+1]; i++)\
443                 {\
444                     int c = str[i+1];\
445                     if(c=='\f') { if(str[i+2]) i++; continue; }\
446                     if(i-j > 16) break;\
447                     if(!curfont->chars.inrange(c-curfont->skip)) break;\
448                     int cw = curfont->chars[c-curfont->skip].w + 1;\
449                     if(w + cw >= maxwidth) break;\
450                     w += cw;\
451                 }\
452                 if(x + w >= maxwidth && j!=0) { TEXTLINE(j-1) x = 0; y += FONTH; }\
453                 TEXTWORD\
454             }\
455             else\
456             { TEXTCHAR(i) }\
457         }\
458     }
459 
460 //all the chars are guaranteed to be either drawable or color commands
461 #define TEXTWORDSKELETON \
462                 for(; j <= i; j++)\
463                 {\
464                     TEXTINDEX(j)\
465                     int c = str[j];\
466                     if(c=='\f') { if(str[j+1]) { j++; TEXTCOLOR(j) }}\
467                     else { TEXTCHAR(j) }\
468                 }
469 
text_visible(const char * str,int hitx,int hity,int maxwidth)470 int text_visible(const char *str, int hitx, int hity, int maxwidth)
471 {
472     #define TEXTINDEX(idx)
473     #define TEXTTAB(idx) TEXTGETCOLUMN
474     #define TEXTWHITE(idx) if(y+FONTH > hity && x >= hitx) return idx;
475     #define TEXTLINE(idx) if(y+FONTH > hity) return idx;
476     #define TEXTCOLOR(idx)
477     #define TEXTCHAR(idx) x += curfont->chars[c-curfont->skip].w+1; TEXTWHITE(idx)
478     #define TEXTWORD TEXTWORDSKELETON
479     TEXTSKELETON
480     #undef TEXTINDEX
481     #undef TEXTTAB
482     #undef TEXTWHITE
483     #undef TEXTLINE
484     #undef TEXTCOLOR
485     #undef TEXTCHAR
486     #undef TEXTWORD
487     return i;
488 }
489 
490 //inverse of text_visible
text_pos(const char * str,int cursor,int & cx,int & cy,int maxwidth)491 void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth)
492 {
493     #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; break; }
494     #define TEXTTAB(idx) TEXTGETCOLUMN
495     #define TEXTWHITE(idx)
496     #define TEXTLINE(idx)
497     #define TEXTCOLOR(idx)
498     #define TEXTCHAR(idx) x += curfont->chars[c-curfont->skip].w + 1;
499     #define TEXTWORD TEXTWORDSKELETON if(i >= cursor) break;
500     cx = INT_MIN;
501     cy = 0;
502     TEXTSKELETON
503     if(cx == INT_MIN) { cx = x; cy = y; }
504     #undef TEXTINDEX
505     #undef TEXTTAB
506     #undef TEXTWHITE
507     #undef TEXTLINE
508     #undef TEXTCOLOR
509     #undef TEXTCHAR
510     #undef TEXTWORD
511 }
512 
text_bounds(const char * str,int & width,int & height,int maxwidth)513 void text_bounds(const char *str, int &width, int &height, int maxwidth)
514 {
515     #define TEXTINDEX(idx)
516     #define TEXTTAB(idx) TEXTSETCOLUMN
517     #define TEXTWHITE(idx)
518     #define TEXTLINE(idx) if(x > width) width = x;
519     #define TEXTCOLOR(idx)
520     #define TEXTCHAR(idx) x += curfont->chars[c-curfont->skip].w + 1;
521     #define TEXTWORD x += w + 1;
522     width = 0;
523     TEXTSKELETON
524     height = y + FONTH;
525     TEXTLINE(_)
526     #undef TEXTINDEX
527     #undef TEXTTAB
528     #undef TEXTWHITE
529     #undef TEXTLINE
530     #undef TEXTCOLOR
531     #undef TEXTCHAR
532     #undef TEXTWORD
533 }
534 
535 /** This is the 1.0.4 function
536     It will substituted by draw_text_wip
537     I am putting this temporarily here because it is impossible to test without colours : Brahma */
draw_text(const char * str,int left,int top,int r,int g,int b,int a,int cursor,int maxwidth)538 void draw_text(const char *str, int left, int top, int r, int g, int b, int a, int cursor, int maxwidth)
539 {
540 #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; cc = str[idx]; }
541 #define TEXTTAB(idx) TEXTGETCOLUMN
542 #define TEXTWHITE(idx)
543 #define TEXTLINE(idx)
544 #define TEXTCOLOR(idx) text_color(str[idx], colorstack, sizeof(colorstack), colorpos, color, a);
545 #define TEXTCHAR(idx) x += draw_char(c, left+x, top+y)+1;
546 #define TEXTWORD TEXTWORDSKELETON
547     char colorstack[10];
548     bvec color(r, g, b);
549     int colorpos = 0, cx = INT_MIN, cy = 0, cc = ' ';
550     colorstack[0] = 'c'; //indicate user color
551     glBlendFunc(GL_SRC_ALPHA, curfont->tex->bpp==32 ? GL_ONE_MINUS_SRC_ALPHA : GL_ONE);
552     glBindTexture(GL_TEXTURE_2D, curfont->tex->id);
553     glBegin(GL_QUADS);
554     glColor4ub(color.x, color.y, color.z, a);
555     TEXTSKELETON
556             glEnd();
557     if(cursor >= 0)
558     {
559         if(cx == INT_MIN) { cx = x; cy = y; }
560         if(maxwidth != -1 && cx >= maxwidth) { cx = 0; cy += FONTH; }
561         int cw = curfont->chars.inrange(cc-33) ? curfont->chars[cc-33].w + 1 : curfont->defaultw;
562         rendercursor(left+cx, top+cy, cw);
563     }
564 #undef TEXTINDEX
565 #undef TEXTTAB
566 #undef TEXTWHITE
567 #undef TEXTLINE
568 #undef TEXTCOLOR
569 #undef TEXTCHAR
570 #undef TEXTWORD
571 }
572 
573 /* WIP ALERT */
574 /*
575 void draw_text_wip(const char *str, int left, int top, int r, int g, int b, int a, int cursor, int maxwidth)
576 {
577     char colorstack[10];
578     bvec color(r, g, b);
579     int colorpos = 0, cx = INT_MIN, cy = 0, cc = ' ';
580     colorstack[0] = 'c'; //indicate user color
581 
582     glBlendFunc(GL_SRC_ALPHA, curfont->tex->bpp==32 ? GL_ONE_MINUS_SRC_ALPHA : GL_ONE);
583     glBindTexture(GL_TEXTURE_2D, curfont->tex->id);
584 
585     glBegin(GL_QUADS);
586     glColor4ub(color.x, color.y, color.z, a);
587 
588     std::string text(str);
589     std::string::iterator begin = text.begin();
590     std::string::iterator end = text.end();
591     std::string::iterator cursoriter = end;
592     if(cursor >= 0 && cursor < utf8::distance(begin, end))
593     {
594         cursoriter = begin;
595         utf8::advance(cursoriter, cursor, end);
596     }
597 
598     int y = 0, x = 0, col = 0, colx = 0;
599 
600     for(std::string::iterator iter = text.begin(); iter != text.end(); utf8::next(iter, text.end()))
601     {
602         int c = utf8::peek_next(iter, text.end());
603 
604         if(iter == cursoriter)
605         {
606             cx = x;
607             cy = y;
608             cc = c;
609         }
610 
611         if(c=='\t')
612         {
613             if(columns && col<columns->length())
614             {
615                 colx += (*columns)[col++];
616                 x = colx;
617             }
618             else x = TABALIGN(x);
619         }
620         else if(c==' ')
621         {
622             x += curfont->defaultw;
623         }
624         else if(c=='\n')
625         {
626             x = 0;
627             y += FONTH;
628         }
629         else if(c=='\f')
630         {
631             std::string::iterator test = iter;
632             test++;
633             if(test != end)
634             {
635                 c = utf8::next(iter, end);
636                 text_color(c, colorstack, sizeof(colorstack), colorpos, color, a);
637             }
638         }
639         else if(c=='\a')
640         {
641             std::string::iterator next = iter;
642             next++;
643             if(next != end)
644             {
645                 iter++;
646             }
647 
648         }
649         else if(curfont->chars.inrange(c-curfont->skip))
650         {
651             font::charinfo &cinfo = getcharinfo(c);
652 
653             if(maxwidth != -1)
654             {
655                 std::string::iterator next = iter;
656                 int w = cinfo.w;
657 
658                 do
659                 {
660                     std::string::iterator test = iter;
661                     int c = utf8::next(test, end);
662                     if(test == end) break;
663 
664                     if(c=='\f')
665                     {
666                         std::string::iterator test = iter;
667                         utf8::advance(test, 2, end);
668                         if(test == end) break;
669                         utf8::next(iter, end);
670                         continue;
671                     }
672                     if(utf8::distance(iter, next) > 16) break;
673                     //if(!curfont->chars.inrange(c-curfont->skip)) fixme
674                     if(c < curfont->skip) // fixme
675                     {
676                         break;
677                     }
678                     int cw = getcharinfo(c).w + 1;
679                     if(w + cw >= maxwidth) break;
680                     w += cw;
681 
682                     utf8::next(iter, end);
683 
684                 } while(true);
685 
686                 if(x + w >= maxwidth && next != begin) //fixme
687                 {
688                     x = 0;
689                     y += FONTH;
690                 }
691 
692                 for(; next <= iter && next != end; )
693                 {
694                     int c = utf8::peek_next(next, end);
695                     if(next == cursoriter) { cx = x; cy = y; cc = c; }
696 
697                     if(c=='\f')
698                     {
699                         std::string::iterator test = next;
700                         utf8::next(test, end);
701                         if(test != end)
702                         {
703                             c = utf8::next(next, end);
704                             text_color(c, colorstack, sizeof(colorstack), colorpos, color, a);
705                         }
706                     }
707                     else
708                     {
709                         x += draw_char(c, left+x, top+y)+1;
710                     }
711 
712                     utf8::next(next, end);
713                 }
714 
715             }
716             else
717             {
718                 x += draw_char(c, left+x, top+y)+1;
719             }
720         }
721     }
722 
723     glEnd();
724     if(cursor >= 0)
725     {
726         if(cx == INT_MIN) { cx = x; cy = y; }
727         if(maxwidth != -1 && cx >= maxwidth) { cx = 0; cy += FONTH; }
728         int cw = curfont->chars.inrange(cc-curfont->skip) ? curfont->chars[cc-curfont->skip].w + 1 : curfont->defaultw;
729         rendercursor(left+cx, top+cy, cw);
730     }
731 }
732 */
reloadfonts()733 void reloadfonts()
734 {
735     //createutf8charset();
736 
737     enumerate(fonts, font, f,
738         if(!reloadtexture(*f.tex)) fatal("failed to reload font texture");
739     );
740 }
741