31#define MAX_CACHE_STRING 128
32#define MAX_CHUNK_CACHE 1024
33#define MAX_WRAP_CACHE 1024
34#define MAX_WRAP_HASH 4096
36#define MAX_FONTNAME 32
37#define MAX_TRUNCMARKER 16
69typedef struct wrapCache_s {
102#define NUM_FONT_STYLES (sizeof(fontStyle) / sizeof(fontRenderStyle_t))
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}
150 TTF_CloseFont(
fonts[
i].font);
184 f->rw = SDL_RWFromMem(
f->buffer, ttfSize);
186 f->font = TTF_OpenFontRW(
f->rw, 0,
size);
188 Com_Error(
ERR_FATAL,
"...could not load ttf font data %s (%s)", path, TTF_GetError());
190 TTF_SetFontHinting(
f->font, TTF_HINTING_NONE);
193 f->style = renderStyle;
195 TTF_SetFontStyle(
f->font,
f->style);
198 f->lineSkip = TTF_FontLineSkip(
f->font);
199 f->height = TTF_FontHeight(
f->font);
214#ifndef COMPILE_UNITTESTS
217 Com_Printf(
"R_GetFont: Could not find font: %s. Return nullptr\n",
name);
228 Com_Printf(
"Font cache info\n========================\n");
242 collSum += collCount;
244 Com_Printf(
"...overall collisions %i\n", collSum);
254 int hashValue = 0x2040189 * ((font -
fonts) + 1);
255 for (
int i = 0;
string[
i] !=
'\0';
i++)
256 hashValue = (hashValue +
string[
i]) * 16777619 + 1;
258 hashValue = (hashValue ^ (hashValue >> 10) ^ (hashValue >> 20));
271 const char old = text[
len];
274 TTF_SizeUTF8(
f->font, text, &width,
nullptr);
300 if (text[
len] ==
' ') {
302 if (width > maxWidth)
311 if (text[
len] ==
'-') {
313 if (width > maxWidth)
330 if (width > maxWidth)
361 TTF_SizeUTF8(
f->font,
buf, &width,
nullptr);
362 if (width > maxWidth)
389 bool truncated =
false;
392 int len = strcspn(&
buf[pos],
"\n");
405 while (
len > 0 &&
buf[pos +
len - 1] ==
' ') {
411 if (maxWidth > 0 && width > maxWidth) {
417 while (
buf[pos +
len + skip] ==
' ')
419 if (
len + skip == 0) {
426 skip = strcspn(&
buf[pos +
len],
"\n");
445 if (
buf[pos] ==
'\n' ||
buf[pos] ==
'\\') {
449 }
while (
buf[pos] !=
'\0');
470 for (wrap =
hash[hashValue]; wrap; wrap = wrap->
next)
472 if (!strncmp(text, wrap->
text,
sizeof(wrap->
text) - 1)
487 bool aborted =
false;
488 const int chunksUsed =
R_FontMakeChunks(
f, text, maxWidth, method, &lines, &aborted);
491 strncpy(wrap->
text, text,
sizeof(wrap->
text));
492 wrap->
text[
sizeof(wrap->
text) - 1] =
'\0';
503 hash[hashValue] = wrap;
522void R_FontTextSize (
const char* fontId,
const char* text,
int maxWidth,
longlines_t method,
int* width,
int* height,
int* lines,
bool* isTruncated)
555#ifdef GL_VERSION_ES_CM_1_0
556 const int samples = GL_RGBA;
557 const int pixelFormat = GL_RGBA;
559 const int samples =
r_config.gl_compressed_alpha_format ?
r_config.gl_compressed_alpha_format :
r_config.gl_alpha_format;
560 const int pixelFormat = GL_BGRA;
563#if SDL_BYTEORDER == SDL_BIG_ENDIAN
564 const Uint32 rmask = 0xff000000;
565 const Uint32 gmask = 0x00ff0000;
566 const Uint32 bmask = 0x0000ff00;
567 const Uint32 amask = 0x000000ff;
569 const Uint32 rmask = 0x000000ff;
570 const Uint32 gmask = 0x0000ff00;
571 const Uint32 bmask = 0x00ff0000;
572 const Uint32 amask = 0xff000000;
578 assert(strlen(text) >= chunk->
pos + chunk->
len);
581 if (chunk->
len >=
sizeof(
buf))
583 memcpy(
buf, &text[chunk->
pos], chunk->
len);
589 static const SDL_Color color = {255, 255, 255, 0};
590 SDL_Surface* textSurface = TTF_RenderUTF8_Blended(font->
font,
buf, color);
598 for (w = 2; w < textSurface->w; w <<= 1) {}
599 for (h = 2; h < textSurface->h; h <<= 1) {}
601 const int colordepth = 32;
602 SDL_Surface* openGLSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, colordepth, rmask, gmask, bmask, amask);
606 SDL_Rect rect = {0, 0,
static_cast<Uint16
>(textSurface->w),
static_cast<Uint16
>(textSurface->h)};
607 if (rect.w > chunk->
width)
608 rect.w = chunk->
width;
611 SDL_SetSurfaceBlendMode(textSurface, SDL_BLENDMODE_NONE);
612 SDL_SetSurfaceAlphaMod(textSurface, 255);
614 SDL_LowerBlit(textSurface, &rect, openGLSurface, &rect);
615 SDL_FreeSurface(textSurface);
619 glTexImage2D(GL_TEXTURE_2D, 0, samples, w, h, 0, pixelFormat, GL_UNSIGNED_BYTE, openGLSurface->pixels);
620 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
621 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
624 SDL_FreeSurface(openGLSurface);
628 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0
633 const float nx = x *
viddef.rx;
634 const float ny = y *
viddef.ry;
635 const float nw = w *
viddef.rx;
636 const float nh = h *
viddef.ry;
640 glVertexPointer(2, GL_SHORT, 0,
r_state.vertex_array_2d);
644 r_state.vertex_array_2d[0] = nx;
645 r_state.vertex_array_2d[1] = ny;
646 r_state.vertex_array_2d[2] = nx + nw;
647 r_state.vertex_array_2d[3] = ny;
649 r_state.vertex_array_2d[4] = nx;
650 r_state.vertex_array_2d[5] = ny + nh;
651 r_state.vertex_array_2d[6] = nx + nw;
652 r_state.vertex_array_2d[7] = ny + nh;
654 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
659 glVertexPointer(3, GL_FLOAT, 0,
r_state.vertex_array_3d);
682 int lineHeight,
const char* c,
int boxHeight,
int scrollPos,
int* curLine,
longlines_t method)
699 if (horizontalAlign == 1)
700 xalign = -(chunk->
width / 2);
701 else if (horizontalAlign == 2)
702 xalign = -chunk->
width;
706 if (linenum < scrollPos || linenum >= scrollPos + boxHeight)
718#ifdef SDL_TTF_VERSION
721 SDL_TTF_VERSION(&version)
722 Com_Printf(
"SDL_ttf version %i.%i.%i - we need at least 2.0.7\n",
727 Com_Printf(
"could not get SDL_ttf version - we need at least 2.0.7\n");
740 if (TTF_Init() == -1)
746 int renderstyle = TTF_STYLE_NORMAL;
748 if (style && style[0] !=
'\0') {
void Com_Error(int code, const char *fmt,...)
void Com_Printf(const char *const fmt,...)
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *texels)
void glGenTextures(GLsizei n, GLuint *textures)
#define Mem_Dup(type, in, n)
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...
static char truncmarker[MAX_TRUNCMARKER]
This string is added at the end of truncated strings. By default it is an ellipsis,...
static wrapCache_t * hash[MAX_WRAP_HASH]
static wrapCache_t wrapCache[MAX_WRAP_CACHE]
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,...
static void R_FontDrawTexture(int texId, int x, int y, int w, int h)
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...
static chunkCache_t chunkCache[MAX_CHUNK_CACHE]
void R_FontSetTruncationMarker(const char *marker)
static font_t fonts[MAX_FONTS]
static const fontRenderStyle_t fontStyle[]
font_t * R_GetFont(const char *name)
Searches the array of available fonts (see fonts.ufo).
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)
static const float font_texcoords[]
void R_FontListCache_f(void)
Console command binding to show the font cache.
void R_FontRegister(const char *name, int size, const char *path, const char *style)
void R_FontCleanCache(void)
Clears font cache and frees memory associated with the cache.
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.
void R_FontShutdown(void)
frees the SDL_ttf fonts
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.
static int R_FontHash(const char *string, const font_t *font)
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.
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...
static font_t * R_FontAnalyze(const char *name, const char *path, int renderStyle, int size)
QGL_EXTERN GLuint GLchar GLuint * len
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
local graphics definitions
#define R_BindTexture(tn)
align_t
We need this here for checking the boundaries from script values.
#define Q_strcasecmp(a, b)
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
This structure holds one piece of text (usually a whole line) and the texture on which it is rendered...
This structure caches information about rendering a text in one font wrapped to a specific width....
char text[MAX_CACHE_STRING]
struct wrapCache_s * next
int UTF8_char_len(unsigned char c)
length of UTF-8 character starting with this byte.
#define UTF8_CONTINUATION_BYTE(c)
#define Vector2Set(v, x, y)