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.
26 #include "cl_dyntexture.h"
29 #include "ft2_fontdefs.h"
32 static mempool_t *fonts_mempool = NULL;
34 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)"};
35 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)"};
36 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)"};
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
46 extern cvar_t v_glslgamma;
48 //=============================================================================
49 /* Support Routines */
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
56 rtexturepool_t *drawtexturepool;
58 static const unsigned char concharimage[FONT_FILESIZE] =
63 static rtexture_t *draw_generateconchars(void)
70 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
72 for (i = 0;i < 8192;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 83 + (unsigned char)(random * 64);
77 data[i*4+1] = 71 + (unsigned char)(random * 32);
78 data[i*4+0] = 23 + (unsigned char)(random * 16);
81 for (i = 8192;i < 32768;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 95 + (unsigned char)(random * 64);
86 data[i*4+1] = 95 + (unsigned char)(random * 64);
87 data[i*4+0] = 95 + (unsigned char)(random * 64);
90 for (i = 32768;i < 40960;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 83 + (unsigned char)(random * 64);
95 data[i*4+1] = 71 + (unsigned char)(random * 32);
96 data[i*4+0] = 23 + (unsigned char)(random * 16);
99 for (i = 40960;i < 65536;i++)
101 random = lhrandom (0.0,1.0);
102 data[i*4+3] = data[i*4+0];
103 data[i*4+2] = 96 + (unsigned char)(random * 64);
104 data[i*4+1] = 43 + (unsigned char)(random * 32);
105 data[i*4+0] = 27 + (unsigned char)(random * 32);
109 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
112 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
117 static rtexture_t *draw_generateditherpattern(void)
120 unsigned char pixels[8][8];
121 for (y = 0;y < 8;y++)
122 for (x = 0;x < 8;x++)
123 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
127 typedef struct embeddedpic_s
136 static const embeddedpic_t embeddedpics[] =
139 "gfx/prydoncursor001", 16, 16,
158 "ui/mousepointer", 16, 16,
177 "gfx/crosshair1", 16, 16,
196 "gfx/crosshair2", 16, 16,
215 "gfx/crosshair3", 16, 16,
234 "gfx/crosshair4", 16, 16,
253 "gfx/crosshair5", 8, 8,
264 "gfx/crosshair6", 2, 2,
269 "gfx/crosshair7", 16, 16,
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
292 const embeddedpic_t *p;
293 for (p = embeddedpics;p->name;p++)
294 if (!strcmp(name, p->name))
295 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
312 // FIXME: move this to client somehow
313 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
316 unsigned char *pixels = NULL;
319 unsigned char *lmpdata;
320 char lmpname[MAX_QPATH];
323 qboolean ddshasalpha;
324 float ddsavgcolor[4];
325 qboolean loaded = false;
327 texflags = TEXF_ALPHA;
328 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
329 texflags |= TEXF_CLAMP;
330 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
331 texflags |= TEXF_COMPRESS;
333 // check whether the picture has already been cached
334 crc = CRC_Block((unsigned char *)path, strlen(path));
335 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
336 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
337 if (!strcmp (path, pic->name))
338 if(!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
340 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
343 pic->autoload = false; // persist it
345 goto reload; // load it below, and then persist
350 if (numcachepics == MAX_CACHED_PICS)
352 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
353 // FIXME: support NULL in callers?
354 return cachepics; // return the first one
356 pic = cachepics + (numcachepics++);
357 strlcpy (pic->name, path, sizeof(pic->name));
359 pic->chain = cachepichash[hashkey];
360 cachepichash[hashkey] = pic;
363 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
364 pic->tex = CL_GetDynTexture( path );
365 // if so, set the width/height, too
367 pic->width = R_TextureWidth(pic->tex);
368 pic->height = R_TextureHeight(pic->tex);
369 // we're done now (early-out)
373 pic->hasalpha = true; // assume alpha unless we know it has none
374 pic->texflags = texflags;
375 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
376 pic->lastusedframe = draw_frame;
378 // load a high quality image from disk if possible
379 if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
381 // note this loads even if autoload is true, otherwise we can't get the width/height
383 pic->hasalpha = ddshasalpha;
384 pic->width = R_TextureWidth(pic->tex);
385 pic->height = R_TextureHeight(pic->tex);
387 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
390 pic->hasalpha = false;
391 if (pic->texflags & TEXF_ALPHA)
393 for (j = 3;j < image_width * image_height * 4;j += 4)
397 pic->hasalpha = true;
403 pic->width = image_width;
404 pic->height = image_height;
407 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, image_width, image_height, pixels, r_texture_sRGB_2d.integer ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
408 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
409 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
414 pic->autoload = false;
415 // never compress the fallback images
416 pic->texflags &= ~TEXF_COMPRESS;
419 // now read the low quality version (wad or lmp file), and take the pic
420 // size from that even if we don't upload the texture, this way the pics
421 // show up the right size in the menu even if they were replaced with
422 // higher or lower resolution versions
423 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
424 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
426 if (developer_loading.integer)
427 Con_Printf("loading lump \"%s\"\n", pic->name);
431 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
432 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
433 // if no high quality replacement image was found, upload the original low quality texture
435 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
439 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
441 if (developer_loading.integer)
442 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
444 if (!strcmp(pic->name, "gfx/conchars"))
446 // conchars is a raw image and with color 0 as transparent instead of 255
449 // if no high quality replacement image was found, upload the original low quality texture
451 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
455 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
456 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
457 // if no high quality replacement image was found, upload the original low quality texture
459 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
470 // if it's not found on disk, generate an image
471 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
472 pic->width = R_TextureWidth(pic->tex);
473 pic->height = R_TextureHeight(pic->tex);
479 cachepic_t *Draw_CachePic (const char *path)
481 return Draw_CachePic_Flags (path, 0); // default to persistent!
484 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
486 if (pic->autoload && !pic->tex)
488 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
490 qboolean ddshasalpha;
491 float ddsavgcolor[4];
492 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0);
494 if (pic->tex == NULL)
496 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_sRGB_2d.integer != 0);
497 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
498 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
500 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
502 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_sRGB_2d.integer != 0);
503 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
504 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
506 if (pic->tex == NULL)
507 pic->tex = draw_generatepic(pic->name, true);
509 pic->lastusedframe = draw_frame;
513 void Draw_Frame(void)
517 static double nextpurgetime;
518 if (nextpurgetime > realtime)
520 nextpurgetime = realtime + 0.05;
521 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
523 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
525 R_FreeTexture(pic->tex);
532 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
537 crc = CRC_Block((unsigned char *)picname, strlen(picname));
538 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
539 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
540 if (!strcmp (picname, pic->name))
545 if (pic->tex && pic->width == width && pic->height == height)
547 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
555 if (numcachepics == MAX_CACHED_PICS)
557 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
558 // FIXME: support NULL in callers?
559 return cachepics; // return the first one
561 pic = cachepics + (numcachepics++);
562 strlcpy (pic->name, picname, sizeof(pic->name));
564 pic->chain = cachepichash[hashkey];
565 cachepichash[hashkey] = pic;
570 pic->height = height;
572 R_FreeTexture(pic->tex);
573 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
577 void Draw_FreePic(const char *picname)
582 // this doesn't really free the pic, but does free it's texture
583 crc = CRC_Block((unsigned char *)picname, strlen(picname));
584 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
585 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
587 if (!strcmp (picname, pic->name) && pic->tex)
589 R_FreeTexture(pic->tex);
598 static float snap_to_pixel_x(float x, float roundUpAt);
599 extern int con_linewidth; // to force rewrapping
600 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
604 char widthfile[MAX_QPATH];
606 fs_offset_t widthbufsize;
608 if(override || !fnt->texpath[0])
610 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
611 // load the cvars when the font is FIRST loader
612 fnt->settings.scale = scale;
613 fnt->settings.voffset = voffset;
614 fnt->settings.antialias = r_font_antialias.integer;
615 fnt->settings.hinting = r_font_hinting.integer;
616 fnt->settings.outline = r_font_postprocess_outline.value;
617 fnt->settings.blur = r_font_postprocess_blur.value;
618 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
619 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
620 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
623 if (fnt->settings.scale <= 0)
624 fnt->settings.scale = 1;
626 if(drawtexturepool == NULL)
627 return; // before gl_draw_start, so will be loaded later
631 // clear freetype font
632 Font_UnloadFont(fnt->ft2);
637 if(fnt->req_face != -1)
639 if(!Font_LoadFont(fnt->texpath, fnt))
640 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
643 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
644 if(fnt->tex == r_texture_notexture)
646 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
648 if (!fnt->fallbacks[i][0])
650 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
651 if(fnt->tex != r_texture_notexture)
654 if(fnt->tex == r_texture_notexture)
656 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
657 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
660 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
663 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
665 // unspecified width == 1 (base width)
666 for(ch = 0; ch < 256; ++ch)
667 fnt->width_of[ch] = 1;
669 // FIXME load "name.width", if it fails, fill all with 1
670 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
672 float extraspacing = 0;
673 const char *p = widthbuf;
678 if(!COM_ParseToken_Simple(&p, false, false))
696 fnt->width_of[ch] = atof(com_token) + extraspacing;
700 if(!strcmp(com_token, "extraspacing"))
702 if(!COM_ParseToken_Simple(&p, false, false))
704 extraspacing = atof(com_token);
706 else if(!strcmp(com_token, "scale"))
708 if(!COM_ParseToken_Simple(&p, false, false))
710 fnt->settings.scale = atof(com_token);
714 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
715 if(!COM_ParseToken_Simple(&p, false, false))
727 for (i = 0; i < MAX_FONT_SIZES; ++i)
729 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
732 for(ch = 0; ch < 256; ++ch)
733 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
737 maxwidth = fnt->width_of[0];
738 for(i = 1; i < 256; ++i)
739 maxwidth = max(maxwidth, fnt->width_of[i]);
740 fnt->maxwidth = maxwidth;
742 // fix up maxwidth for overlap
743 fnt->maxwidth *= fnt->settings.scale;
745 if(fnt == FONT_CONSOLE)
746 con_linewidth = -1; // rewrap console in next frame
749 extern cvar_t developer_font;
750 dp_font_t *FindFont(const char *title, qboolean allocate_new)
755 for(i = 0; i < dp_fonts.maxsize; ++i)
756 if(!strcmp(dp_fonts.f[i].title, title))
757 return &dp_fonts.f[i];
758 // if not found - try allocate
761 // find any font with empty title
762 for(i = 0; i < dp_fonts.maxsize; ++i)
764 if(!strcmp(dp_fonts.f[i].title, ""))
766 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
767 return &dp_fonts.f[i];
770 // if no any 'free' fonts - expand buffer
771 oldsize = dp_fonts.maxsize;
772 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
773 if (developer_font.integer)
774 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
775 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
776 // relink ft2 structures
777 for(i = 0; i < oldsize; ++i)
778 if (dp_fonts.f[i].ft2)
779 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
780 // register a font in first expanded slot
781 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
782 return &dp_fonts.f[oldsize];
787 static float snap_to_pixel_x(float x, float roundUpAt)
789 float pixelpos = x * vid.width / vid_conwidth.value;
790 int snap = (int) pixelpos;
791 if (pixelpos - snap >= roundUpAt) ++snap;
792 return ((float)snap * vid_conwidth.value / vid.width);
794 x = (int)(x * vid.width / vid_conwidth.value);
795 x = (x * vid_conwidth.value / vid.width);
800 static float snap_to_pixel_y(float y, float roundUpAt)
802 float pixelpos = y * vid.height / vid_conheight.value;
803 int snap = (int) pixelpos;
804 if (pixelpos - snap > roundUpAt) ++snap;
805 return ((float)snap * vid_conheight.value / vid.height);
807 y = (int)(y * vid.height / vid_conheight.value);
808 y = (y * vid_conheight.value / vid.height);
813 static void LoadFont_f(void)
817 const char *filelist, *c, *cm;
818 float sz, scale, voffset;
819 char mainfont[MAX_QPATH];
823 Con_Printf("Available font commands:\n");
824 for(i = 0; i < dp_fonts.maxsize; ++i)
825 if (dp_fonts.f[i].title[0])
826 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
827 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
828 "can specify multiple fonts and faces\n"
829 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
830 "to load face 2 of the font gfx/vera-sans and use face 1\n"
831 "of gfx/fallback as fallback font.\n"
832 "You can also specify a list of font sizes to load, like this:\n"
833 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
834 "In many cases, 8 12 16 24 32 should be a good choice.\n"
836 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
837 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
841 f = FindFont(Cmd_Argv(1), true);
844 Con_Printf("font function not found\n");
849 filelist = "gfx/conchars";
851 filelist = Cmd_Argv(2);
853 memset(f->fallbacks, 0, sizeof(f->fallbacks));
854 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
856 // first font is handled "normally"
857 c = strchr(filelist, ':');
858 cm = strchr(filelist, ',');
859 if(c && (!cm || c < cm))
860 f->req_face = atoi(c+1);
867 if(!c || (c - filelist) > MAX_QPATH)
868 strlcpy(mainfont, filelist, sizeof(mainfont));
871 memcpy(mainfont, filelist, c - filelist);
872 mainfont[c - filelist] = 0;
875 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
877 c = strchr(filelist, ',');
883 c = strchr(filelist, ':');
884 cm = strchr(filelist, ',');
885 if(c && (!cm || c < cm))
886 f->fallback_faces[i] = atoi(c+1);
889 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
892 if(!c || (c-filelist) > MAX_QPATH)
894 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
898 memcpy(f->fallbacks[i], filelist, c - filelist);
899 f->fallbacks[i][c - filelist] = 0;
903 // for now: by default load only one size: the default size
905 for(i = 1; i < MAX_FONT_SIZES; ++i)
906 f->req_sizes[i] = -1;
912 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
915 if (!strcmp(Cmd_Argv(i), "scale"))
919 scale = atof(Cmd_Argv(i));
922 if (!strcmp(Cmd_Argv(i), "voffset"))
926 voffset = atof(Cmd_Argv(i));
931 continue; // no slot for other sizes
933 // parse one of sizes
934 sz = atof(Cmd_Argv(i));
935 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
937 // search for duplicated sizes
939 for (j=0; j<sizes; j++)
940 if (f->req_sizes[j] == sz)
943 continue; // sz already in req_sizes, don't add it again
945 if (sizes == MAX_FONT_SIZES)
947 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
951 f->req_sizes[sizes] = sz;
957 LoadFont(true, mainfont, f, scale, voffset);
965 static void gl_draw_start(void)
968 drawtexturepool = R_AllocTexturePool();
971 memset(cachepichash, 0, sizeof(cachepichash));
975 // load default font textures
976 for(i = 0; i < dp_fonts.maxsize; ++i)
977 if (dp_fonts.f[i].title[0])
978 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
980 // draw the loading screen so people have something to see in the newly opened window
981 SCR_UpdateLoadingScreen(true);
984 static void gl_draw_shutdown(void)
988 R_FreeTexturePool(&drawtexturepool);
991 memset(cachepichash, 0, sizeof(cachepichash));
994 static void gl_draw_newmap(void)
999 void GL_Draw_Init (void)
1003 Cvar_RegisterVariable(&r_font_postprocess_blur);
1004 Cvar_RegisterVariable(&r_font_postprocess_outline);
1005 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1006 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1007 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1008 Cvar_RegisterVariable(&r_font_hinting);
1009 Cvar_RegisterVariable(&r_font_antialias);
1010 Cvar_RegisterVariable(&r_textshadow);
1011 Cvar_RegisterVariable(&r_textbrightness);
1012 Cvar_RegisterVariable(&r_textcontrast);
1014 // allocate fonts storage
1015 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1016 dp_fonts.maxsize = MAX_FONTS;
1017 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1018 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1020 // assign starting font names
1021 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1022 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1023 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1024 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1025 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1026 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1027 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1028 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1029 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1030 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1031 if(!FONT_USER(i)->title[0])
1032 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1034 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1035 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1038 static void _DrawQ_Setup(void)
1040 r_viewport_t viewport;
1041 if (r_refdef.draw2dstage == 1)
1043 r_refdef.draw2dstage = 1;
1045 R_Viewport_InitOrtho(&viewport, &identitymatrix, r_refdef.view.x, vid.height - r_refdef.view.y - r_refdef.view.height, r_refdef.view.width, r_refdef.view.height, 0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100, NULL);
1046 R_Mesh_ResetRenderTargets();
1047 R_SetViewport(&viewport);
1048 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1049 GL_DepthFunc(GL_LEQUAL);
1050 GL_PolygonOffset(0,0);
1051 GL_CullFace(GL_NONE);
1052 R_EntityMatrix(&identitymatrix);
1054 GL_DepthRange(0, 1);
1055 GL_PolygonOffset(0, 0);
1056 GL_DepthTest(false);
1060 qboolean r_draw2d_force = false;
1061 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1065 if(!r_draw2d.integer && !r_draw2d_force)
1067 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1069 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1071 if(flags == DRAWFLAG_ADDITIVE)
1073 GL_DepthMask(false);
1074 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1076 else if(flags == DRAWFLAG_MODULATE)
1078 GL_DepthMask(false);
1079 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1081 else if(flags == DRAWFLAG_2XMODULATE)
1083 GL_DepthMask(false);
1084 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1086 else if(flags == DRAWFLAG_SCREEN)
1088 GL_DepthMask(false);
1089 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1093 GL_DepthMask(false);
1094 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1099 GL_BlendFunc(GL_ONE, GL_ZERO);
1103 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1107 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1108 if(!r_draw2d.integer && !r_draw2d_force)
1111 // R_Mesh_ResetTextureState();
1112 floats[12] = 0.0f;floats[13] = 0.0f;
1113 floats[14] = 1.0f;floats[15] = 0.0f;
1114 floats[16] = 1.0f;floats[17] = 1.0f;
1115 floats[18] = 0.0f;floats[19] = 1.0f;
1116 floats[20] = floats[24] = floats[28] = floats[32] = red;
1117 floats[21] = floats[25] = floats[29] = floats[33] = green;
1118 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1119 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1125 height = pic->height;
1126 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1129 // AK07: lets be texel correct on the corners
1131 float horz_offset = 0.5f / pic->width;
1132 float vert_offset = 0.5f / pic->height;
1134 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1135 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1136 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1137 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1142 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1144 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1145 floats[0] = floats[9] = x;
1146 floats[1] = floats[4] = y;
1147 floats[3] = floats[6] = x + width;
1148 floats[7] = floats[10] = y + height;
1150 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1151 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1154 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)
1157 float af = DEG2RAD(-angle); // forward
1158 float ar = DEG2RAD(-angle + 90); // right
1159 float sinaf = sin(af);
1160 float cosaf = cos(af);
1161 float sinar = sin(ar);
1162 float cosar = cos(ar);
1164 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1165 if(!r_draw2d.integer && !r_draw2d_force)
1168 // R_Mesh_ResetTextureState();
1174 height = pic->height;
1175 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1178 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1180 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1183 floats[0] = x - cosaf*org_x - cosar*org_y;
1184 floats[1] = y - sinaf*org_x - sinar*org_y;
1187 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1188 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1191 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1192 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1195 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1196 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1198 floats[12] = 0.0f;floats[13] = 0.0f;
1199 floats[14] = 1.0f;floats[15] = 0.0f;
1200 floats[16] = 1.0f;floats[17] = 1.0f;
1201 floats[18] = 0.0f;floats[19] = 1.0f;
1202 floats[20] = floats[24] = floats[28] = floats[32] = red;
1203 floats[21] = floats[25] = floats[29] = floats[33] = green;
1204 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1205 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1207 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1208 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1211 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1215 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1216 if(!r_draw2d.integer && !r_draw2d_force)
1219 // R_Mesh_ResetTextureState();
1220 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1222 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1223 floats[0] = floats[9] = x;
1224 floats[1] = floats[4] = y;
1225 floats[3] = floats[6] = x + width;
1226 floats[7] = floats[10] = y + height;
1227 floats[12] = 0.0f;floats[13] = 0.0f;
1228 floats[14] = 1.0f;floats[15] = 0.0f;
1229 floats[16] = 1.0f;floats[17] = 1.0f;
1230 floats[18] = 0.0f;floats[19] = 1.0f;
1231 floats[20] = floats[24] = floats[28] = floats[32] = red;
1232 floats[21] = floats[25] = floats[29] = floats[33] = green;
1233 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1234 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1236 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1237 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1240 /// color tag printing
1241 static const vec4_t string_colors[] =
1244 // LordHavoc: why on earth is cyan before magenta in Quake3?
1245 // LordHavoc: note: Doom3 uses white for [0] and [7]
1246 {0.0, 0.0, 0.0, 1.0}, // black
1247 {1.0, 0.0, 0.0, 1.0}, // red
1248 {0.0, 1.0, 0.0, 1.0}, // green
1249 {1.0, 1.0, 0.0, 1.0}, // yellow
1250 {0.0, 0.0, 1.0, 1.0}, // blue
1251 {0.0, 1.0, 1.0, 1.0}, // cyan
1252 {1.0, 0.0, 1.0, 1.0}, // magenta
1253 {1.0, 1.0, 1.0, 1.0}, // white
1254 // [515]'s BX_COLOREDTEXT extension
1255 {1.0, 1.0, 1.0, 0.5}, // half transparent
1256 {0.5, 0.5, 0.5, 1.0} // half brightness
1257 // Black's color table
1258 //{1.0, 1.0, 1.0, 1.0},
1259 //{1.0, 0.0, 0.0, 1.0},
1260 //{0.0, 1.0, 0.0, 1.0},
1261 //{0.0, 0.0, 1.0, 1.0},
1262 //{1.0, 1.0, 0.0, 1.0},
1263 //{0.0, 1.0, 1.0, 1.0},
1264 //{1.0, 0.0, 1.0, 1.0},
1265 //{0.1, 0.1, 0.1, 1.0}
1268 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1270 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1272 float C = r_textcontrast.value;
1273 float B = r_textbrightness.value;
1274 if (colorindex & 0x10000) // that bit means RGB color
1276 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1277 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1278 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1279 color[3] = (colorindex & 0xf) / 15.0;
1282 Vector4Copy(string_colors[colorindex], color);
1283 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1286 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1287 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1291 // NOTE: this function always draws exactly one character if maxwidth <= 0
1292 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)
1294 const char *text_start = text;
1295 int colorindex = STRING_COLOR_DEFAULT;
1298 Uchar ch, mapch, nextch;
1299 Uchar prevch = 0; // used for kerning
1304 ft2_font_map_t *fontmap = NULL;
1305 ft2_font_map_t *map = NULL;
1306 //ft2_font_map_t *prevmap = NULL;
1307 ft2_font_t *ft2 = fnt->ft2;
1309 qboolean snap = true;
1310 qboolean least_one = false;
1311 float dw; // display w
1312 //float dh; // display h
1313 const float *width_of;
1320 // do this in the end
1321 w *= fnt->settings.scale;
1322 h *= fnt->settings.scale;
1324 // find the most fitting size:
1328 map_index = Font_IndexForSize(ft2, h, &w, &h);
1330 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1331 fontmap = Font_MapForIndex(ft2, map_index);
1340 if (!outcolor || *outcolor == -1)
1341 colorindex = STRING_COLOR_DEFAULT;
1343 colorindex = *outcolor;
1345 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1346 // ftbase_x = snap_to_pixel_x(0);
1351 maxwidth = -maxwidth;
1355 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1358 width_of = fontmap->width_of;
1360 width_of = fnt->width_of;
1362 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1365 nextch = ch = u8_getnchar(text, &text, bytes_left);
1366 i = text - text_start;
1369 if (ch == ' ' && !fontmap)
1371 if(!least_one || i0) // never skip the first character
1372 if(x + width_of[(int) ' '] * dw > maxwidth)
1375 break; // oops, can't draw this
1377 x += width_of[(int) ' '] * dw;
1380 // i points to the char after ^
1381 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1383 ch = *text; // colors are ascii, so no u8_ needed
1384 if (ch <= '9' && ch >= '0') // ^[0-9] found
1386 colorindex = ch - '0';
1391 // i points to the char after ^...
1392 // i+3 points to 3 in ^x123
1393 // i+3 == *maxlen would mean that char is missing
1394 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1396 // building colorindex...
1397 ch = tolower(text[1]);
1398 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1399 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1400 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1401 else tempcolorindex = 0;
1404 ch = tolower(text[2]);
1405 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1406 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1407 else tempcolorindex = 0;
1410 ch = tolower(text[3]);
1411 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1412 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1413 else tempcolorindex = 0;
1416 colorindex = tempcolorindex | 0xf;
1417 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1425 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1434 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1441 map = ft2_oldstyle_map;
1443 if(!least_one || i0) // never skip the first character
1444 if(x + width_of[ch] * dw > maxwidth)
1447 break; // oops, can't draw this
1449 x += width_of[ch] * dw;
1451 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1453 map = FontMap_FindForChar(fontmap, ch);
1456 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1462 mapch = ch - map->start;
1463 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1465 x += map->glyphs[mapch].advance_x * dw;
1474 *outcolor = colorindex;
1479 float DrawQ_Color[4];
1480 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)
1482 int shadow, colorindex = STRING_COLOR_DEFAULT;
1484 float x = startx, y, s, t, u, v, thisw;
1485 float *av, *at, *ac;
1487 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1488 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1489 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1490 Uchar ch, mapch, nextch;
1491 Uchar prevch = 0; // used for kerning
1494 //ft2_font_map_t *prevmap = NULL; // the previous map
1495 ft2_font_map_t *map = NULL; // the currently used map
1496 ft2_font_map_t *fontmap = NULL; // the font map for the size
1498 const char *text_start = text;
1500 ft2_font_t *ft2 = fnt->ft2;
1501 qboolean snap = true;
1505 const float *width_of;
1508 tw = R_TextureWidth(fnt->tex);
1509 th = R_TextureHeight(fnt->tex);
1517 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1518 w *= fnt->settings.scale;
1519 h *= fnt->settings.scale;
1524 map_index = Font_IndexForSize(ft2, h, &w, &h);
1526 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1527 fontmap = Font_MapForIndex(ft2, map_index);
1533 // draw the font at its baseline when using freetype
1535 ftbase_y = dh * (4.5/6.0);
1540 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1541 if(!r_draw2d.integer && !r_draw2d_force)
1542 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1544 // R_Mesh_ResetTextureState();
1546 R_Mesh_TexBind(0, fnt->tex);
1547 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1554 //ftbase_x = snap_to_pixel_x(ftbase_x);
1557 startx = snap_to_pixel_x(startx, 0.4);
1558 starty = snap_to_pixel_y(starty, 0.4);
1559 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1562 pix_x = vid.width / vid_conwidth.value;
1563 pix_y = vid.height / vid_conheight.value;
1566 width_of = fontmap->width_of;
1568 width_of = fnt->width_of;
1570 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1575 if (!outcolor || *outcolor == -1)
1576 colorindex = STRING_COLOR_DEFAULT;
1578 colorindex = *outcolor;
1580 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1587 x += r_textshadow.value * vid.width / vid_conwidth.value;
1588 y += r_textshadow.value * vid.height / vid_conheight.value;
1591 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1593 nextch = ch = u8_getnchar(text, &text, bytes_left);
1594 i = text - text_start;
1597 if (ch == ' ' && !fontmap)
1599 x += width_of[(int) ' '] * dw;
1602 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1604 ch = *text; // colors are ascii, so no u8_ needed
1605 if (ch <= '9' && ch >= '0') // ^[0-9] found
1607 colorindex = ch - '0';
1608 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1613 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1615 // building colorindex...
1616 ch = tolower(text[1]);
1617 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1618 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1619 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1620 else tempcolorindex = 0;
1623 ch = tolower(text[2]);
1624 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1625 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1626 else tempcolorindex = 0;
1629 ch = tolower(text[3]);
1630 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1631 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1632 else tempcolorindex = 0;
1635 colorindex = tempcolorindex | 0xf;
1636 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1637 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1638 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1646 else if (ch == STRING_COLOR_TAG)
1655 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1656 // this way we don't need to rebind fnt->tex for every old-style character
1657 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1660 x += 1.0/pix_x * r_textshadow.value;
1661 y += 1.0/pix_y * r_textshadow.value;
1663 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1671 if (map != ft2_oldstyle_map)
1675 // switching from freetype to non-freetype rendering
1676 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1677 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1683 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1684 map = ft2_oldstyle_map;
1688 //num = (unsigned char) text[i];
1689 //thisw = fnt->width_of[num];
1690 thisw = fnt->width_of[ch];
1691 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1692 s = (ch & 15)*0.0625f + (0.5f / tw);
1693 t = (ch >> 4)*0.0625f + (0.5f / th);
1694 u = 0.0625f * thisw - (1.0f / tw);
1695 v = 0.0625f - (1.0f / th);
1696 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1697 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1698 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1699 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1700 at[ 0] = s ; at[ 1] = t ;
1701 at[ 2] = s+u ; at[ 3] = t ;
1702 at[ 4] = s+u ; at[ 5] = t+v ;
1703 at[ 6] = s ; at[ 7] = t+v ;
1704 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1705 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1706 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1707 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1712 if (batchcount >= QUADELEMENTS_MAXQUADS)
1714 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1715 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1721 x += width_of[ch] * dw;
1723 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1725 // new charmap - need to render
1728 // we need a different character map, render what we currently have:
1729 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1730 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1737 map = FontMap_FindForChar(fontmap, ch);
1740 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1747 // this shouldn't happen
1752 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1);
1755 mapch = ch - map->start;
1756 thisw = map->glyphs[mapch].advance_x;
1760 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1767 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1768 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1769 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1770 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1771 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1772 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1773 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1774 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1775 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1776 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1777 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1778 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1787 if (batchcount >= QUADELEMENTS_MAXQUADS)
1789 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1790 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1803 x -= 1.0/pix_x * r_textshadow.value;
1804 y -= 1.0/pix_y * r_textshadow.value;
1810 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1811 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1815 *outcolor = colorindex;
1817 // note: this relies on the proper text (not shadow) being drawn last
1821 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)
1823 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1826 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)
1828 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1831 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1833 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1836 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1838 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1843 // no ^xrgb management
1844 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1846 int color, numchars = 0;
1847 char *outputend2c = output2c + maxoutchars - 2;
1848 if (!outcolor || *outcolor == -1)
1849 color = STRING_COLOR_DEFAULT;
1853 maxreadchars = 1<<30;
1854 textend = text + maxreadchars;
1855 while (text != textend && *text)
1857 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1859 if (text[1] == STRING_COLOR_TAG)
1861 else if (text[1] >= '0' && text[1] <= '9')
1863 color = text[1] - '0';
1868 if (output2c >= outputend2c)
1870 *output2c++ = *text++;
1871 *output2c++ = color;
1874 output2c[0] = output2c[1] = 0;
1881 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)
1885 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1886 if(!r_draw2d.integer && !r_draw2d_force)
1889 // R_Mesh_ResetTextureState();
1895 height = pic->height;
1896 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1899 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1901 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1902 floats[0] = floats[9] = x;
1903 floats[1] = floats[4] = y;
1904 floats[3] = floats[6] = x + width;
1905 floats[7] = floats[10] = y + height;
1906 floats[12] = s1;floats[13] = t1;
1907 floats[14] = s2;floats[15] = t2;
1908 floats[16] = s4;floats[17] = t4;
1909 floats[18] = s3;floats[19] = t3;
1910 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1911 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1912 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1913 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1915 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1916 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1919 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1923 if(!r_draw2d.integer && !r_draw2d_force)
1925 DrawQ_ProcessDrawFlag(flags, hasalpha);
1927 // R_Mesh_ResetTextureState();
1928 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1930 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1931 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1934 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1938 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1939 if(!r_draw2d.integer && !r_draw2d_force)
1943 switch(vid.renderpath)
1945 case RENDERPATH_GL11:
1946 case RENDERPATH_GL13:
1947 case RENDERPATH_GL20:
1949 qglBegin(GL_LINE_LOOP);
1950 for (num = 0;num < mesh->num_vertices;num++)
1952 if (mesh->data_color4f)
1953 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]);
1954 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1959 case RENDERPATH_D3D9:
1960 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1962 case RENDERPATH_D3D10:
1963 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1965 case RENDERPATH_D3D11:
1966 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1968 case RENDERPATH_SOFT:
1969 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1971 case RENDERPATH_GLES1:
1972 case RENDERPATH_GLES2:
1973 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1978 //[515]: this is old, delete
1979 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1981 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1982 if(!r_draw2d.integer && !r_draw2d_force)
1985 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1987 switch(vid.renderpath)
1989 case RENDERPATH_GL11:
1990 case RENDERPATH_GL13:
1991 case RENDERPATH_GL20:
1994 //qglLineWidth(width);CHECKGLERROR
1996 GL_Color(r,g,b,alpha);
1999 qglVertex2f(x1, y1);
2000 qglVertex2f(x2, y2);
2004 case RENDERPATH_D3D9:
2005 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2007 case RENDERPATH_D3D10:
2008 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2010 case RENDERPATH_D3D11:
2011 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2013 case RENDERPATH_SOFT:
2014 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2016 case RENDERPATH_GLES1:
2017 case RENDERPATH_GLES2:
2018 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2023 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2026 qboolean hasalpha = false;
2027 for (i = 0;i < numlines*2;i++)
2028 if (color4f[i*4+3] < 1.0f)
2031 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2033 if(!r_draw2d.integer && !r_draw2d_force)
2036 switch(vid.renderpath)
2038 case RENDERPATH_GL11:
2039 case RENDERPATH_GL13:
2040 case RENDERPATH_GL20:
2043 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2045 //qglLineWidth(width);CHECKGLERROR
2048 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2049 qglDrawArrays(GL_LINES, 0, numlines*2);
2052 case RENDERPATH_D3D9:
2053 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2055 case RENDERPATH_D3D10:
2056 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2058 case RENDERPATH_D3D11:
2059 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2061 case RENDERPATH_SOFT:
2062 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2064 case RENDERPATH_GLES1:
2065 case RENDERPATH_GLES2:
2066 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2071 void DrawQ_SetClipArea(float x, float y, float width, float height)
2076 // We have to convert the con coords into real coords
2077 // OGL uses top to bottom
2078 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2079 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2080 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2081 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2082 switch(vid.renderpath)
2084 case RENDERPATH_GL11:
2085 case RENDERPATH_GL13:
2086 case RENDERPATH_GL20:
2087 case RENDERPATH_GLES1:
2088 case RENDERPATH_GLES2:
2089 case RENDERPATH_SOFT:
2090 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2092 case RENDERPATH_D3D9:
2093 GL_Scissor(ix, iy, iw, ih);
2095 case RENDERPATH_D3D10:
2096 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2098 case RENDERPATH_D3D11:
2099 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2103 GL_ScissorTest(true);
2106 void DrawQ_ResetClipArea(void)
2109 GL_ScissorTest(false);
2112 void DrawQ_Finish(void)
2114 r_refdef.draw2dstage = 0;
2117 void DrawQ_RecalcView(void)
2119 if(r_refdef.draw2dstage)
2120 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2123 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2124 void R_DrawGamma(void)
2127 switch(vid.renderpath)
2129 case RENDERPATH_GL20:
2130 case RENDERPATH_D3D9:
2131 case RENDERPATH_D3D10:
2132 case RENDERPATH_D3D11:
2133 case RENDERPATH_GLES2:
2134 if (vid_usinghwgamma || v_glslgamma.integer)
2137 case RENDERPATH_GL11:
2138 case RENDERPATH_GL13:
2139 if (vid_usinghwgamma)
2142 case RENDERPATH_GLES1:
2143 case RENDERPATH_SOFT:
2146 // all the blends ignore depth
2147 // R_Mesh_ResetTextureState();
2148 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2150 GL_DepthRange(0, 1);
2151 GL_PolygonOffset(0, 0);
2152 GL_DepthTest(false);
2153 if (v_color_enable.integer)
2155 c[0] = v_color_white_r.value;
2156 c[1] = v_color_white_g.value;
2157 c[2] = v_color_white_b.value;
2160 c[0] = c[1] = c[2] = v_contrast.value;
2161 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2163 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2164 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2166 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2167 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2168 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2169 VectorScale(c, 0.5, c);
2172 if (v_color_enable.integer)
2174 c[0] = v_color_black_r.value;
2175 c[1] = v_color_black_g.value;
2176 c[2] = v_color_black_b.value;
2179 c[0] = c[1] = c[2] = v_brightness.value;
2180 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2182 GL_BlendFunc(GL_ONE, GL_ONE);
2183 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2184 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2185 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);