29 #include "../../shared/utf8.h"
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
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}
127 if (!chunkCache[
i].texnum)
129 glDeleteTextures(1, &(chunkCache[
i].texnum));
150 TTF_CloseFont(fonts[
i].font);
152 SDL_RWclose(fonts[
i].rw);
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 #if SDL_VERSION_ATLEAST(2, 0, 0)
191 TTF_SetFontHinting(f->
font, TTF_HINTING_NONE);
195 f->
style = renderStyle;
216 #ifndef COMPILE_UNITTESTS
219 Com_Printf(
"R_GetFont: Could not find font: %s. Return nullptr\n", name);
230 Com_Printf(
"Font cache info\n========================\n");
244 collSum += collCount;
246 Com_Printf(
"...overall collisions %i\n", collSum);
256 int hashValue = 0x2040189 * ((font -
fonts) + 1);
257 for (
int i = 0;
string[
i] !=
'\0';
i++)
258 hashValue = (hashValue +
string[
i]) * 16777619 + 1;
260 hashValue = (hashValue ^ (hashValue >> 10) ^ (hashValue >> 20));
273 const char old = text[
len];
276 TTF_SizeUTF8(f->
font, text, &width,
nullptr);
302 if (text[
len] ==
' ') {
304 if (width > maxWidth)
313 if (text[
len] ==
'-') {
315 if (width > maxWidth)
332 if (width > maxWidth)
355 buf[
len - 1] = text[
len - 1];
363 TTF_SizeUTF8(f->
font, buf, &width,
nullptr);
364 if (width > maxWidth)
391 bool truncated =
false;
394 int len = strcspn(&buf[pos],
"\n");
401 if (len > 0 && utf8len !=
UTF8_char_len(buf[pos + len - utf8len])) {
407 while (len > 0 && buf[pos + len - 1] ==
' ') {
413 if (maxWidth > 0 && width > maxWidth) {
419 while (buf[pos + len + skip] ==
' ')
421 if (len + skip == 0) {
428 skip = strcspn(&buf[pos + len],
"\n");
447 if (buf[pos] ==
'\n' || buf[pos] ==
'\\') {
451 }
while (buf[pos] !=
'\0');
472 for (wrap = hash[hashValue]; wrap; wrap = wrap->
next)
474 if (!strncmp(text, wrap->
text,
sizeof(wrap->
text) - 1)
480 && (chunkCache[wrap->
chunkIdx].
width <= maxWidth || maxWidth <= 0))))
489 bool aborted =
false;
490 const int chunksUsed =
R_FontMakeChunks(f, text, maxWidth, method, &lines, &aborted);
493 strncpy(wrap->
text, text,
sizeof(wrap->
text));
494 wrap->
text[
sizeof(wrap->
text) - 1] =
'\0';
504 wrap->
next = hash[hashValue];
505 hash[hashValue] = wrap;
524 void R_FontTextSize (
const char* fontId,
const char* text,
int maxWidth,
longlines_t method,
int* width,
int* height,
int* lines,
bool* isTruncated)
557 #ifdef GL_VERSION_ES_CM_1_0
558 const int samples = GL_RGBA;
559 const int pixelFormat = GL_RGBA;
562 const int pixelFormat = GL_BGRA;
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;
571 const Uint32 rmask = 0x000000ff;
572 const Uint32 gmask = 0x0000ff00;
573 const Uint32 bmask = 0x00ff0000;
574 const Uint32 amask = 0xff000000;
580 assert(strlen(text) >= chunk->
pos + chunk->
len);
583 if (chunk->
len >=
sizeof(buf))
585 memcpy(buf, &text[chunk->
pos], chunk->
len);
591 static const SDL_Color color = {255, 255, 255, 0};
592 SDL_Surface* textSurface = TTF_RenderUTF8_Blended(font->
font, buf, color);
600 for (w = 2; w < textSurface->w; w <<= 1) {}
601 for (h = 2; h < textSurface->h; h <<= 1) {}
603 const int colordepth = 32;
604 SDL_Surface* openGLSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, colordepth, rmask, gmask, bmask, amask);
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;
613 #if SDL_VERSION_ATLEAST(2,0,0)
614 SDL_SetSurfaceBlendMode(textSurface, SDL_BLENDMODE_NONE);
615 SDL_SetSurfaceAlphaMod(textSurface, 255);
617 SDL_SetAlpha(textSurface, 0, 255);
620 SDL_LowerBlit(textSurface, &rect, openGLSurface, &rect);
621 SDL_FreeSurface(textSurface);
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);
630 SDL_FreeSurface(openGLSurface);
634 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0
660 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
688 int lineHeight,
const char* c,
int boxHeight,
int scrollPos,
int* curLine,
longlines_t method)
705 if (horizontalAlign == 1)
706 xalign = -(chunk->
width / 2);
707 else if (horizontalAlign == 2)
708 xalign = -chunk->
width;
712 if (linenum < scrollPos || linenum >= scrollPos + boxHeight)
724 #ifdef SDL_TTF_VERSION
727 SDL_TTF_VERSION(&version)
728 Com_Printf(
"SDL_ttf version %i.%i.%i - we need at least 2.0.7\n",
733 Com_Printf(
"could not get SDL_ttf version - we need at least 2.0.7\n");
746 if (TTF_Init() == -1)
752 int renderstyle = TTF_STYLE_NORMAL;
754 if (style && style[0] !=
'\0') {
struct wrapCache_s * next
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.
char text[MAX_CACHE_STRING]
void R_FontListCache_f(void)
Console command binding to show the font cache.
GLfloat * vertex_array_3d
int UTF8_char_len(unsigned char c)
length of UTF-8 character starting with this byte.
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...
local graphics definitions
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
This structure holds one piece of text (usually a whole line) and the texture on which it is rendered...
void Com_Printf(const char *const fmt,...)
static void R_FontDrawTexture(int texId, int x, int y, int w, int h)
#define UTF8_CONTINUATION_BYTE(c)
GLshort * vertex_array_2d
void R_FontShutdown(void)
frees the SDL_ttf fonts
void Com_Error(int code, const char *fmt,...)
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
font_t * R_GetFont(const char *name)
Searches the array of available fonts (see fonts.ufo)
align_t
We need this here for checking the boundaries from script values.
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...
#define Vector2Set(v, x, y)
static wrapCache_t * hash[MAX_WRAP_HASH]
static const float font_texcoords[]
int gl_compressed_alpha_format
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *texels)
static chunkCache_t chunkCache[MAX_CHUNK_CACHE]
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.
#define Q_strcasecmp(a, b)
static wrapCache_t wrapCache[MAX_WRAP_CACHE]
void R_FontRegister(const char *name, int size, const char *path, const char *style)
void R_FontSetTruncationMarker(const char *marker)
static font_t * R_FontAnalyze(const char *name, const char *path, int renderStyle, int size)
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_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.
This structure caches information about rendering a text in one font wrapped to a specific width...
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...
QGL_EXTERN GLuint GLchar GLuint * len
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...
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
static font_t fonts[MAX_FONTS]
static const fontRenderStyle_t fontStyle[]
void glGenTextures(GLsizei n, GLuint *textures)
void R_FontCleanCache(void)
Clears font cache and frees memory associated with the cache.
static int R_FontHash(const char *string, const font_t *font)
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)
#define Mem_Dup(type, in, n)
#define R_BindTexture(tn)