UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
r_font.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "r_local.h"
27 #include "r_font.h"
28 #include "r_error.h"
29 #include "../../shared/utf8.h"
30 
31 #define MAX_CACHE_STRING 128
32 #define MAX_CHUNK_CACHE 1024 /* making this bigger uses more GL textures */
33 #define MAX_WRAP_CACHE 1024 /* making this bigger uses more memory */
34 #define MAX_WRAP_HASH 4096 /* making this bigger reduces collisions */
35 #define MAX_FONTS 16
36 #define MAX_FONTNAME 32
37 #define MAX_TRUNCMARKER 16 /* enough for 3 chinese chars */
38 
39 #define BUF_SIZE 4096
40 
48 typedef struct {
49  int pos;
50  int len;
51  int linenum;
52  int width;
53  /* no need for individual line height, just use font->height */
54  bool truncated;
57 } chunkCache_t;
58 
69 typedef struct wrapCache_s {
71  const font_t* font;
72  struct wrapCache_s* next;
73  int maxWidth;
75  int numChunks;
76  int numLines;
77  int chunkIdx;
78  bool aborted;
79 } wrapCache_t;
80 
81 static int numFonts = 0;
83 
87 static int numChunks = 0;
88 static int numWraps = 0;
89 
95 static char truncmarker[MAX_TRUNCMARKER] = "...";
96 
97 typedef struct {
98  const char* name;
101 
102 #define NUM_FONT_STYLES (sizeof(fontStyle) / sizeof(fontRenderStyle_t))
103 static const fontRenderStyle_t fontStyle[] = {
104  {"TTF_STYLE_NORMAL", TTF_STYLE_NORMAL},
105  {"TTF_STYLE_BOLD", TTF_STYLE_BOLD},
106  {"TTF_STYLE_ITALIC", TTF_STYLE_ITALIC},
107  {"TTF_STYLE_UNDERLINE", TTF_STYLE_UNDERLINE}
108 };
109 
110 /*============================================================== */
111 
112 void R_FontSetTruncationMarker (const char* marker)
113 {
114  Q_strncpyz(truncmarker, marker, sizeof(truncmarker));
115 }
116 
117 
121 void R_FontCleanCache (void)
122 {
123  R_CheckError();
124 
125  /* free the surfaces */
126  for (int i = 0; i < numChunks; i++) {
127  if (!chunkCache[i].texnum)
128  continue;
129  glDeleteTextures(1, &(chunkCache[i].texnum));
130  R_CheckError();
131  }
132 
133  OBJZERO(chunkCache);
134  OBJZERO(wrapCache);
135  OBJZERO(hash);
136  numChunks = 0;
137  numWraps = 0;
138 }
139 
144 void R_FontShutdown (void)
145 {
147 
148  for (int i = 0; i < numFonts; i++)
149  if (fonts[i].font) {
150  TTF_CloseFont(fonts[i].font);
151  Mem_Free(fonts[i].buffer);
152  SDL_RWclose(fonts[i].rw);
153  }
154 
155  OBJZERO(fonts);
156  numFonts = 0;
157 
158  /* now quit SDL_ttf, too */
159  TTF_Quit();
160 }
161 
165 static font_t* R_FontAnalyze (const char* name, const char* path, int renderStyle, int size)
166 {
167  if (numFonts >= MAX_FONTS)
168  return nullptr;
169 
170  /* allocate new font */
171  font_t* f = &fonts[numFonts];
172  OBJZERO(*f);
173 
174  /* copy fontname */
175  f->name = name;
176 
177  byte* buf;
178  const int ttfSize = FS_LoadFile(path, &buf);
179  if (ttfSize == -1)
180  Com_Error(ERR_FATAL, "...could not load font file %s", path);
181 
182  /* duplicate the memory to survive a filesystem restart */
183  f->buffer = Mem_Dup(byte, buf, ttfSize);
184  f->rw = SDL_RWFromMem(f->buffer, ttfSize);
185 
186  f->font = TTF_OpenFontRW(f->rw, 0, size);
187  if (!f->font)
188  Com_Error(ERR_FATAL, "...could not load ttf font data %s (%s)", path, TTF_GetError());
189 
190 #if SDL_VERSION_ATLEAST(2, 0, 0)
191  TTF_SetFontHinting(f->font, TTF_HINTING_NONE);
192 #endif
193 
194  /* font style */
195  f->style = renderStyle;
196  if (f->style)
197  TTF_SetFontStyle(f->font, f->style);
198 
199  numFonts++;
200  f->lineSkip = TTF_FontLineSkip(f->font);
201  f->height = TTF_FontHeight(f->font);
202 
203  /* return the font */
204  return f;
205 }
206 
210 font_t* R_GetFont (const char* name)
211 {
212  for (int i = 0; i < numFonts; i++)
213  if (Q_streq(name, fonts[i].name))
214  return &fonts[i];
215 
216 #ifndef COMPILE_UNITTESTS
217  Com_Error(ERR_FATAL, "Could not find font: %s", name);
218 #else
219  Com_Printf("R_GetFont: Could not find font: %s. Return nullptr\n", name);
220  return nullptr;
221 #endif
222 }
223 
224 
228 void R_FontListCache_f (void)
229 {
230  Com_Printf("Font cache info\n========================\n");
231  Com_Printf("...wrap cache size: %i - used %i\n", MAX_WRAP_CACHE, numWraps);
232  Com_Printf("...chunk cache size: %i - used %i\n", MAX_CHUNK_CACHE, numChunks);
233 
234  int collSum = 0;
235  for (int i = 0; i < numWraps; i++) {
236  const wrapCache_t* wrap = &wrapCache[i];
237  int collCount = 0;
238  while (wrap->next) {
239  collCount++;
240  wrap = wrap->next;
241  }
242  if (collCount)
243  Com_Printf("...%i collisions for %s\n", collCount, wrap->text);
244  collSum += collCount;
245  }
246  Com_Printf("...overall collisions %i\n", collSum);
247 }
248 
254 static int R_FontHash (const char* string, const font_t* font)
255 {
256  int hashValue = 0x2040189 * ((font - fonts) + 1);
257  for (int i = 0; string[i] != '\0'; i++)
258  hashValue = (hashValue + string[i]) * 16777619 + 1;
259 
260  hashValue = (hashValue ^ (hashValue >> 10) ^ (hashValue >> 20));
261  return hashValue & (MAX_WRAP_HASH - 1);
262 }
263 
268 static int R_FontChunkLength (const font_t* f, char* text, int len)
269 {
270  if (len == 0)
271  return 0;
272 
273  const char old = text[len];
274  text[len] = '\0';
275  int width;
276  TTF_SizeUTF8(f->font, text, &width, nullptr);
277  text[len] = old;
278 
279  return width;
280 }
281 
294 static int R_FontFindFit (const font_t* font, char* text, int maxlen, int maxWidth, int* widthp)
295 {
296  int bestbreak = 0;
297 
298  *widthp = 0;
299 
300  /* Fit whole words */
301  for (int len = 1; len < maxlen; len++) {
302  if (text[len] == ' ') {
303  int width = R_FontChunkLength(font, text, len);
304  if (width > maxWidth)
305  break;
306  bestbreak = len;
307  *widthp = width;
308  }
309  }
310 
311  /* Fit hyphenated word parts */
312  for (int len = bestbreak + 1; len < maxlen; len++) {
313  if (text[len] == '-') {
314  int width = R_FontChunkLength(font, text, len + 1);
315  if (width > maxWidth)
316  break;
317  bestbreak = len + 1;
318  *widthp = width;
319  }
320  }
321 
322  if (bestbreak > 0)
323  return bestbreak;
324 
327  /* Can't fit even one word. Break first word anywhere. */
328  for (int len = 1; len < maxlen; len++) {
329  if (UTF8_CONTINUATION_BYTE(text[len]))
330  continue;
331  int width = R_FontChunkLength(font, text, len);
332  if (width > maxWidth)
333  break;
334  bestbreak = len;
335  *widthp = width;
336  }
337 
338  return bestbreak;
339 }
340 
347 static int R_FontFindTruncFit (const font_t* f, const char* text, int maxlen, int maxWidth, bool mark, int* widthp)
348 {
349  char buf[BUF_SIZE];
350 
351  int breaklen = 0;
352  *widthp = 0;
353 
354  for (int len = 1; len < maxlen; len++) {
355  buf[len - 1] = text[len - 1];
356  if (UTF8_CONTINUATION_BYTE(text[len]))
357  continue;
358  if (mark)
359  Q_strncpyz(&buf[len], truncmarker, sizeof(buf) - len);
360  else
361  buf[len] = '\0';
362  int width;
363  TTF_SizeUTF8(f->font, buf, &width, nullptr);
364  if (width > maxWidth)
365  return breaklen;
366  breaklen = len;
367  *widthp = width;
368  }
369 
370  return maxlen;
371 }
372 
378 static int R_FontMakeChunks (const font_t* f, const char* text, int maxWidth, longlines_t method, int* lines, bool* aborted)
379 {
380  assert(text);
381 
382  int lineno = 0;
383  int pos = 0;
384  int startChunks = numChunks;
385  char buf[BUF_SIZE];
386 
387  Q_strncpyz(buf, text, sizeof(buf));
388 
389  do {
390  int skip = 0;
391  bool truncated = false;
392 
393  /* find mandatory break */
394  int len = strcspn(&buf[pos], "\n");
395 
396  /* tidy up broken UTF-8 at end of line which may have been
397  * truncated by caller by use of functions like Q_strncpyz */
398  int utf8len = 1;
399  while (len > utf8len && UTF8_CONTINUATION_BYTE(buf[pos + len - utf8len]))
400  utf8len++;
401  if (len > 0 && utf8len != UTF8_char_len(buf[pos + len - utf8len])) {
402  len -= utf8len;
403  skip += utf8len;
404  }
405 
406  /* delete trailing spaces */
407  while (len > 0 && buf[pos + len - 1] == ' ') {
408  len--;
409  skip++;
410  }
411 
412  int width = R_FontChunkLength(f, &buf[pos], len);
413  if (maxWidth > 0 && width > maxWidth) {
414  if (method == LONGLINES_WRAP) {
415  /* full chunk didn't fit; try smaller */
416  len = R_FontFindFit(f, &buf[pos], len, maxWidth, &width);
417  /* skip following spaces */
418  skip = 0;
419  while (buf[pos + len + skip] == ' ')
420  skip++;
421  if (len + skip == 0) {
422  *aborted = true;
423  break; /* could not fit even one character */
424  }
425  } else {
426  truncated = (method == LONGLINES_PRETTYCHOP);
427  len = R_FontFindTruncFit(f, &buf[pos], len, maxWidth, truncated, &width);
428  skip = strcspn(&buf[pos + len], "\n");
429  }
430  }
431  if (width > 0) {
432  /* add chunk to cache */
433  if (numChunks >= MAX_CHUNK_CACHE) {
434  /* whoops, ran out of cache, wipe cache and start over */
437  return R_FontMakeChunks(f, text, maxWidth, method, lines, aborted);
438  }
439  chunkCache[numChunks].pos = pos;
440  chunkCache[numChunks].len = len;
441  chunkCache[numChunks].linenum = lineno;
442  chunkCache[numChunks].width = width;
443  chunkCache[numChunks].truncated = truncated;
444  numChunks++;
445  }
446  pos += len + skip;
447  if (buf[pos] == '\n' || buf[pos] == '\\') {
448  pos++;
449  }
450  lineno++;
451  } while (buf[pos] != '\0');
452 
453  /* If there were empty lines at the end of the text, then lineno might
454  * be greater than the linenum of the last chunk. Some callers need to
455  * know this to count lines accurately. */
456  *lines = lineno;
457  return numChunks - startChunks;
458 }
459 
464 static wrapCache_t* R_FontWrapText (const font_t* f, const char* text, int maxWidth, longlines_t method)
465 {
466  wrapCache_t* wrap;
467  const int hashValue = R_FontHash(text ,f);
468 
469  /* String is considered a match if the part that fit in entry->string
470  * matches. Since the hash value also matches and the hash was taken
471  * over the whole string, this is good enough. */
472  for (wrap = hash[hashValue]; wrap; wrap = wrap->next)
473  /* big strings are cut, we must not test the 256th character ('\0') */
474  if (!strncmp(text, wrap->text, sizeof(wrap->text) - 1)
475  && wrap->font == f
476  && wrap->method == method
477  && (wrap->maxWidth == maxWidth
478  || (wrap->numLines == 1 && !wrap->aborted
479  && !chunkCache[wrap->chunkIdx].truncated
480  && (chunkCache[wrap->chunkIdx].width <= maxWidth || maxWidth <= 0))))
481  return wrap;
482 
483  if (numWraps >= MAX_WRAP_CACHE)
485 
486  /* It is possible that R_FontMakeChunks will wipe the cache,
487  * so do not rely on numWraps until it completes. */
488  int lines;
489  bool aborted = false;
490  const int chunksUsed = R_FontMakeChunks(f, text, maxWidth, method, &lines, &aborted);
491 
492  wrap = &wrapCache[numWraps];
493  strncpy(wrap->text, text, sizeof(wrap->text));
494  wrap->text[sizeof(wrap->text) - 1] = '\0';
495  wrap->font = f;
496  wrap->maxWidth = maxWidth;
497  wrap->method = method;
498  wrap->aborted = aborted;
499  wrap->numChunks = chunksUsed;
500  wrap->numLines = lines;
501  wrap->chunkIdx = numChunks - chunksUsed;
502 
503  /* insert new text into wrap cache */
504  wrap->next = hash[hashValue];
505  hash[hashValue] = wrap;
506  numWraps++;
507 
508  return wrap;
509 }
510 
524 void R_FontTextSize (const char* fontId, const char* text, int maxWidth, longlines_t method, int* width, int* height, int* lines, bool* isTruncated)
525 {
526  const font_t* font = R_GetFont(fontId);
527  const wrapCache_t* wrap = R_FontWrapText(font, text, maxWidth, method);
528 
529  if (width) {
530  *width = 0;
531  for (int i = 0; i < wrap->numChunks; i++) {
532  if (chunkCache[wrap->chunkIdx + i].width > *width)
533  *width = chunkCache[wrap->chunkIdx + i].width;
534  }
535  }
536 
537  if (height)
538  *height = (wrap->numLines - 1) * font->lineSkip + font->height;
539 
540  if (lines)
541  *lines = wrap->numLines;
542 
543  if (isTruncated)
544  *isTruncated = chunkCache[wrap->chunkIdx].truncated;
545 }
546 
555 static void R_FontGenerateTexture (const font_t* font, const char* text, chunkCache_t* chunk)
556 {
557 #ifdef GL_VERSION_ES_CM_1_0
558  const int samples = GL_RGBA;
559  const int pixelFormat = GL_RGBA; /* There's no GL_BGRA symbol defined in Android GLES headers */
560 #else
562  const int pixelFormat = GL_BGRA;
563 #endif
564 
565 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
566  const Uint32 rmask = 0xff000000;
567  const Uint32 gmask = 0x00ff0000;
568  const Uint32 bmask = 0x0000ff00;
569  const Uint32 amask = 0x000000ff;
570 #else
571  const Uint32 rmask = 0x000000ff;
572  const Uint32 gmask = 0x0000ff00;
573  const Uint32 bmask = 0x00ff0000;
574  const Uint32 amask = 0xff000000;
575 #endif
576 
577  if (chunk->texnum != 0)
578  return; /* already generated */
579 
580  assert(strlen(text) >= chunk->pos + chunk->len);
581 
582  char buf[BUF_SIZE];
583  if (chunk->len >= sizeof(buf))
584  return;
585  memcpy(buf, &text[chunk->pos], chunk->len);
586  buf[chunk->len] = 0;
587 
588  if (chunk->truncated)
589  Q_strncpyz(buf + chunk->len, truncmarker, sizeof(buf) - chunk->len);
590 
591  static const SDL_Color color = {255, 255, 255, 0}; /* The 4th value is unused */
592  SDL_Surface* textSurface = TTF_RenderUTF8_Blended(font->font, buf, color);
593  if (!textSurface) {
594  Com_Printf("%s (%s)\n", TTF_GetError(), buf);
595  return;
596  }
597 
598  /* copy text to a surface of suitable size for a texture (power of two) */
599  int w, h;
600  for (w = 2; w < textSurface->w; w <<= 1) {}
601  for (h = 2; h < textSurface->h; h <<= 1) {}
602 
603  const int colordepth = 32;
604  SDL_Surface* openGLSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, colordepth, rmask, gmask, bmask, amask);
605  if (!openGLSurface)
606  return;
607 
608  SDL_Rect rect = {0, 0, static_cast<Uint16>(textSurface->w), static_cast<Uint16>(textSurface->h)};
609  if (rect.w > chunk->width)
610  rect.w = chunk->width;
611 
612  /* ignore alpha when blitting - just copy it over */
613 #if SDL_VERSION_ATLEAST(2,0,0)
614  SDL_SetSurfaceBlendMode(textSurface, SDL_BLENDMODE_NONE);
615  SDL_SetSurfaceAlphaMod(textSurface, 255);
616 #else
617  SDL_SetAlpha(textSurface, 0, 255);
618 #endif
619 
620  SDL_LowerBlit(textSurface, &rect, openGLSurface, &rect);
621  SDL_FreeSurface(textSurface);
622 
623  glGenTextures(1, &chunk->texnum);
624  R_BindTexture(chunk->texnum);
625  glTexImage2D(GL_TEXTURE_2D, 0, samples, w, h, 0, pixelFormat, GL_UNSIGNED_BYTE, openGLSurface->pixels);
626  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
627  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
628  Vector2Set(chunk->texsize, w, h);
629  R_CheckError();
630  SDL_FreeSurface(openGLSurface);
631 }
632 
633 static const float font_texcoords[] = {
634  0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0
635 };
636 
637 static void R_FontDrawTexture (int texId, int x, int y, int w, int h)
638 {
639  const float nx = x * viddef.rx;
640  const float ny = y * viddef.ry;
641  const float nw = w * viddef.rx;
642  const float nh = h * viddef.ry;
643 
644  R_BindTexture(texId);
645 
646  glVertexPointer(2, GL_SHORT, 0, r_state.vertex_array_2d);
647 
648  memcpy(texunit_diffuse.texcoord_array, font_texcoords, sizeof(font_texcoords));
649 
650  r_state.vertex_array_2d[0] = nx;
651  r_state.vertex_array_2d[1] = ny;
652  r_state.vertex_array_2d[2] = nx + nw;
653  r_state.vertex_array_2d[3] = ny;
654 
655  r_state.vertex_array_2d[4] = nx;
656  r_state.vertex_array_2d[5] = ny + nh;
657  r_state.vertex_array_2d[6] = nx + nw;
658  r_state.vertex_array_2d[7] = ny + nh;
659 
660  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
661 
662  refdef.batchCount++;
663 
664  /* set back to standard 3d pointer */
665  glVertexPointer(3, GL_FLOAT, 0, r_state.vertex_array_3d);
666 }
667 
687 int R_FontDrawString (const char* fontId, align_t align, int x, int y, int absX, int maxWidth,
688  int lineHeight, const char* c, int boxHeight, int scrollPos, int* curLine, longlines_t method)
689 {
690  const font_t* font = R_GetFont(fontId);
691  const align_t horizontalAlign = (align_t)(align % 3); /* left, center, right */
692  const wrapCache_t* wrap = R_FontWrapText(font, c, maxWidth - (x - absX), method);
693 
694  if (boxHeight <= 0)
695  boxHeight = wrap->numLines;
696 
697  for (int i = 0; i < wrap->numChunks; i++) {
698  int xalign = 0;
699  chunkCache_t* chunk = &chunkCache[wrap->chunkIdx + i];
700  int linenum = chunk->linenum;
701 
702  if (curLine)
703  linenum += *curLine;
704 
705  if (horizontalAlign == 1)
706  xalign = -(chunk->width / 2);
707  else if (horizontalAlign == 2)
708  xalign = -chunk->width;
709  else
710  xalign = 0;
711 
712  if (linenum < scrollPos || linenum >= scrollPos + boxHeight)
713  continue;
714 
715  R_FontGenerateTexture(font, c, chunk);
716  R_FontDrawTexture(chunk->texnum, x + xalign, y + (linenum - scrollPos) * lineHeight, chunk->texsize[0], chunk->texsize[1]);
717  }
718 
719  return wrap->numLines;
720 }
721 
722 void R_FontInit (void)
723 {
724 #ifdef SDL_TTF_VERSION
725  SDL_version version;
726 
727  SDL_TTF_VERSION(&version)
728  Com_Printf("SDL_ttf version %i.%i.%i - we need at least 2.0.7\n",
729  version.major,
730  version.minor,
731  version.patch);
732 #else
733  Com_Printf("could not get SDL_ttf version - we need at least 2.0.7\n");
734 #endif
735 
736  numFonts = 0;
737  OBJZERO(fonts);
738 
739  OBJZERO(chunkCache);
740  OBJZERO(wrapCache);
741  OBJZERO(hash);
742  numChunks = 0;
743  numWraps = 0;
744 
745  /* init the truetype font engine */
746  if (TTF_Init() == -1)
747  Com_Error(ERR_FATAL, "SDL_ttf error: %s", TTF_GetError());
748 }
749 
750 void R_FontRegister (const char* name, int size, const char* path, const char* style)
751 {
752  int renderstyle = TTF_STYLE_NORMAL; /* NORMAL is standard */
753 
754  if (style && style[0] != '\0') {
755  for (int i = 0; i < NUM_FONT_STYLES; i++)
756  if (!Q_strcasecmp(fontStyle[i].name, style)) {
757  renderstyle = fontStyle[i].renderStyle;
758  break;
759  }
760  }
761 
762  R_FontAnalyze(name, path, renderstyle, size);
763 }
struct wrapCache_s * next
Definition: r_font.cpp:72
int linenum
Definition: r_font.cpp:51
TTF_Font * font
Definition: r_font.h:31
int numLines
Definition: r_font.cpp:76
#define MAX_CACHE_STRING
Definition: r_font.cpp:31
static char truncmarker[MAX_TRUNCMARKER]
This string is added at the end of truncated strings. By default it is an ellipsis, but the caller can change that.
Definition: r_font.cpp:95
char text[MAX_CACHE_STRING]
Definition: r_font.cpp:70
void R_FontListCache_f(void)
Console command binding to show the font cache.
Definition: r_font.cpp:228
GLfloat * vertex_array_3d
Definition: r_state.h:100
int UTF8_char_len(unsigned char c)
length of UTF-8 character starting with this byte.
Definition: utf8.cpp:109
#define MAX_CHUNK_CACHE
Definition: r_font.cpp:32
struct wrapCache_s wrapCache_t
This structure caches information about rendering a text in one font wrapped to a specific width...
static int R_FontChunkLength(const font_t *f, char *text, int len)
Calculate the width in pixels needed to render a piece of text. Can temporarily modify the caller's s...
Definition: r_font.cpp:268
float rx
Definition: cl_video.h:71
#define MAX_FONTS
Definition: r_font.cpp:35
const font_t * font
Definition: r_font.cpp:71
viddef_t viddef
Definition: cl_video.cpp:34
local graphics definitions
int numChunks
Definition: r_font.cpp:75
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
Definition: files.cpp:384
This structure holds one piece of text (usually a whole line) and the texture on which it is rendered...
Definition: r_font.cpp:48
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
rconfig_t r_config
Definition: r_main.cpp:47
static void R_FontDrawTexture(int texId, int x, int y, int w, int h)
Definition: r_font.cpp:637
void R_FontInit(void)
Definition: r_font.cpp:722
const char * name
Definition: r_font.cpp:98
static int numFonts
Definition: r_font.cpp:81
QGL_EXTERN GLuint
Definition: r_gl.h:124
const char * name
Definition: r_font.h:30
voidpf void * buf
Definition: ioapi.h:42
#define ERR_FATAL
Definition: common.h:210
#define UTF8_CONTINUATION_BYTE(c)
Definition: utf8.h:35
GLshort * vertex_array_2d
Definition: r_state.h:101
void R_FontShutdown(void)
frees the SDL_ttf fonts
Definition: r_font.cpp:144
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
QGL_EXTERN GLuint maxlen
Definition: r_gl.h:102
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
font_t * R_GetFont(const char *name)
Searches the array of available fonts (see fonts.ufo)
Definition: r_font.cpp:210
align_t
We need this here for checking the boundaries from script values.
Definition: scripts.h:90
GLuint texnum
Definition: r_font.cpp:56
static void R_FontGenerateTexture(const font_t *font, const char *text, chunkCache_t *chunk)
Renders the text surface and converts to 32bit SDL_Surface that is stored in font_t structure...
Definition: r_font.cpp:555
vec2_t texsize
Definition: r_font.cpp:55
GLsizei size
Definition: r_gl.h:152
rendererData_t refdef
Definition: r_main.cpp:45
#define OBJZERO(obj)
Definition: shared.h:178
int chunkIdx
Definition: r_font.cpp:77
#define Vector2Set(v, x, y)
Definition: vector.h:61
static wrapCache_t * hash[MAX_WRAP_HASH]
Definition: r_font.cpp:86
static const float font_texcoords[]
Definition: r_font.cpp:633
int gl_compressed_alpha_format
Definition: r_local.h:193
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *texels)
Definition: gldummy.cpp:17
static chunkCache_t chunkCache[MAX_CHUNK_CACHE]
Definition: r_font.cpp:84
static int R_FontMakeChunks(const font_t *f, const char *text, int maxWidth, longlines_t method, int *lines, bool *aborted)
Split text into chunks that fit on one line, and create cache entries for those chunks.
Definition: r_font.cpp:378
#define MAX_WRAP_HASH
Definition: r_font.cpp:34
#define Q_strcasecmp(a, b)
Definition: shared.h:131
Definition: r_font.h:29
static wrapCache_t wrapCache[MAX_WRAP_CACHE]
Definition: r_font.cpp:85
void R_FontRegister(const char *name, int size, const char *path, const char *style)
Definition: r_font.cpp:750
#define MAX_WRAP_CACHE
Definition: r_font.cpp:33
void R_FontSetTruncationMarker(const char *marker)
Definition: r_font.cpp:112
static font_t * R_FontAnalyze(const char *name, const char *path, int renderStyle, int size)
Definition: r_font.cpp:165
static wrapCache_t * R_FontWrapText(const font_t *f, const char *text, int maxWidth, longlines_t method)
Wrap text according to provided parameters. Pull wrapping from cache if possible. ...
Definition: r_font.cpp:464
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
void R_FontTextSize(const char *fontId, const char *text, int maxWidth, longlines_t method, int *width, int *height, int *lines, bool *isTruncated)
Supply information about the size of the text when it is linewrapped and rendered, without actually rendering it. Any of the output parameters may be nullptr.
Definition: r_font.cpp:524
int style
Definition: r_font.h:34
This structure caches information about rendering a text in one font wrapped to a specific width...
Definition: r_font.cpp:69
static int R_FontFindTruncFit(const font_t *f, const char *text, int maxlen, int maxWidth, bool mark, int *widthp)
Find longest part of text that fits in maxWidth pixels, with a marker (ellipsis) at the end to show t...
Definition: r_font.cpp:347
#define texunit_diffuse
Definition: r_state.h:68
#define MAX_TRUNCMARKER
Definition: r_font.cpp:37
QGL_EXTERN GLint i
Definition: r_gl.h:113
QGL_EXTERN GLuint GLchar GLuint * len
Definition: r_gl.h:99
int lineSkip
Definition: r_font.h:35
static int R_FontFindFit(const font_t *font, char *text, int maxlen, int maxWidth, int *widthp)
Find longest part of text that fits in maxWidth pixels, with a clean break such as at a word boundary...
Definition: r_font.cpp:294
bool truncated
Definition: r_font.cpp:54
int gl_alpha_format
Definition: r_local.h:190
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
int height
Definition: r_font.h:36
byte * buffer
Definition: r_font.h:33
static font_t fonts[MAX_FONTS]
Definition: r_font.cpp:82
#define NUM_FONT_STYLES
Definition: r_font.cpp:102
#define Mem_Free(ptr)
Definition: mem.h:35
static const fontRenderStyle_t fontStyle[]
Definition: r_font.cpp:103
Error checking function.
void glGenTextures(GLsizei n, GLuint *textures)
Definition: gldummy.cpp:22
void R_FontCleanCache(void)
Clears font cache and frees memory associated with the cache.
Definition: r_font.cpp:121
vec_t vec2_t[2]
Definition: ufotypes.h:38
rstate_t r_state
Definition: r_main.cpp:48
#define R_CheckError()
Definition: r_error.h:30
static int R_FontHash(const char *string, const font_t *font)
Definition: r_font.cpp:254
#define Q_streq(a, b)
Definition: shared.h:136
int R_FontDrawString(const char *fontId, align_t align, int x, int y, int absX, int maxWidth, int lineHeight, const char *c, int boxHeight, int scrollPos, int *curLine, longlines_t method)
Definition: r_font.cpp:687
int maxWidth
Definition: r_font.cpp:73
float ry
Definition: cl_video.h:72
SDL_RWops * rw
Definition: r_font.h:32
bool aborted
Definition: r_font.cpp:78
uint8_t byte
Definition: ufotypes.h:34
#define Mem_Dup(type, in, n)
Definition: mem.h:47
longlines_t
Definition: cl_renderer.h:217
longlines_t method
Definition: r_font.cpp:74
static int numChunks
Definition: r_font.cpp:87
#define R_BindTexture(tn)
Definition: r_state.h:184
#define BUF_SIZE
Definition: r_font.cpp:39
static int numWraps
Definition: r_font.cpp:88