2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 #include "ft2_fontdefs.h"
34 // this flag indicates that it should be loaded and unloaded on demand
36 // texture flags to upload with
38 // texture may be freed after a while
41 skinframe_t *skinframe;
42 // used for hash lookups
43 struct cachepic_s *chain;
44 // flags - CACHEPICFLAG_NEWPIC for example
51 static mempool_t *fonts_mempool = NULL;
53 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
54 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
55 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
57 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
58 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
59 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
60 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
61 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
62 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
63 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
64 cvar_t r_nearest_2d = {CVAR_SAVE, "r_nearest_2d", "0", "use nearest filtering on all 2d textures (including conchars)"};
65 cvar_t r_nearest_conchars = {CVAR_SAVE, "r_nearest_conchars", "0", "use nearest filtering on conchars texture"};
67 //=============================================================================
68 /* Support Routines */
70 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
71 static cachepic_t cachepics[MAX_CACHED_PICS];
72 static int numcachepics;
74 rtexturepool_t *drawtexturepool;
83 // FIXME: move this to client somehow
84 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
90 texflags = TEXF_ALPHA;
91 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
92 texflags |= TEXF_CLAMP;
93 if (cachepicflags & CACHEPICFLAG_MIPMAP)
94 texflags |= TEXF_MIPMAP;
95 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
96 texflags |= TEXF_COMPRESS;
97 if ((cachepicflags & CACHEPICFLAG_NEAREST) || r_nearest_2d.integer)
98 texflags |= TEXF_FORCENEAREST;
100 // check whether the picture has already been cached
101 crc = CRC_Block((unsigned char *)path, strlen(path));
102 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
103 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
105 if (!strcmp(path, pic->name))
107 // if it was created (or replaced) by Draw_NewPic, just return it
108 if (!(pic->flags & CACHEPICFLAG_NEWPIC))
110 // reload the pic if texflags changed in important ways
111 // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag, and ignore TEXF_MIPMAP because QC specifies that
112 if ((pic->texflags ^ texflags) & ~(TEXF_COMPRESS | TEXF_MIPMAP))
114 Con_DPrintf("Draw_CachePic(\"%s\"): reloading pic due to mismatch on flags\n", path);
117 if (!pic->skinframe || !pic->skinframe->base)
119 Con_DPrintf("Draw_CachePic(\"%s\"): reloading pic\n", pic);
122 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
123 pic->autoload = false; // caller is making this pic persistent
126 R_SkinFrame_MarkUsed(pic->skinframe);
127 pic->lastusedframe = draw_frame;
132 if (numcachepics == MAX_CACHED_PICS)
134 Con_Printf ("Draw_CachePic(\"%s\"): numcachepics == MAX_CACHED_PICS\n", path);
135 // FIXME: support NULL in callers?
136 return cachepics; // return the first one
138 Con_DPrintf("Draw_CachePic(\"%s\"): loading pic\n", path);
139 pic = cachepics + (numcachepics++);
140 memset(pic, 0, sizeof(*pic));
141 strlcpy (pic->name, path, sizeof(pic->name));
143 pic->chain = cachepichash[hashkey];
144 cachepichash[hashkey] = pic;
148 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
150 pic->flags = cachepicflags;
151 pic->texflags = texflags;
152 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT) != 0;
153 pic->lastusedframe = draw_frame;
157 // reload image after it was unloaded or texflags changed significantly
158 R_SkinFrame_LoadExternal_SkinFrame(pic->skinframe, pic->name, texflags | TEXF_FORCE_RELOAD, (cachepicflags & CACHEPICFLAG_QUIET) == 0, (cachepicflags & CACHEPICFLAG_FAILONMISSING) == 0);
162 // load high quality image (this falls back to low quality too)
163 pic->skinframe = R_SkinFrame_LoadExternal(pic->name, texflags | TEXF_FORCE_RELOAD, (cachepicflags & CACHEPICFLAG_QUIET) == 0, (cachepicflags & CACHEPICFLAG_FAILONMISSING) == 0);
166 // get the dimensions of the image we loaded (if it was successful)
167 if (pic->skinframe && pic->skinframe->base)
169 pic->width = R_TextureWidth(pic->skinframe->base);
170 pic->height = R_TextureHeight(pic->skinframe->base);
173 // check for a low quality version of the pic and use its size if possible, to match the stock hud
174 Image_GetStockPicSize(pic->name, &pic->width, &pic->height);
179 cachepic_t *Draw_CachePic (const char *path)
181 return Draw_CachePic_Flags (path, 0); // default to persistent!
184 const char *Draw_GetPicName(cachepic_t *pic)
191 int Draw_GetPicWidth(cachepic_t *pic)
198 int Draw_GetPicHeight(cachepic_t *pic)
205 qboolean Draw_IsPicLoaded(cachepic_t *pic)
209 if (pic->autoload && (!pic->skinframe || !pic->skinframe->base))
210 pic->skinframe = R_SkinFrame_LoadExternal(pic->name, pic->texflags | TEXF_FORCE_RELOAD, false, true);
211 // skinframe will only be NULL if the pic was created with CACHEPICFLAG_FAILONMISSING and not found
212 return pic->skinframe != NULL && pic->skinframe->base != NULL;
215 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
219 if (pic->autoload && (!pic->skinframe || !pic->skinframe->base))
220 pic->skinframe = R_SkinFrame_LoadExternal(pic->name, pic->texflags | TEXF_FORCE_RELOAD, false, true);
221 pic->lastusedframe = draw_frame;
222 return pic->skinframe ? pic->skinframe->base : NULL;
225 void Draw_Frame(void)
229 static double nextpurgetime;
230 if (nextpurgetime > realtime)
232 nextpurgetime = realtime + 0.05;
233 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
234 if (pic->autoload && pic->skinframe && pic->skinframe->base && pic->lastusedframe < draw_frame - 3)
235 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
239 cachepic_t *Draw_NewPic(const char *picname, int width, int height, unsigned char *pixels_bgra, textype_t textype, int texflags)
244 crc = CRC_Block((unsigned char *)picname, strlen(picname));
245 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
246 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
247 if (!strcmp (picname, pic->name))
252 if (pic->flags & CACHEPICFLAG_NEWPIC && pic->skinframe && pic->skinframe->base && pic->width == width && pic->height == height)
254 R_UpdateTexture(pic->skinframe->base, pixels_bgra, 0, 0, 0, width, height, 1);
255 R_SkinFrame_MarkUsed(pic->skinframe);
256 pic->lastusedframe = draw_frame;
262 if (numcachepics == MAX_CACHED_PICS)
264 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
265 // FIXME: support NULL in callers?
266 return cachepics; // return the first one
268 pic = cachepics + (numcachepics++);
269 memset(pic, 0, sizeof(*pic));
270 strlcpy (pic->name, picname, sizeof(pic->name));
272 pic->chain = cachepichash[hashkey];
273 cachepichash[hashkey] = pic;
276 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
278 pic->autoload = false;
279 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
280 pic->flags |= (texflags & TEXF_CLAMP) ? 0 : CACHEPICFLAG_NOCLAMP;
281 pic->flags |= (texflags & TEXF_FORCENEAREST) ? CACHEPICFLAG_NEAREST : 0;
283 pic->height = height;
284 pic->skinframe = R_SkinFrame_LoadInternalBGRA(picname, texflags | TEXF_FORCE_RELOAD, pixels_bgra, width, height, vid.sRGB2D);
285 pic->lastusedframe = draw_frame;
289 void Draw_FreePic(const char *picname)
294 // this doesn't really free the pic, but does free its texture
295 crc = CRC_Block((unsigned char *)picname, strlen(picname));
296 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
297 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
299 if (!strcmp (picname, pic->name) && pic->skinframe)
301 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
309 static float snap_to_pixel_x(float x, float roundUpAt);
310 extern int con_linewidth; // to force rewrapping
311 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
315 char widthfile[MAX_QPATH];
317 fs_offset_t widthbufsize;
319 if(override || !fnt->texpath[0])
321 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
322 // load the cvars when the font is FIRST loader
323 fnt->settings.scale = scale;
324 fnt->settings.voffset = voffset;
325 fnt->settings.antialias = r_font_antialias.integer;
326 fnt->settings.hinting = r_font_hinting.integer;
327 fnt->settings.outline = r_font_postprocess_outline.value;
328 fnt->settings.blur = r_font_postprocess_blur.value;
329 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
330 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
331 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
334 if (fnt->settings.scale <= 0)
335 fnt->settings.scale = 1;
337 if(drawtexturepool == NULL)
338 return; // before gl_draw_start, so will be loaded later
342 // clear freetype font
343 Font_UnloadFont(fnt->ft2);
348 if(fnt->req_face != -1)
350 if(!Font_LoadFont(fnt->texpath, fnt))
351 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
354 fnt->pic = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0) | CACHEPICFLAG_FAILONMISSING);
355 if(!Draw_IsPicLoaded(fnt->pic))
357 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
359 if (!fnt->fallbacks[i][0])
361 fnt->pic = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0) | CACHEPICFLAG_FAILONMISSING);
362 if(Draw_IsPicLoaded(fnt->pic))
365 if(!Draw_IsPicLoaded(fnt->pic))
367 fnt->pic = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0));
368 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
371 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
374 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
376 // unspecified width == 1 (base width)
377 for(ch = 0; ch < 256; ++ch)
378 fnt->width_of[ch] = 1;
380 // FIXME load "name.width", if it fails, fill all with 1
381 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
383 float extraspacing = 0;
384 const char *p = widthbuf;
389 if(!COM_ParseToken_Simple(&p, false, false, true))
407 fnt->width_of[ch] = atof(com_token) + extraspacing;
411 if(!strcmp(com_token, "extraspacing"))
413 if(!COM_ParseToken_Simple(&p, false, false, true))
415 extraspacing = atof(com_token);
417 else if(!strcmp(com_token, "scale"))
419 if(!COM_ParseToken_Simple(&p, false, false, true))
421 fnt->settings.scale = atof(com_token);
425 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
426 if(!COM_ParseToken_Simple(&p, false, false, true))
438 for (i = 0; i < MAX_FONT_SIZES; ++i)
440 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
443 for(ch = 0; ch < 256; ++ch)
444 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
448 maxwidth = fnt->width_of[0];
449 for(i = 1; i < 256; ++i)
450 maxwidth = max(maxwidth, fnt->width_of[i]);
451 fnt->maxwidth = maxwidth;
453 // fix up maxwidth for overlap
454 fnt->maxwidth *= fnt->settings.scale;
456 if(fnt == FONT_CONSOLE)
457 con_linewidth = -1; // rewrap console in next frame
460 extern cvar_t developer_font;
461 dp_font_t *FindFont(const char *title, qboolean allocate_new)
466 for(i = 0; i < dp_fonts.maxsize; ++i)
467 if(!strcmp(dp_fonts.f[i].title, title))
468 return &dp_fonts.f[i];
469 // if not found - try allocate
472 // find any font with empty title
473 for(i = 0; i < dp_fonts.maxsize; ++i)
475 if(!strcmp(dp_fonts.f[i].title, ""))
477 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
478 return &dp_fonts.f[i];
481 // if no any 'free' fonts - expand buffer
482 oldsize = dp_fonts.maxsize;
483 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
484 if (developer_font.integer)
485 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
486 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
487 // relink ft2 structures
488 for(i = 0; i < oldsize; ++i)
489 if (dp_fonts.f[i].ft2)
490 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
491 // register a font in first expanded slot
492 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
493 return &dp_fonts.f[oldsize];
498 static float snap_to_pixel_x(float x, float roundUpAt)
500 float pixelpos = x * vid.width / vid_conwidth.value;
501 int snap = (int) pixelpos;
502 if (pixelpos - snap >= roundUpAt) ++snap;
503 return ((float)snap * vid_conwidth.value / vid.width);
505 x = (int)(x * vid.width / vid_conwidth.value);
506 x = (x * vid_conwidth.value / vid.width);
511 static float snap_to_pixel_y(float y, float roundUpAt)
513 float pixelpos = y * vid.height / vid_conheight.value;
514 int snap = (int) pixelpos;
515 if (pixelpos - snap > roundUpAt) ++snap;
516 return ((float)snap * vid_conheight.value / vid.height);
518 y = (int)(y * vid.height / vid_conheight.value);
519 y = (y * vid_conheight.value / vid.height);
524 static void LoadFont_f(void)
528 const char *filelist, *c, *cm;
529 float sz, scale, voffset;
530 char mainfont[MAX_QPATH];
534 Con_Printf("Available font commands:\n");
535 for(i = 0; i < dp_fonts.maxsize; ++i)
536 if (dp_fonts.f[i].title[0])
537 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
538 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
539 "can specify multiple fonts and faces\n"
540 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
541 "to load face 2 of the font gfx/vera-sans and use face 1\n"
542 "of gfx/fallback as fallback font.\n"
543 "You can also specify a list of font sizes to load, like this:\n"
544 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
545 "In many cases, 8 12 16 24 32 should be a good choice.\n"
547 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
548 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
552 f = FindFont(Cmd_Argv(1), true);
555 Con_Printf("font function not found\n");
560 filelist = "gfx/conchars";
562 filelist = Cmd_Argv(2);
564 memset(f->fallbacks, 0, sizeof(f->fallbacks));
565 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
567 // first font is handled "normally"
568 c = strchr(filelist, ':');
569 cm = strchr(filelist, ',');
570 if(c && (!cm || c < cm))
571 f->req_face = atoi(c+1);
578 if(!c || (c - filelist) > MAX_QPATH)
579 strlcpy(mainfont, filelist, sizeof(mainfont));
582 memcpy(mainfont, filelist, c - filelist);
583 mainfont[c - filelist] = 0;
586 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
588 c = strchr(filelist, ',');
594 c = strchr(filelist, ':');
595 cm = strchr(filelist, ',');
596 if(c && (!cm || c < cm))
597 f->fallback_faces[i] = atoi(c+1);
600 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
603 if(!c || (c-filelist) > MAX_QPATH)
605 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
609 memcpy(f->fallbacks[i], filelist, c - filelist);
610 f->fallbacks[i][c - filelist] = 0;
614 // for now: by default load only one size: the default size
616 for(i = 1; i < MAX_FONT_SIZES; ++i)
617 f->req_sizes[i] = -1;
623 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
626 if (!strcmp(Cmd_Argv(i), "scale"))
630 scale = atof(Cmd_Argv(i));
633 if (!strcmp(Cmd_Argv(i), "voffset"))
637 voffset = atof(Cmd_Argv(i));
642 continue; // no slot for other sizes
644 // parse one of sizes
645 sz = atof(Cmd_Argv(i));
646 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
648 // search for duplicated sizes
650 for (j=0; j<sizes; j++)
651 if (f->req_sizes[j] == sz)
654 continue; // sz already in req_sizes, don't add it again
656 if (sizes == MAX_FONT_SIZES)
658 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
662 f->req_sizes[sizes] = sz;
668 LoadFont(true, mainfont, f, scale, voffset);
676 static void gl_draw_start(void)
680 drawtexturepool = R_AllocTexturePool();
683 memset(cachepichash, 0, sizeof(cachepichash));
687 // load default font textures
688 for(i = 0; i < dp_fonts.maxsize; ++i)
689 if (dp_fonts.f[i].title[0])
690 LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
692 // draw the loading screen so people have something to see in the newly opened window
693 SCR_UpdateLoadingScreen(true, true);
696 static void gl_draw_shutdown(void)
700 R_FreeTexturePool(&drawtexturepool);
703 memset(cachepichash, 0, sizeof(cachepichash));
706 static void gl_draw_newmap(void)
711 // mark all of the persistent pics so they are not purged...
712 for (i = 0; i < numcachepics; i++)
714 cachepic_t *pic = cachepics + i;
715 if (!pic->autoload && pic->skinframe)
716 R_SkinFrame_MarkUsed(pic->skinframe);
720 void GL_Draw_Init (void)
724 Cvar_RegisterVariable(&r_font_postprocess_blur);
725 Cvar_RegisterVariable(&r_font_postprocess_outline);
726 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
727 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
728 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
729 Cvar_RegisterVariable(&r_font_hinting);
730 Cvar_RegisterVariable(&r_font_antialias);
731 Cvar_RegisterVariable(&r_textshadow);
732 Cvar_RegisterVariable(&r_textbrightness);
733 Cvar_RegisterVariable(&r_textcontrast);
734 Cvar_RegisterVariable(&r_nearest_2d);
735 Cvar_RegisterVariable(&r_nearest_conchars);
737 // allocate fonts storage
738 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
739 dp_fonts.maxsize = MAX_FONTS;
740 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
741 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
743 // assign starting font names
744 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
745 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
746 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
747 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
748 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
749 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
750 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
751 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
752 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
753 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
754 if(!FONT_USER(i)->title[0])
755 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
757 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
758 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
761 void DrawQ_Start(void)
763 r_refdef.draw2dstage = 1;
764 R_ResetViewRendering2D_Common(0, NULL, NULL, 0, 0, vid.width, vid.height, vid_conwidth.integer, vid_conheight.integer);
767 qboolean r_draw2d_force = false;
769 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
771 dp_model_t *mod = CL_Mesh_UI();
775 pic = Draw_CachePic("white");
776 // make sure pic is loaded - we don't use the texture here, Mod_Mesh_GetTexture looks up the skinframe by name
777 Draw_GetPicTexture(pic);
781 height = pic->height;
782 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
783 e0 = Mod_Mesh_IndexForVertex(mod, surf, x , y , 0, 0, 0, -1, 0, 0, 0, 0, red, green, blue, alpha);
784 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y , 0, 0, 0, -1, 1, 0, 0, 0, red, green, blue, alpha);
785 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y + height, 0, 0, 0, -1, 1, 1, 0, 0, red, green, blue, alpha);
786 e3 = Mod_Mesh_IndexForVertex(mod, surf, x , y + height, 0, 0, 0, -1, 0, 1, 0, 0, red, green, blue, alpha);
787 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
788 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
791 void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, float org_x, float org_y, float angle, float red, float green, float blue, float alpha, int flags)
793 float af = DEG2RAD(-angle); // forward
794 float ar = DEG2RAD(-angle + 90); // right
795 float sinaf = sin(af);
796 float cosaf = cos(af);
797 float sinar = sin(ar);
798 float cosar = cos(ar);
799 dp_model_t *mod = CL_Mesh_UI();
803 pic = Draw_CachePic("white");
804 // make sure pic is loaded - we don't use the texture here, Mod_Mesh_GetTexture looks up the skinframe by name
805 Draw_GetPicTexture(pic);
809 height = pic->height;
810 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
811 e0 = Mod_Mesh_IndexForVertex(mod, surf, x - cosaf * org_x - cosar * org_y , y - sinaf * org_x - sinar * org_y , 0, 0, 0, -1, 0, 0, 0, 0, red, green, blue, alpha);
812 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + cosaf * (width - org_x) - cosar * org_y , y + sinaf * (width - org_x) - sinar * org_y , 0, 0, 0, -1, 1, 0, 0, 0, red, green, blue, alpha);
813 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + cosaf * (width - org_x) + cosar * (height - org_y), y + sinaf * (width - org_x) + sinar * (height - org_y), 0, 0, 0, -1, 1, 1, 0, 0, red, green, blue, alpha);
814 e3 = Mod_Mesh_IndexForVertex(mod, surf, x - cosaf * org_x + cosar * (height - org_y), y - sinaf * org_x + sinar * (height - org_y), 0, 0, 0, -1, 0, 1, 0, 0, red, green, blue, alpha);
815 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
816 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
819 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
821 DrawQ_Pic(x, y, Draw_CachePic("white"), width, height, red, green, blue, alpha, flags);
824 /// color tag printing
825 static const vec4_t string_colors[] =
828 // LordHavoc: why on earth is cyan before magenta in Quake3?
829 // LordHavoc: note: Doom3 uses white for [0] and [7]
830 {0.0, 0.0, 0.0, 1.0}, // black
831 {1.0, 0.0, 0.0, 1.0}, // red
832 {0.0, 1.0, 0.0, 1.0}, // green
833 {1.0, 1.0, 0.0, 1.0}, // yellow
834 {0.0, 0.0, 1.0, 1.0}, // blue
835 {0.0, 1.0, 1.0, 1.0}, // cyan
836 {1.0, 0.0, 1.0, 1.0}, // magenta
837 {1.0, 1.0, 1.0, 1.0}, // white
838 // [515]'s BX_COLOREDTEXT extension
839 {1.0, 1.0, 1.0, 0.5}, // half transparent
840 {0.5, 0.5, 0.5, 1.0} // half brightness
841 // Black's color table
842 //{1.0, 1.0, 1.0, 1.0},
843 //{1.0, 0.0, 0.0, 1.0},
844 //{0.0, 1.0, 0.0, 1.0},
845 //{0.0, 0.0, 1.0, 1.0},
846 //{1.0, 1.0, 0.0, 1.0},
847 //{0.0, 1.0, 1.0, 1.0},
848 //{1.0, 0.0, 1.0, 1.0},
849 //{0.1, 0.1, 0.1, 1.0}
852 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
854 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
856 float C = r_textcontrast.value;
857 float B = r_textbrightness.value;
858 if (colorindex & 0x10000) // that bit means RGB color
860 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
861 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
862 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
863 color[3] = (colorindex & 0xf) / 15.0;
866 Vector4Copy(string_colors[colorindex], color);
867 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
870 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
871 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
875 // NOTE: this function always draws exactly one character if maxwidth <= 0
876 float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *maxlen, float w, float h, float sw, float sh, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
878 const char *text_start = text;
879 int colorindex = STRING_COLOR_DEFAULT;
882 Uchar ch, mapch, nextch;
883 Uchar prevch = 0; // used for kerning
888 ft2_font_map_t *fontmap = NULL;
889 ft2_font_map_t *map = NULL;
890 //ft2_font_map_t *prevmap = NULL;
891 ft2_font_t *ft2 = fnt->ft2;
893 qboolean snap = true;
894 qboolean least_one = false;
895 float dw; // display w
896 //float dh; // display h
897 const float *width_of;
904 // do this in the end
905 w *= fnt->settings.scale;
906 h *= fnt->settings.scale;
908 // find the most fitting size:
912 map_index = Font_IndexForSize(ft2, h, &w, &h);
914 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
915 fontmap = Font_MapForIndex(ft2, map_index);
924 if (!outcolor || *outcolor == -1)
925 colorindex = STRING_COLOR_DEFAULT;
927 colorindex = *outcolor;
929 // maxwidth /= fnt->scale; // w and h are multiplied by it already
930 // ftbase_x = snap_to_pixel_x(0);
935 maxwidth = -maxwidth;
939 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
942 width_of = fontmap->width_of;
944 width_of = fnt->width_of;
947 while (((bytes_left = *maxlen - (text - text_start)) > 0) && *text)
950 nextch = ch = u8_getnchar(text, &text, bytes_left);
951 i = text - text_start;
954 if (ch == ' ' && !fontmap)
956 if(!least_one || i0) // never skip the first character
957 if(x + width_of[(int) ' '] * dw > maxwidth)
960 break; // oops, can't draw this
962 x += width_of[(int) ' '] * dw;
965 // i points to the char after ^
966 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
968 ch = *text; // colors are ascii, so no u8_ needed
969 if (ch <= '9' && ch >= '0') // ^[0-9] found
971 colorindex = ch - '0';
976 // i points to the char after ^...
977 // i+3 points to 3 in ^x123
978 // i+3 == *maxlen would mean that char is missing
979 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
981 // building colorindex...
982 ch = tolower(text[1]);
983 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
984 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
985 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
986 else tempcolorindex = 0;
989 ch = tolower(text[2]);
990 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
991 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
992 else tempcolorindex = 0;
995 ch = tolower(text[3]);
996 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
997 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
998 else tempcolorindex = 0;
1001 colorindex = tempcolorindex | 0xf;
1002 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1010 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1019 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1026 map = ft2_oldstyle_map;
1028 if(!least_one || i0) // never skip the first character
1029 if(x + width_of[ch] * dw > maxwidth)
1032 break; // oops, can't draw this
1034 x += width_of[ch] * dw;
1036 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1038 map = FontMap_FindForChar(fontmap, ch);
1041 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1047 mapch = ch - map->start;
1048 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1050 x += map->glyphs[mapch].advance_x * dw;
1059 *outcolor = colorindex;
1064 float DrawQ_Color[4];
1065 float DrawQ_String_Scale(float startx, float starty, const char *text, size_t maxlen, float w, float h, float sw, float sh, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1067 int shadow, colorindex = STRING_COLOR_DEFAULT;
1069 float x = startx, y, s, t, u, v, thisw;
1070 Uchar ch, mapch, nextch;
1071 Uchar prevch = 0; // used for kerning
1074 //ft2_font_map_t *prevmap = NULL; // the previous map
1075 ft2_font_map_t *map = NULL; // the currently used map
1076 ft2_font_map_t *fontmap = NULL; // the font map for the size
1078 const char *text_start = text;
1080 ft2_font_t *ft2 = fnt->ft2;
1081 qboolean snap = true;
1085 const float *width_of;
1086 dp_model_t *mod = CL_Mesh_UI();
1087 msurface_t *surf = NULL;
1090 tw = Draw_GetPicWidth(fnt->pic);
1091 th = Draw_GetPicHeight(fnt->pic);
1099 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1100 w *= fnt->settings.scale;
1101 h *= fnt->settings.scale;
1106 map_index = Font_IndexForSize(ft2, h, &w, &h);
1108 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1109 fontmap = Font_MapForIndex(ft2, map_index);
1115 // draw the font at its baseline when using freetype
1117 ftbase_y = dh * (4.5/6.0);
1122 if(!r_draw2d.integer && !r_draw2d_force)
1123 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1125 //ftbase_x = snap_to_pixel_x(ftbase_x);
1128 startx = snap_to_pixel_x(startx, 0.4);
1129 starty = snap_to_pixel_y(starty, 0.4);
1130 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1133 pix_x = vid.width / vid_conwidth.value;
1134 pix_y = vid.height / vid_conheight.value;
1137 width_of = fontmap->width_of;
1139 width_of = fnt->width_of;
1141 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1146 if (!outcolor || *outcolor == -1)
1147 colorindex = STRING_COLOR_DEFAULT;
1149 colorindex = *outcolor;
1151 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1158 x += r_textshadow.value * vid.width / vid_conwidth.value;
1159 y += r_textshadow.value * vid.height / vid_conheight.value;
1162 while (((bytes_left = maxlen - (text - text_start)) > 0) && *text)
1164 nextch = ch = u8_getnchar(text, &text, bytes_left);
1165 i = text - text_start;
1168 if (ch == ' ' && !fontmap)
1170 x += width_of[(int) ' '] * dw;
1173 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1175 ch = *text; // colors are ascii, so no u8_ needed
1176 if (ch <= '9' && ch >= '0') // ^[0-9] found
1178 colorindex = ch - '0';
1179 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1184 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1186 // building colorindex...
1187 ch = tolower(text[1]);
1188 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1189 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1190 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1191 else tempcolorindex = 0;
1194 ch = tolower(text[2]);
1195 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1196 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1197 else tempcolorindex = 0;
1200 ch = tolower(text[3]);
1201 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1202 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1203 else tempcolorindex = 0;
1206 colorindex = tempcolorindex | 0xf;
1207 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1208 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1209 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1217 else if (ch == STRING_COLOR_TAG)
1226 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1227 // this way we don't need to rebind fnt->tex for every old-style character
1228 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1231 x += 1.0/pix_x * r_textshadow.value;
1232 y += 1.0/pix_y * r_textshadow.value;
1234 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1241 map = ft2_oldstyle_map;
1243 //num = (unsigned char) text[i];
1244 //thisw = fnt->width_of[num];
1245 thisw = fnt->width_of[ch];
1246 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1247 if (r_nearest_conchars.integer)
1249 s = (ch & 15)*0.0625f;
1250 t = (ch >> 4)*0.0625f;
1251 u = 0.0625f * thisw;
1256 s = (ch & 15)*0.0625f + (0.5f / tw);
1257 t = (ch >> 4)*0.0625f + (0.5f / th);
1258 u = 0.0625f * thisw - (1.0f / tw);
1259 v = 0.0625f - (1.0f / th);
1261 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, fnt->pic->name, flags, TEXF_ALPHA | TEXF_CLAMP, MATERIALFLAG_VERTEXCOLOR), true);
1262 e0 = Mod_Mesh_IndexForVertex(mod, surf, x , y , 10, 0, 0, -1, s , t , 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1263 e1 = Mod_Mesh_IndexForVertex(mod, surf, x+dw*thisw, y , 10, 0, 0, -1, s+u, t , 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1264 e2 = Mod_Mesh_IndexForVertex(mod, surf, x+dw*thisw, y+dh, 10, 0, 0, -1, s+u, t+v, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1265 e3 = Mod_Mesh_IndexForVertex(mod, surf, x , y+dh, 10, 0, 0, -1, s , t+v, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1266 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1267 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1268 x += width_of[ch] * dw;
1270 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1273 map = FontMap_FindForChar(fontmap, ch);
1276 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1283 // this shouldn't happen
1290 mapch = ch - map->start;
1291 thisw = map->glyphs[mapch].advance_x;
1295 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1302 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, map->pic->name, flags, TEXF_ALPHA | TEXF_CLAMP, MATERIALFLAG_VERTEXCOLOR), true);
1303 e0 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmin, y + dh * map->glyphs[mapch].vymin, 10, 0, 0, -1, map->glyphs[mapch].txmin, map->glyphs[mapch].tymin, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1304 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmax, y + dh * map->glyphs[mapch].vymin, 10, 0, 0, -1, map->glyphs[mapch].txmax, map->glyphs[mapch].tymin, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1305 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmax, y + dh * map->glyphs[mapch].vymax, 10, 0, 0, -1, map->glyphs[mapch].txmax, map->glyphs[mapch].tymax, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1306 e3 = Mod_Mesh_IndexForVertex(mod, surf, x + dw * map->glyphs[mapch].vxmin, y + dh * map->glyphs[mapch].vymax, 10, 0, 0, -1, map->glyphs[mapch].txmin, map->glyphs[mapch].tymax, 0, 0, DrawQ_Color[0], DrawQ_Color[1], DrawQ_Color[2], DrawQ_Color[3]);
1307 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1308 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1320 x -= 1.0/pix_x * r_textshadow.value;
1321 y -= 1.0/pix_y * r_textshadow.value;
1327 *outcolor = colorindex;
1329 // note: this relies on the proper text (not shadow) being drawn last
1333 float DrawQ_String(float startx, float starty, const char *text, size_t maxlen, float w, float h, float basered, float basegreen, float baseblue, float basealpha, int flags, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt)
1335 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1338 float DrawQ_TextWidth_UntilWidth_TrackColors(const char *text, size_t *maxlen, float w, float h, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1340 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1343 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1345 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1348 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1350 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1355 // no ^xrgb management
1356 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1358 int color, numchars = 0;
1359 char *outputend2c = output2c + maxoutchars - 2;
1360 if (!outcolor || *outcolor == -1)
1361 color = STRING_COLOR_DEFAULT;
1365 maxreadchars = 1<<30;
1366 textend = text + maxreadchars;
1367 while (text != textend && *text)
1369 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1371 if (text[1] == STRING_COLOR_TAG)
1373 else if (text[1] >= '0' && text[1] <= '9')
1375 color = text[1] - '0';
1380 if (output2c >= outputend2c)
1382 *output2c++ = *text++;
1383 *output2c++ = color;
1386 output2c[0] = output2c[1] = 0;
1393 void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height, float s1, float t1, float r1, float g1, float b1, float a1, float s2, float t2, float r2, float g2, float b2, float a2, float s3, float t3, float r3, float g3, float b3, float a3, float s4, float t4, float r4, float g4, float b4, float a4, int flags)
1395 dp_model_t *mod = CL_Mesh_UI();
1399 pic = Draw_CachePic("white");
1400 // make sure pic is loaded - we don't use the texture here, Mod_Mesh_GetTexture looks up the skinframe by name
1401 Draw_GetPicTexture(pic);
1405 height = pic->height;
1406 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, pic->name, flags, pic->texflags, MATERIALFLAG_VERTEXCOLOR), true);
1407 e0 = Mod_Mesh_IndexForVertex(mod, surf, x , y , 0, 0, 0, -1, s1, t1, 0, 0, r1, g1, b1, a1);
1408 e1 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y , 0, 0, 0, -1, s2, t2, 0, 0, r2, g2, b2, a2);
1409 e2 = Mod_Mesh_IndexForVertex(mod, surf, x + width, y + height, 0, 0, 0, -1, s4, t4, 0, 0, r4, g4, b4, a4);
1410 e3 = Mod_Mesh_IndexForVertex(mod, surf, x , y + height, 0, 0, 0, -1, s3, t3, 0, 0, r3, g3, b3, a3);
1411 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1412 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1415 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1417 dp_model_t *mod = CL_Mesh_UI();
1420 float offsetx, offsety;
1421 // width is measured in real pixels
1422 if (fabs(x2 - x1) > fabs(y2 - y1))
1425 offsety = 0.5f * width * vid_conheight.value / vid.height;
1429 offsetx = 0.5f * width * vid_conwidth.value / vid.width;
1432 surf = Mod_Mesh_AddSurface(mod, Mod_Mesh_GetTexture(mod, "white", 0, 0, MATERIALFLAG_VERTEXCOLOR), true);
1433 e0 = Mod_Mesh_IndexForVertex(mod, surf, x1 - offsetx, y1 - offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1434 e1 = Mod_Mesh_IndexForVertex(mod, surf, x2 - offsetx, y2 - offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1435 e2 = Mod_Mesh_IndexForVertex(mod, surf, x2 + offsetx, y2 + offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1436 e3 = Mod_Mesh_IndexForVertex(mod, surf, x1 + offsetx, y1 + offsety, 10, 0, 0, -1, 0, 0, 0, 0, r, g, b, alpha);
1437 Mod_Mesh_AddTriangle(mod, surf, e0, e1, e2);
1438 Mod_Mesh_AddTriangle(mod, surf, e0, e2, e3);
1441 void DrawQ_SetClipArea(float x, float y, float width, float height)
1446 // We have to convert the con coords into real coords
1447 // OGL uses bottom to top (origin is in bottom left)
1448 ix = (int)(0.5 + x * ((float)r_refdef.view.width / vid_conwidth.integer)) + r_refdef.view.x;
1449 iy = (int)(0.5 + y * ((float)r_refdef.view.height / vid_conheight.integer)) + r_refdef.view.y;
1450 iw = (int)(0.5 + width * ((float)r_refdef.view.width / vid_conwidth.integer));
1451 ih = (int)(0.5 + height * ((float)r_refdef.view.height / vid_conheight.integer));
1452 switch(vid.renderpath)
1454 case RENDERPATH_GL32:
1455 case RENDERPATH_GLES2:
1456 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1460 GL_ScissorTest(true);
1463 void DrawQ_ResetClipArea(void)
1466 GL_ScissorTest(false);
1469 void DrawQ_Finish(void)
1472 r_refdef.draw2dstage = 0;
1475 void DrawQ_RecalcView(void)
1478 if(r_refdef.draw2dstage)
1479 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
1482 void DrawQ_FlushUI(void)
1485 dp_model_t *mod = CL_Mesh_UI();
1486 if (mod->num_surfaces == 0)
1489 if (!r_draw2d.integer && !r_draw2d_force)
1491 Mod_Mesh_Reset(mod);
1495 // TODO: render the mesh using R_Q1BSP_Draw or similar, for full material support.
1496 GL_DepthMask(false);
1497 R_Mesh_PrepareVertices_Generic_Arrays(mod->surfmesh.num_vertices, mod->surfmesh.data_vertex3f, mod->surfmesh.data_lightmapcolor4f, mod->surfmesh.data_texcoordtexture2f);
1498 for (i = 0; i < mod->num_surfaces; i++)
1500 msurface_t *surf = mod->data_surfaces + i;
1501 texture_t *tex = surf->texture;
1502 if (tex->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND)
1503 GL_BlendFunc(tex->customblendfunc[0], tex->customblendfunc[1]);
1504 else if (tex->currentmaterialflags & MATERIALFLAG_ADD)
1505 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1506 else if (tex->currentmaterialflags & MATERIALFLAG_ALPHA)
1507 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1509 GL_BlendFunc(GL_ONE, GL_ZERO);
1510 R_SetupShader_Generic(tex->currentskinframe->base, (tex->currentmaterialflags & MATERIALFLAG_CUSTOMBLEND) ? false : true, true, false);
1511 R_Mesh_Draw(surf->num_firstvertex, surf->num_vertices, surf->num_firsttriangle, surf->num_triangles, mod->surfmesh.data_element3i, NULL, 0, mod->surfmesh.data_element3s, NULL, 0);
1514 Mod_Mesh_Reset(mod);