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)
111 R_SkinFrame_MarkUsed(pic->skinframe);
112 pic->lastusedframe = draw_frame;
115 if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS | TEXF_MIPMAP))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag, and ignore TEXF_MIPMAP because QC specifies that
117 if (!pic->skinframe || !pic->skinframe->base)
119 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
120 pic->autoload = false; // caller is making this pic persistent
121 R_SkinFrame_MarkUsed(pic->skinframe);
122 pic->lastusedframe = draw_frame;
128 if (numcachepics == MAX_CACHED_PICS)
130 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
131 // FIXME: support NULL in callers?
132 return cachepics; // return the first one
134 pic = cachepics + (numcachepics++);
135 memset(pic, 0, sizeof(*pic));
136 strlcpy (pic->name, path, sizeof(pic->name));
138 pic->chain = cachepichash[hashkey];
139 cachepichash[hashkey] = pic;
143 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
145 pic->flags = cachepicflags;
146 pic->texflags = texflags;
147 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT) != 0;
148 pic->lastusedframe = draw_frame;
150 // load high quality image (this falls back to low quality too)
151 pic->skinframe = R_SkinFrame_LoadExternal(pic->name, texflags, (cachepicflags & CACHEPICFLAG_QUIET) == 0, (cachepicflags & CACHEPICFLAG_FAILONMISSING) == 0);
153 // get the dimensions of the image we loaded (if it was successful)
154 if (pic->skinframe && pic->skinframe->base)
156 pic->width = R_TextureWidth(pic->skinframe->base);
157 pic->height = R_TextureHeight(pic->skinframe->base);
160 // check for a low quality version of the pic and use its size if possible, to match the stock hud
161 Image_GetStockPicSize(pic->name, &pic->width, &pic->height);
166 cachepic_t *Draw_CachePic (const char *path)
168 return Draw_CachePic_Flags (path, 0); // default to persistent!
171 const char *Draw_GetPicName(cachepic_t *pic)
178 int Draw_GetPicWidth(cachepic_t *pic)
185 int Draw_GetPicHeight(cachepic_t *pic)
192 qboolean Draw_IsPicLoaded(cachepic_t *pic)
196 if (pic->autoload && (!pic->skinframe || !pic->skinframe->base))
197 pic->skinframe = R_SkinFrame_LoadExternal(pic->name, pic->texflags, false, true);
198 // skinframe will only be NULL if the pic was created with CACHEPICFLAG_FAILONMISSING and not found
199 return pic->skinframe != NULL && pic->skinframe->base != NULL;
202 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
206 if (pic->autoload && (!pic->skinframe || !pic->skinframe->base))
207 pic->skinframe = R_SkinFrame_LoadExternal(pic->name, pic->texflags, false, true);
208 pic->lastusedframe = draw_frame;
209 return pic->skinframe ? pic->skinframe->base : NULL;
212 void Draw_Frame(void)
216 static double nextpurgetime;
217 if (nextpurgetime > realtime)
219 nextpurgetime = realtime + 0.05;
220 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
221 if (pic->autoload && pic->skinframe && pic->skinframe->base && pic->lastusedframe < draw_frame)
222 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
226 cachepic_t *Draw_NewPic(const char *picname, int width, int height, unsigned char *pixels_bgra, textype_t textype, int texflags)
231 crc = CRC_Block((unsigned char *)picname, strlen(picname));
232 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
233 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
234 if (!strcmp (picname, pic->name))
239 if (pic->flags & CACHEPICFLAG_NEWPIC && pic->skinframe && pic->skinframe->base && pic->width == width && pic->height == height)
241 R_UpdateTexture(pic->skinframe->base, pixels_bgra, 0, 0, 0, width, height, 1);
242 R_SkinFrame_MarkUsed(pic->skinframe);
243 pic->lastusedframe = draw_frame;
249 if (numcachepics == MAX_CACHED_PICS)
251 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
252 // FIXME: support NULL in callers?
253 return cachepics; // return the first one
255 pic = cachepics + (numcachepics++);
256 memset(pic, 0, sizeof(*pic));
257 strlcpy (pic->name, picname, sizeof(pic->name));
259 pic->chain = cachepichash[hashkey];
260 cachepichash[hashkey] = pic;
263 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
265 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
266 pic->flags |= (texflags & TEXF_CLAMP) ? 0 : CACHEPICFLAG_NOCLAMP;
267 pic->flags |= (texflags & TEXF_FORCENEAREST) ? CACHEPICFLAG_NEAREST : 0;
269 pic->height = height;
270 pic->skinframe = R_SkinFrame_LoadInternalBGRA(picname, texflags, pixels_bgra, width, height, vid.sRGB2D);
271 pic->lastusedframe = draw_frame;
275 void Draw_FreePic(const char *picname)
280 // this doesn't really free the pic, but does free its texture
281 crc = CRC_Block((unsigned char *)picname, strlen(picname));
282 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
283 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
285 if (!strcmp (picname, pic->name) && pic->skinframe)
287 R_SkinFrame_PurgeSkinFrame(pic->skinframe);
295 static float snap_to_pixel_x(float x, float roundUpAt);
296 extern int con_linewidth; // to force rewrapping
297 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
301 char widthfile[MAX_QPATH];
303 fs_offset_t widthbufsize;
305 if(override || !fnt->texpath[0])
307 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
308 // load the cvars when the font is FIRST loader
309 fnt->settings.scale = scale;
310 fnt->settings.voffset = voffset;
311 fnt->settings.antialias = r_font_antialias.integer;
312 fnt->settings.hinting = r_font_hinting.integer;
313 fnt->settings.outline = r_font_postprocess_outline.value;
314 fnt->settings.blur = r_font_postprocess_blur.value;
315 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
316 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
317 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
320 if (fnt->settings.scale <= 0)
321 fnt->settings.scale = 1;
323 if(drawtexturepool == NULL)
324 return; // before gl_draw_start, so will be loaded later
328 // clear freetype font
329 Font_UnloadFont(fnt->ft2);
334 if(fnt->req_face != -1)
336 if(!Font_LoadFont(fnt->texpath, fnt))
337 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
340 fnt->pic = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0) | CACHEPICFLAG_FAILONMISSING);
341 if(!Draw_IsPicLoaded(fnt->pic))
343 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
345 if (!fnt->fallbacks[i][0])
347 fnt->pic = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0) | CACHEPICFLAG_FAILONMISSING);
348 if(Draw_IsPicLoaded(fnt->pic))
351 if(!Draw_IsPicLoaded(fnt->pic))
353 fnt->pic = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0));
354 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
357 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
360 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
362 // unspecified width == 1 (base width)
363 for(ch = 0; ch < 256; ++ch)
364 fnt->width_of[ch] = 1;
366 // FIXME load "name.width", if it fails, fill all with 1
367 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
369 float extraspacing = 0;
370 const char *p = widthbuf;
375 if(!COM_ParseToken_Simple(&p, false, false, true))
393 fnt->width_of[ch] = atof(com_token) + extraspacing;
397 if(!strcmp(com_token, "extraspacing"))
399 if(!COM_ParseToken_Simple(&p, false, false, true))
401 extraspacing = atof(com_token);
403 else if(!strcmp(com_token, "scale"))
405 if(!COM_ParseToken_Simple(&p, false, false, true))
407 fnt->settings.scale = atof(com_token);
411 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
412 if(!COM_ParseToken_Simple(&p, false, false, true))
424 for (i = 0; i < MAX_FONT_SIZES; ++i)
426 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
429 for(ch = 0; ch < 256; ++ch)
430 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
434 maxwidth = fnt->width_of[0];
435 for(i = 1; i < 256; ++i)
436 maxwidth = max(maxwidth, fnt->width_of[i]);
437 fnt->maxwidth = maxwidth;
439 // fix up maxwidth for overlap
440 fnt->maxwidth *= fnt->settings.scale;
442 if(fnt == FONT_CONSOLE)
443 con_linewidth = -1; // rewrap console in next frame
446 extern cvar_t developer_font;
447 dp_font_t *FindFont(const char *title, qboolean allocate_new)
452 for(i = 0; i < dp_fonts.maxsize; ++i)
453 if(!strcmp(dp_fonts.f[i].title, title))
454 return &dp_fonts.f[i];
455 // if not found - try allocate
458 // find any font with empty title
459 for(i = 0; i < dp_fonts.maxsize; ++i)
461 if(!strcmp(dp_fonts.f[i].title, ""))
463 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
464 return &dp_fonts.f[i];
467 // if no any 'free' fonts - expand buffer
468 oldsize = dp_fonts.maxsize;
469 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
470 if (developer_font.integer)
471 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
472 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
473 // relink ft2 structures
474 for(i = 0; i < oldsize; ++i)
475 if (dp_fonts.f[i].ft2)
476 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
477 // register a font in first expanded slot
478 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
479 return &dp_fonts.f[oldsize];
484 static float snap_to_pixel_x(float x, float roundUpAt)
486 float pixelpos = x * vid.width / vid_conwidth.value;
487 int snap = (int) pixelpos;
488 if (pixelpos - snap >= roundUpAt) ++snap;
489 return ((float)snap * vid_conwidth.value / vid.width);
491 x = (int)(x * vid.width / vid_conwidth.value);
492 x = (x * vid_conwidth.value / vid.width);
497 static float snap_to_pixel_y(float y, float roundUpAt)
499 float pixelpos = y * vid.height / vid_conheight.value;
500 int snap = (int) pixelpos;
501 if (pixelpos - snap > roundUpAt) ++snap;
502 return ((float)snap * vid_conheight.value / vid.height);
504 y = (int)(y * vid.height / vid_conheight.value);
505 y = (y * vid_conheight.value / vid.height);
510 static void LoadFont_f(void)
514 const char *filelist, *c, *cm;
515 float sz, scale, voffset;
516 char mainfont[MAX_QPATH];
520 Con_Printf("Available font commands:\n");
521 for(i = 0; i < dp_fonts.maxsize; ++i)
522 if (dp_fonts.f[i].title[0])
523 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
524 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
525 "can specify multiple fonts and faces\n"
526 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
527 "to load face 2 of the font gfx/vera-sans and use face 1\n"
528 "of gfx/fallback as fallback font.\n"
529 "You can also specify a list of font sizes to load, like this:\n"
530 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
531 "In many cases, 8 12 16 24 32 should be a good choice.\n"
533 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
534 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
538 f = FindFont(Cmd_Argv(1), true);
541 Con_Printf("font function not found\n");
546 filelist = "gfx/conchars";
548 filelist = Cmd_Argv(2);
550 memset(f->fallbacks, 0, sizeof(f->fallbacks));
551 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
553 // first font is handled "normally"
554 c = strchr(filelist, ':');
555 cm = strchr(filelist, ',');
556 if(c && (!cm || c < cm))
557 f->req_face = atoi(c+1);
564 if(!c || (c - filelist) > MAX_QPATH)
565 strlcpy(mainfont, filelist, sizeof(mainfont));
568 memcpy(mainfont, filelist, c - filelist);
569 mainfont[c - filelist] = 0;
572 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
574 c = strchr(filelist, ',');
580 c = strchr(filelist, ':');
581 cm = strchr(filelist, ',');
582 if(c && (!cm || c < cm))
583 f->fallback_faces[i] = atoi(c+1);
586 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
589 if(!c || (c-filelist) > MAX_QPATH)
591 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
595 memcpy(f->fallbacks[i], filelist, c - filelist);
596 f->fallbacks[i][c - filelist] = 0;
600 // for now: by default load only one size: the default size
602 for(i = 1; i < MAX_FONT_SIZES; ++i)
603 f->req_sizes[i] = -1;
609 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
612 if (!strcmp(Cmd_Argv(i), "scale"))
616 scale = atof(Cmd_Argv(i));
619 if (!strcmp(Cmd_Argv(i), "voffset"))
623 voffset = atof(Cmd_Argv(i));
628 continue; // no slot for other sizes
630 // parse one of sizes
631 sz = atof(Cmd_Argv(i));
632 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
634 // search for duplicated sizes
636 for (j=0; j<sizes; j++)
637 if (f->req_sizes[j] == sz)
640 continue; // sz already in req_sizes, don't add it again
642 if (sizes == MAX_FONT_SIZES)
644 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
648 f->req_sizes[sizes] = sz;
654 LoadFont(true, mainfont, f, scale, voffset);
662 static void gl_draw_start(void)
666 drawtexturepool = R_AllocTexturePool();
669 memset(cachepichash, 0, sizeof(cachepichash));
673 // load default font textures
674 for(i = 0; i < dp_fonts.maxsize; ++i)
675 if (dp_fonts.f[i].title[0])
676 LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
678 // draw the loading screen so people have something to see in the newly opened window
679 SCR_UpdateLoadingScreen(true, true);
682 static void gl_draw_shutdown(void)
686 R_FreeTexturePool(&drawtexturepool);
689 memset(cachepichash, 0, sizeof(cachepichash));
692 static void gl_draw_newmap(void)
697 // mark all of the persistent pics so they are not purged...
698 for (i = 0; i < numcachepics; i++)
700 cachepic_t *pic = cachepics + i;
701 if (!pic->autoload && pic->skinframe)
702 R_SkinFrame_MarkUsed(pic->skinframe);
706 void GL_Draw_Init (void)
710 Cvar_RegisterVariable(&r_font_postprocess_blur);
711 Cvar_RegisterVariable(&r_font_postprocess_outline);
712 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
713 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
714 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
715 Cvar_RegisterVariable(&r_font_hinting);
716 Cvar_RegisterVariable(&r_font_antialias);
717 Cvar_RegisterVariable(&r_textshadow);
718 Cvar_RegisterVariable(&r_textbrightness);
719 Cvar_RegisterVariable(&r_textcontrast);
720 Cvar_RegisterVariable(&r_nearest_2d);
721 Cvar_RegisterVariable(&r_nearest_conchars);
723 // allocate fonts storage
724 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
725 dp_fonts.maxsize = MAX_FONTS;
726 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
727 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
729 // assign starting font names
730 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
731 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
732 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
733 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
734 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
735 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
736 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
737 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
738 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
739 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
740 if(!FONT_USER(i)->title[0])
741 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
743 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
744 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
747 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
749 if (r_refdef.draw2dstage == 1)
751 r_refdef.draw2dstage = 1;
753 R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
756 qboolean r_draw2d_force = false;
757 static void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
760 if(!r_draw2d.integer && !r_draw2d_force)
762 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->skinframe && pic->skinframe->hasalpha));
764 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
766 if(flags == DRAWFLAG_ADDITIVE)
769 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
771 else if(flags == DRAWFLAG_MODULATE)
774 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
776 else if(flags == DRAWFLAG_2XMODULATE)
779 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
781 else if(flags == DRAWFLAG_SCREEN)
784 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
789 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
794 GL_BlendFunc(GL_ONE, GL_ZERO);
798 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
802 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
803 if(!r_draw2d.integer && !r_draw2d_force)
806 // R_Mesh_ResetTextureState();
807 floats[12] = 0.0f;floats[13] = 0.0f;
808 floats[14] = 1.0f;floats[15] = 0.0f;
809 floats[16] = 1.0f;floats[17] = 1.0f;
810 floats[18] = 0.0f;floats[19] = 1.0f;
811 floats[20] = floats[24] = floats[28] = floats[32] = red;
812 floats[21] = floats[25] = floats[29] = floats[33] = green;
813 floats[22] = floats[26] = floats[30] = floats[34] = blue;
814 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
820 height = pic->height;
821 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
824 // AK07: lets be texel correct on the corners
826 float horz_offset = 0.5f / pic->width;
827 float vert_offset = 0.5f / pic->height;
829 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
830 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
831 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
832 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
837 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
839 floats[2] = floats[5] = floats[8] = floats[11] = 0;
840 floats[0] = floats[9] = x;
841 floats[1] = floats[4] = y;
842 floats[3] = floats[6] = x + width;
843 floats[7] = floats[10] = y + height;
845 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
846 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
849 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)
852 float af = DEG2RAD(-angle); // forward
853 float ar = DEG2RAD(-angle + 90); // right
854 float sinaf = sin(af);
855 float cosaf = cos(af);
856 float sinar = sin(ar);
857 float cosar = cos(ar);
859 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
860 if(!r_draw2d.integer && !r_draw2d_force)
863 // R_Mesh_ResetTextureState();
869 height = pic->height;
870 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
873 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
875 floats[2] = floats[5] = floats[8] = floats[11] = 0;
878 floats[0] = x - cosaf*org_x - cosar*org_y;
879 floats[1] = y - sinaf*org_x - sinar*org_y;
882 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
883 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
886 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
887 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
890 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
891 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
893 floats[12] = 0.0f;floats[13] = 0.0f;
894 floats[14] = 1.0f;floats[15] = 0.0f;
895 floats[16] = 1.0f;floats[17] = 1.0f;
896 floats[18] = 0.0f;floats[19] = 1.0f;
897 floats[20] = floats[24] = floats[28] = floats[32] = red;
898 floats[21] = floats[25] = floats[29] = floats[33] = green;
899 floats[22] = floats[26] = floats[30] = floats[34] = blue;
900 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
902 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
903 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
906 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
910 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
911 if(!r_draw2d.integer && !r_draw2d_force)
914 // R_Mesh_ResetTextureState();
915 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
917 floats[2] = floats[5] = floats[8] = floats[11] = 0;
918 floats[0] = floats[9] = x;
919 floats[1] = floats[4] = y;
920 floats[3] = floats[6] = x + width;
921 floats[7] = floats[10] = y + height;
922 floats[12] = 0.0f;floats[13] = 0.0f;
923 floats[14] = 1.0f;floats[15] = 0.0f;
924 floats[16] = 1.0f;floats[17] = 1.0f;
925 floats[18] = 0.0f;floats[19] = 1.0f;
926 floats[20] = floats[24] = floats[28] = floats[32] = red;
927 floats[21] = floats[25] = floats[29] = floats[33] = green;
928 floats[22] = floats[26] = floats[30] = floats[34] = blue;
929 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
931 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
932 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
935 /// color tag printing
936 static const vec4_t string_colors[] =
939 // LordHavoc: why on earth is cyan before magenta in Quake3?
940 // LordHavoc: note: Doom3 uses white for [0] and [7]
941 {0.0, 0.0, 0.0, 1.0}, // black
942 {1.0, 0.0, 0.0, 1.0}, // red
943 {0.0, 1.0, 0.0, 1.0}, // green
944 {1.0, 1.0, 0.0, 1.0}, // yellow
945 {0.0, 0.0, 1.0, 1.0}, // blue
946 {0.0, 1.0, 1.0, 1.0}, // cyan
947 {1.0, 0.0, 1.0, 1.0}, // magenta
948 {1.0, 1.0, 1.0, 1.0}, // white
949 // [515]'s BX_COLOREDTEXT extension
950 {1.0, 1.0, 1.0, 0.5}, // half transparent
951 {0.5, 0.5, 0.5, 1.0} // half brightness
952 // Black's color table
953 //{1.0, 1.0, 1.0, 1.0},
954 //{1.0, 0.0, 0.0, 1.0},
955 //{0.0, 1.0, 0.0, 1.0},
956 //{0.0, 0.0, 1.0, 1.0},
957 //{1.0, 1.0, 0.0, 1.0},
958 //{0.0, 1.0, 1.0, 1.0},
959 //{1.0, 0.0, 1.0, 1.0},
960 //{0.1, 0.1, 0.1, 1.0}
963 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
965 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
967 float C = r_textcontrast.value;
968 float B = r_textbrightness.value;
969 if (colorindex & 0x10000) // that bit means RGB color
971 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
972 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
973 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
974 color[3] = (colorindex & 0xf) / 15.0;
977 Vector4Copy(string_colors[colorindex], color);
978 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
981 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
982 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
986 // NOTE: this function always draws exactly one character if maxwidth <= 0
987 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)
989 const char *text_start = text;
990 int colorindex = STRING_COLOR_DEFAULT;
993 Uchar ch, mapch, nextch;
994 Uchar prevch = 0; // used for kerning
999 ft2_font_map_t *fontmap = NULL;
1000 ft2_font_map_t *map = NULL;
1001 //ft2_font_map_t *prevmap = NULL;
1002 ft2_font_t *ft2 = fnt->ft2;
1004 qboolean snap = true;
1005 qboolean least_one = false;
1006 float dw; // display w
1007 //float dh; // display h
1008 const float *width_of;
1015 // do this in the end
1016 w *= fnt->settings.scale;
1017 h *= fnt->settings.scale;
1019 // find the most fitting size:
1023 map_index = Font_IndexForSize(ft2, h, &w, &h);
1025 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1026 fontmap = Font_MapForIndex(ft2, map_index);
1035 if (!outcolor || *outcolor == -1)
1036 colorindex = STRING_COLOR_DEFAULT;
1038 colorindex = *outcolor;
1040 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1041 // ftbase_x = snap_to_pixel_x(0);
1046 maxwidth = -maxwidth;
1050 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1053 width_of = fontmap->width_of;
1055 width_of = fnt->width_of;
1058 while (((bytes_left = *maxlen - (text - text_start)) > 0) && *text)
1061 nextch = ch = u8_getnchar(text, &text, bytes_left);
1062 i = text - text_start;
1065 if (ch == ' ' && !fontmap)
1067 if(!least_one || i0) // never skip the first character
1068 if(x + width_of[(int) ' '] * dw > maxwidth)
1071 break; // oops, can't draw this
1073 x += width_of[(int) ' '] * dw;
1076 // i points to the char after ^
1077 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1079 ch = *text; // colors are ascii, so no u8_ needed
1080 if (ch <= '9' && ch >= '0') // ^[0-9] found
1082 colorindex = ch - '0';
1087 // i points to the char after ^...
1088 // i+3 points to 3 in ^x123
1089 // i+3 == *maxlen would mean that char is missing
1090 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1092 // building colorindex...
1093 ch = tolower(text[1]);
1094 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1095 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1096 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1097 else tempcolorindex = 0;
1100 ch = tolower(text[2]);
1101 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1102 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1103 else tempcolorindex = 0;
1106 ch = tolower(text[3]);
1107 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1108 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1109 else tempcolorindex = 0;
1112 colorindex = tempcolorindex | 0xf;
1113 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1121 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1130 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1137 map = ft2_oldstyle_map;
1139 if(!least_one || i0) // never skip the first character
1140 if(x + width_of[ch] * dw > maxwidth)
1143 break; // oops, can't draw this
1145 x += width_of[ch] * dw;
1147 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1149 map = FontMap_FindForChar(fontmap, ch);
1152 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1158 mapch = ch - map->start;
1159 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1161 x += map->glyphs[mapch].advance_x * dw;
1170 *outcolor = colorindex;
1175 float DrawQ_Color[4];
1176 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)
1178 int shadow, colorindex = STRING_COLOR_DEFAULT;
1180 float x = startx, y, s, t, u, v, thisw;
1181 float *av, *at, *ac;
1183 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1184 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1185 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1186 Uchar ch, mapch, nextch;
1187 Uchar prevch = 0; // used for kerning
1190 //ft2_font_map_t *prevmap = NULL; // the previous map
1191 ft2_font_map_t *map = NULL; // the currently used map
1192 ft2_font_map_t *fontmap = NULL; // the font map for the size
1194 const char *text_start = text;
1196 ft2_font_t *ft2 = fnt->ft2;
1197 qboolean snap = true;
1201 const float *width_of;
1204 tw = Draw_GetPicWidth(fnt->pic);
1205 th = Draw_GetPicHeight(fnt->pic);
1213 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1214 w *= fnt->settings.scale;
1215 h *= fnt->settings.scale;
1220 map_index = Font_IndexForSize(ft2, h, &w, &h);
1222 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1223 fontmap = Font_MapForIndex(ft2, map_index);
1229 // draw the font at its baseline when using freetype
1231 ftbase_y = dh * (4.5/6.0);
1236 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1237 if(!r_draw2d.integer && !r_draw2d_force)
1238 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1240 // R_Mesh_ResetTextureState();
1242 R_Mesh_TexBind(0, Draw_GetPicTexture(fnt->pic));
1243 R_SetupShader_Generic(Draw_GetPicTexture(fnt->pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1250 //ftbase_x = snap_to_pixel_x(ftbase_x);
1253 startx = snap_to_pixel_x(startx, 0.4);
1254 starty = snap_to_pixel_y(starty, 0.4);
1255 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1258 pix_x = vid.width / vid_conwidth.value;
1259 pix_y = vid.height / vid_conheight.value;
1262 width_of = fontmap->width_of;
1264 width_of = fnt->width_of;
1266 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1271 if (!outcolor || *outcolor == -1)
1272 colorindex = STRING_COLOR_DEFAULT;
1274 colorindex = *outcolor;
1276 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1283 x += r_textshadow.value * vid.width / vid_conwidth.value;
1284 y += r_textshadow.value * vid.height / vid_conheight.value;
1287 while (((bytes_left = maxlen - (text - text_start)) > 0) && *text)
1289 nextch = ch = u8_getnchar(text, &text, bytes_left);
1290 i = text - text_start;
1293 if (ch == ' ' && !fontmap)
1295 x += width_of[(int) ' '] * dw;
1298 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1300 ch = *text; // colors are ascii, so no u8_ needed
1301 if (ch <= '9' && ch >= '0') // ^[0-9] found
1303 colorindex = ch - '0';
1304 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1309 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1311 // building colorindex...
1312 ch = tolower(text[1]);
1313 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1314 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1315 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1316 else tempcolorindex = 0;
1319 ch = tolower(text[2]);
1320 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1321 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1322 else tempcolorindex = 0;
1325 ch = tolower(text[3]);
1326 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1327 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1328 else tempcolorindex = 0;
1331 colorindex = tempcolorindex | 0xf;
1332 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1333 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1334 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1342 else if (ch == STRING_COLOR_TAG)
1351 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1352 // this way we don't need to rebind fnt->tex for every old-style character
1353 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1356 x += 1.0/pix_x * r_textshadow.value;
1357 y += 1.0/pix_y * r_textshadow.value;
1359 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1367 if (map != ft2_oldstyle_map)
1371 // switching from freetype to non-freetype rendering
1372 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1373 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1379 R_SetupShader_Generic(Draw_GetPicTexture(fnt->pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1380 map = ft2_oldstyle_map;
1384 //num = (unsigned char) text[i];
1385 //thisw = fnt->width_of[num];
1386 thisw = fnt->width_of[ch];
1387 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1388 if (r_nearest_conchars.integer)
1390 s = (ch & 15)*0.0625f;
1391 t = (ch >> 4)*0.0625f;
1392 u = 0.0625f * thisw;
1397 s = (ch & 15)*0.0625f + (0.5f / tw);
1398 t = (ch >> 4)*0.0625f + (0.5f / th);
1399 u = 0.0625f * thisw - (1.0f / tw);
1400 v = 0.0625f - (1.0f / th);
1402 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1403 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1404 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1405 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1406 at[ 0] = s ; at[ 1] = t ;
1407 at[ 2] = s+u ; at[ 3] = t ;
1408 at[ 4] = s+u ; at[ 5] = t+v ;
1409 at[ 6] = s ; at[ 7] = t+v ;
1410 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1411 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1412 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1413 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1418 if (batchcount >= QUADELEMENTS_MAXQUADS)
1420 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1421 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1427 x += width_of[ch] * dw;
1429 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1431 // new charmap - need to render
1434 // we need a different character map, render what we currently have:
1435 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1436 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1443 map = FontMap_FindForChar(fontmap, ch);
1446 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1453 // this shouldn't happen
1458 R_SetupShader_Generic(Draw_GetPicTexture(map->pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1461 mapch = ch - map->start;
1462 thisw = map->glyphs[mapch].advance_x;
1466 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1473 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1474 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1475 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1476 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1477 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1478 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1479 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1480 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1481 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1482 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1483 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1484 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1493 if (batchcount >= QUADELEMENTS_MAXQUADS)
1495 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1496 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1509 x -= 1.0/pix_x * r_textshadow.value;
1510 y -= 1.0/pix_y * r_textshadow.value;
1516 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1517 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1521 *outcolor = colorindex;
1523 // note: this relies on the proper text (not shadow) being drawn last
1527 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)
1529 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1532 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)
1534 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1537 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1539 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1542 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1544 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1549 // no ^xrgb management
1550 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1552 int color, numchars = 0;
1553 char *outputend2c = output2c + maxoutchars - 2;
1554 if (!outcolor || *outcolor == -1)
1555 color = STRING_COLOR_DEFAULT;
1559 maxreadchars = 1<<30;
1560 textend = text + maxreadchars;
1561 while (text != textend && *text)
1563 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1565 if (text[1] == STRING_COLOR_TAG)
1567 else if (text[1] >= '0' && text[1] <= '9')
1569 color = text[1] - '0';
1574 if (output2c >= outputend2c)
1576 *output2c++ = *text++;
1577 *output2c++ = color;
1580 output2c[0] = output2c[1] = 0;
1587 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)
1591 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1592 if(!r_draw2d.integer && !r_draw2d_force)
1595 // R_Mesh_ResetTextureState();
1601 height = pic->height;
1602 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & (DRAWFLAGS_BLEND | DRAWFLAG_NOGAMMA)) ? false : true, true, false);
1605 R_SetupShader_Generic_NoTexture((flags & (DRAWFLAGS_BLEND | DRAWFLAG_NOGAMMA)) ? false : true, true);
1607 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1608 floats[0] = floats[9] = x;
1609 floats[1] = floats[4] = y;
1610 floats[3] = floats[6] = x + width;
1611 floats[7] = floats[10] = y + height;
1612 floats[12] = s1;floats[13] = t1;
1613 floats[14] = s2;floats[15] = t2;
1614 floats[16] = s4;floats[17] = t4;
1615 floats[18] = s3;floats[19] = t3;
1616 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1617 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1618 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1619 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1621 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1622 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1625 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1628 if(!r_draw2d.integer && !r_draw2d_force)
1630 DrawQ_ProcessDrawFlag(flags, hasalpha);
1632 // R_Mesh_ResetTextureState();
1633 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1635 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1636 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1639 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1641 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1642 if(!r_draw2d.integer && !r_draw2d_force)
1646 switch(vid.renderpath)
1648 case RENDERPATH_GL11:
1649 case RENDERPATH_GL13:
1650 case RENDERPATH_GL20:
1655 qglBegin(GL_LINE_LOOP);
1656 for (num = 0;num < mesh->num_vertices;num++)
1658 if (mesh->data_color4f)
1659 GL_Color(mesh->data_color4f[num*4+0], mesh->data_color4f[num*4+1], mesh->data_color4f[num*4+2], mesh->data_color4f[num*4+3]);
1660 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1667 case RENDERPATH_D3D9:
1668 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1670 case RENDERPATH_D3D10:
1671 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1673 case RENDERPATH_D3D11:
1674 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1676 case RENDERPATH_SOFT:
1677 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1679 case RENDERPATH_GLES1:
1680 case RENDERPATH_GLES2:
1681 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1686 //[515]: this is old, delete
1687 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1689 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1690 if(!r_draw2d.integer && !r_draw2d_force)
1693 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1695 switch(vid.renderpath)
1697 case RENDERPATH_GL11:
1698 case RENDERPATH_GL13:
1699 case RENDERPATH_GL20:
1703 //qglLineWidth(width);CHECKGLERROR
1705 GL_Color(r,g,b,alpha);
1708 qglVertex2f(x1, y1);
1709 qglVertex2f(x2, y2);
1714 case RENDERPATH_D3D9:
1715 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1717 case RENDERPATH_D3D10:
1718 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1720 case RENDERPATH_D3D11:
1721 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1723 case RENDERPATH_SOFT:
1724 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1726 case RENDERPATH_GLES1:
1727 case RENDERPATH_GLES2:
1728 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1733 void DrawQ_Lines (float width, int numlines, int flags, qboolean hasalpha)
1735 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
1737 if(!r_draw2d.integer && !r_draw2d_force)
1740 switch(vid.renderpath)
1742 case RENDERPATH_GL11:
1743 case RENDERPATH_GL13:
1744 case RENDERPATH_GL20:
1747 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1749 //qglLineWidth(width);CHECKGLERROR
1752 qglDrawArrays(GL_LINES, 0, numlines*2);
1755 case RENDERPATH_D3D9:
1756 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1758 case RENDERPATH_D3D10:
1759 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1761 case RENDERPATH_D3D11:
1762 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1764 case RENDERPATH_SOFT:
1765 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1767 case RENDERPATH_GLES1:
1768 case RENDERPATH_GLES2:
1769 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1774 void DrawQ_SetClipArea(float x, float y, float width, float height)
1779 // We have to convert the con coords into real coords
1780 // OGL uses bottom to top (origin is in bottom left)
1781 ix = (int)(0.5 + x * ((float)r_refdef.view.width / vid_conwidth.integer)) + r_refdef.view.x;
1782 iy = (int)(0.5 + y * ((float)r_refdef.view.height / vid_conheight.integer)) + r_refdef.view.y;
1783 iw = (int)(0.5 + width * ((float)r_refdef.view.width / vid_conwidth.integer));
1784 ih = (int)(0.5 + height * ((float)r_refdef.view.height / vid_conheight.integer));
1785 switch(vid.renderpath)
1787 case RENDERPATH_GL11:
1788 case RENDERPATH_GL13:
1789 case RENDERPATH_GL20:
1790 case RENDERPATH_GLES1:
1791 case RENDERPATH_GLES2:
1792 case RENDERPATH_SOFT:
1793 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1795 case RENDERPATH_D3D9:
1796 GL_Scissor(ix, iy, iw, ih);
1798 case RENDERPATH_D3D10:
1799 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1801 case RENDERPATH_D3D11:
1802 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1806 GL_ScissorTest(true);
1809 void DrawQ_ResetClipArea(void)
1812 GL_ScissorTest(false);
1815 void DrawQ_Finish(void)
1817 r_refdef.draw2dstage = 0;
1820 void DrawQ_RecalcView(void)
1822 if(r_refdef.draw2dstage)
1823 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again