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
437 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
442 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
444 if (developer_loading.integer)
445 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
447 if (!strcmp(pic->name, "gfx/conchars"))
449 // conchars is a raw image and with color 0 as transparent instead of 255
452 // if no high quality replacement image was found, upload the original low quality texture
456 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
461 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
462 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
463 // if no high quality replacement image was found, upload the original low quality texture
467 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
479 // if it's not found on disk, generate an image
480 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
481 pic->width = R_TextureWidth(pic->tex);
482 pic->height = R_TextureHeight(pic->tex);
488 cachepic_t *Draw_CachePic (const char *path)
490 return Draw_CachePic_Flags (path, 0); // default to persistent!
493 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
495 if (pic->autoload && !pic->tex)
497 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
499 qboolean ddshasalpha;
500 float ddsavgcolor[4];
501 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), pic->texflags, &ddshasalpha, ddsavgcolor, 0);
503 if (pic->tex == NULL)
505 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_sRGB_2d.integer != 0);
506 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
507 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
509 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
511 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_sRGB_2d.integer != 0);
512 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
513 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
515 if (pic->tex == NULL)
516 pic->tex = draw_generatepic(pic->name, true);
518 pic->lastusedframe = draw_frame;
522 void Draw_Frame(void)
526 static double nextpurgetime;
527 if (nextpurgetime > realtime)
529 nextpurgetime = realtime + 0.05;
530 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
532 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
534 R_FreeTexture(pic->tex);
541 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
546 crc = CRC_Block((unsigned char *)picname, strlen(picname));
547 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
548 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
549 if (!strcmp (picname, pic->name))
554 if (pic->tex && pic->width == width && pic->height == height)
556 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
564 if (numcachepics == MAX_CACHED_PICS)
566 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
567 // FIXME: support NULL in callers?
568 return cachepics; // return the first one
570 pic = cachepics + (numcachepics++);
571 strlcpy (pic->name, picname, sizeof(pic->name));
573 pic->chain = cachepichash[hashkey];
574 cachepichash[hashkey] = pic;
579 pic->height = height;
581 R_FreeTexture(pic->tex);
582 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
586 void Draw_FreePic(const char *picname)
591 // this doesn't really free the pic, but does free it's texture
592 crc = CRC_Block((unsigned char *)picname, strlen(picname));
593 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
594 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
596 if (!strcmp (picname, pic->name) && pic->tex)
598 R_FreeTexture(pic->tex);
607 static float snap_to_pixel_x(float x, float roundUpAt);
608 extern int con_linewidth; // to force rewrapping
609 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
613 char widthfile[MAX_QPATH];
615 fs_offset_t widthbufsize;
617 if(override || !fnt->texpath[0])
619 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
620 // load the cvars when the font is FIRST loader
621 fnt->settings.scale = scale;
622 fnt->settings.voffset = voffset;
623 fnt->settings.antialias = r_font_antialias.integer;
624 fnt->settings.hinting = r_font_hinting.integer;
625 fnt->settings.outline = r_font_postprocess_outline.value;
626 fnt->settings.blur = r_font_postprocess_blur.value;
627 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
628 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
629 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
632 if (fnt->settings.scale <= 0)
633 fnt->settings.scale = 1;
635 if(drawtexturepool == NULL)
636 return; // before gl_draw_start, so will be loaded later
640 // clear freetype font
641 Font_UnloadFont(fnt->ft2);
646 if(fnt->req_face != -1)
648 if(!Font_LoadFont(fnt->texpath, fnt))
649 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
652 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
653 if(fnt->tex == r_texture_notexture)
655 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
657 if (!fnt->fallbacks[i][0])
659 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
660 if(fnt->tex != r_texture_notexture)
663 if(fnt->tex == r_texture_notexture)
665 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
666 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
669 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
672 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
674 // unspecified width == 1 (base width)
675 for(ch = 0; ch < 256; ++ch)
676 fnt->width_of[ch] = 1;
678 // FIXME load "name.width", if it fails, fill all with 1
679 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
681 float extraspacing = 0;
682 const char *p = widthbuf;
687 if(!COM_ParseToken_Simple(&p, false, false))
705 fnt->width_of[ch] = atof(com_token) + extraspacing;
709 if(!strcmp(com_token, "extraspacing"))
711 if(!COM_ParseToken_Simple(&p, false, false))
713 extraspacing = atof(com_token);
715 else if(!strcmp(com_token, "scale"))
717 if(!COM_ParseToken_Simple(&p, false, false))
719 fnt->settings.scale = atof(com_token);
723 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
724 if(!COM_ParseToken_Simple(&p, false, false))
736 for (i = 0; i < MAX_FONT_SIZES; ++i)
738 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
741 for(ch = 0; ch < 256; ++ch)
742 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
746 maxwidth = fnt->width_of[0];
747 for(i = 1; i < 256; ++i)
748 maxwidth = max(maxwidth, fnt->width_of[i]);
749 fnt->maxwidth = maxwidth;
751 // fix up maxwidth for overlap
752 fnt->maxwidth *= fnt->settings.scale;
754 if(fnt == FONT_CONSOLE)
755 con_linewidth = -1; // rewrap console in next frame
758 extern cvar_t developer_font;
759 dp_font_t *FindFont(const char *title, qboolean allocate_new)
764 for(i = 0; i < dp_fonts.maxsize; ++i)
765 if(!strcmp(dp_fonts.f[i].title, title))
766 return &dp_fonts.f[i];
767 // if not found - try allocate
770 // find any font with empty title
771 for(i = 0; i < dp_fonts.maxsize; ++i)
773 if(!strcmp(dp_fonts.f[i].title, ""))
775 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
776 return &dp_fonts.f[i];
779 // if no any 'free' fonts - expand buffer
780 oldsize = dp_fonts.maxsize;
781 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
782 if (developer_font.integer)
783 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
784 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
785 // relink ft2 structures
786 for(i = 0; i < oldsize; ++i)
787 if (dp_fonts.f[i].ft2)
788 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
789 // register a font in first expanded slot
790 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
791 return &dp_fonts.f[oldsize];
796 static float snap_to_pixel_x(float x, float roundUpAt)
798 float pixelpos = x * vid.width / vid_conwidth.value;
799 int snap = (int) pixelpos;
800 if (pixelpos - snap >= roundUpAt) ++snap;
801 return ((float)snap * vid_conwidth.value / vid.width);
803 x = (int)(x * vid.width / vid_conwidth.value);
804 x = (x * vid_conwidth.value / vid.width);
809 static float snap_to_pixel_y(float y, float roundUpAt)
811 float pixelpos = y * vid.height / vid_conheight.value;
812 int snap = (int) pixelpos;
813 if (pixelpos - snap > roundUpAt) ++snap;
814 return ((float)snap * vid_conheight.value / vid.height);
816 y = (int)(y * vid.height / vid_conheight.value);
817 y = (y * vid_conheight.value / vid.height);
822 static void LoadFont_f(void)
826 const char *filelist, *c, *cm;
827 float sz, scale, voffset;
828 char mainfont[MAX_QPATH];
832 Con_Printf("Available font commands:\n");
833 for(i = 0; i < dp_fonts.maxsize; ++i)
834 if (dp_fonts.f[i].title[0])
835 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
836 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
837 "can specify multiple fonts and faces\n"
838 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
839 "to load face 2 of the font gfx/vera-sans and use face 1\n"
840 "of gfx/fallback as fallback font.\n"
841 "You can also specify a list of font sizes to load, like this:\n"
842 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
843 "In many cases, 8 12 16 24 32 should be a good choice.\n"
845 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
846 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
850 f = FindFont(Cmd_Argv(1), true);
853 Con_Printf("font function not found\n");
858 filelist = "gfx/conchars";
860 filelist = Cmd_Argv(2);
862 memset(f->fallbacks, 0, sizeof(f->fallbacks));
863 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
865 // first font is handled "normally"
866 c = strchr(filelist, ':');
867 cm = strchr(filelist, ',');
868 if(c && (!cm || c < cm))
869 f->req_face = atoi(c+1);
876 if(!c || (c - filelist) > MAX_QPATH)
877 strlcpy(mainfont, filelist, sizeof(mainfont));
880 memcpy(mainfont, filelist, c - filelist);
881 mainfont[c - filelist] = 0;
884 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
886 c = strchr(filelist, ',');
892 c = strchr(filelist, ':');
893 cm = strchr(filelist, ',');
894 if(c && (!cm || c < cm))
895 f->fallback_faces[i] = atoi(c+1);
898 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
901 if(!c || (c-filelist) > MAX_QPATH)
903 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
907 memcpy(f->fallbacks[i], filelist, c - filelist);
908 f->fallbacks[i][c - filelist] = 0;
912 // for now: by default load only one size: the default size
914 for(i = 1; i < MAX_FONT_SIZES; ++i)
915 f->req_sizes[i] = -1;
921 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
924 if (!strcmp(Cmd_Argv(i), "scale"))
928 scale = atof(Cmd_Argv(i));
931 if (!strcmp(Cmd_Argv(i), "voffset"))
935 voffset = atof(Cmd_Argv(i));
940 continue; // no slot for other sizes
942 // parse one of sizes
943 sz = atof(Cmd_Argv(i));
944 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
946 // search for duplicated sizes
948 for (j=0; j<sizes; j++)
949 if (f->req_sizes[j] == sz)
952 continue; // sz already in req_sizes, don't add it again
954 if (sizes == MAX_FONT_SIZES)
956 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
960 f->req_sizes[sizes] = sz;
966 LoadFont(true, mainfont, f, scale, voffset);
974 static void gl_draw_start(void)
977 drawtexturepool = R_AllocTexturePool();
980 memset(cachepichash, 0, sizeof(cachepichash));
984 // load default font textures
985 for(i = 0; i < dp_fonts.maxsize; ++i)
986 if (dp_fonts.f[i].title[0])
987 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
989 // draw the loading screen so people have something to see in the newly opened window
990 SCR_UpdateLoadingScreen(true);
993 static void gl_draw_shutdown(void)
997 R_FreeTexturePool(&drawtexturepool);
1000 memset(cachepichash, 0, sizeof(cachepichash));
1003 static void gl_draw_newmap(void)
1008 void GL_Draw_Init (void)
1012 Cvar_RegisterVariable(&r_font_postprocess_blur);
1013 Cvar_RegisterVariable(&r_font_postprocess_outline);
1014 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1015 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1016 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1017 Cvar_RegisterVariable(&r_font_hinting);
1018 Cvar_RegisterVariable(&r_font_antialias);
1019 Cvar_RegisterVariable(&r_textshadow);
1020 Cvar_RegisterVariable(&r_textbrightness);
1021 Cvar_RegisterVariable(&r_textcontrast);
1023 // allocate fonts storage
1024 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1025 dp_fonts.maxsize = MAX_FONTS;
1026 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1027 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1029 // assign starting font names
1030 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1031 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1032 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1033 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1034 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1035 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1036 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1037 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1038 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1039 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1040 if(!FONT_USER(i)->title[0])
1041 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1043 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1044 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1047 static void _DrawQ_Setup(void)
1049 r_viewport_t viewport;
1050 if (r_refdef.draw2dstage == 1)
1052 r_refdef.draw2dstage = 1;
1054 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);
1055 R_Mesh_ResetRenderTargets();
1056 R_SetViewport(&viewport);
1057 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1058 GL_DepthFunc(GL_LEQUAL);
1059 GL_PolygonOffset(0,0);
1060 GL_CullFace(GL_NONE);
1061 R_EntityMatrix(&identitymatrix);
1063 GL_DepthRange(0, 1);
1064 GL_PolygonOffset(0, 0);
1065 GL_DepthTest(false);
1069 qboolean r_draw2d_force = false;
1070 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1074 if(!r_draw2d.integer && !r_draw2d_force)
1076 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1078 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1080 if(flags == DRAWFLAG_ADDITIVE)
1082 GL_DepthMask(false);
1083 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1085 else if(flags == DRAWFLAG_MODULATE)
1087 GL_DepthMask(false);
1088 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1090 else if(flags == DRAWFLAG_2XMODULATE)
1092 GL_DepthMask(false);
1093 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1095 else if(flags == DRAWFLAG_SCREEN)
1097 GL_DepthMask(false);
1098 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1102 GL_DepthMask(false);
1103 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1108 GL_BlendFunc(GL_ONE, GL_ZERO);
1112 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1116 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1117 if(!r_draw2d.integer && !r_draw2d_force)
1120 // R_Mesh_ResetTextureState();
1121 floats[12] = 0.0f;floats[13] = 0.0f;
1122 floats[14] = 1.0f;floats[15] = 0.0f;
1123 floats[16] = 1.0f;floats[17] = 1.0f;
1124 floats[18] = 0.0f;floats[19] = 1.0f;
1125 floats[20] = floats[24] = floats[28] = floats[32] = red;
1126 floats[21] = floats[25] = floats[29] = floats[33] = green;
1127 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1128 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1134 height = pic->height;
1135 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1138 // AK07: lets be texel correct on the corners
1140 float horz_offset = 0.5f / pic->width;
1141 float vert_offset = 0.5f / pic->height;
1143 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1144 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1145 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1146 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1151 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1153 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1154 floats[0] = floats[9] = x;
1155 floats[1] = floats[4] = y;
1156 floats[3] = floats[6] = x + width;
1157 floats[7] = floats[10] = y + height;
1159 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1160 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1163 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)
1166 float af = DEG2RAD(-angle); // forward
1167 float ar = DEG2RAD(-angle + 90); // right
1168 float sinaf = sin(af);
1169 float cosaf = cos(af);
1170 float sinar = sin(ar);
1171 float cosar = cos(ar);
1173 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1174 if(!r_draw2d.integer && !r_draw2d_force)
1177 // R_Mesh_ResetTextureState();
1183 height = pic->height;
1184 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1187 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1189 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1192 floats[0] = x - cosaf*org_x - cosar*org_y;
1193 floats[1] = y - sinaf*org_x - sinar*org_y;
1196 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1197 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1200 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1201 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1204 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1205 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1207 floats[12] = 0.0f;floats[13] = 0.0f;
1208 floats[14] = 1.0f;floats[15] = 0.0f;
1209 floats[16] = 1.0f;floats[17] = 1.0f;
1210 floats[18] = 0.0f;floats[19] = 1.0f;
1211 floats[20] = floats[24] = floats[28] = floats[32] = red;
1212 floats[21] = floats[25] = floats[29] = floats[33] = green;
1213 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1214 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1216 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1217 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1220 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1224 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1225 if(!r_draw2d.integer && !r_draw2d_force)
1228 // R_Mesh_ResetTextureState();
1229 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1231 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1232 floats[0] = floats[9] = x;
1233 floats[1] = floats[4] = y;
1234 floats[3] = floats[6] = x + width;
1235 floats[7] = floats[10] = y + height;
1236 floats[12] = 0.0f;floats[13] = 0.0f;
1237 floats[14] = 1.0f;floats[15] = 0.0f;
1238 floats[16] = 1.0f;floats[17] = 1.0f;
1239 floats[18] = 0.0f;floats[19] = 1.0f;
1240 floats[20] = floats[24] = floats[28] = floats[32] = red;
1241 floats[21] = floats[25] = floats[29] = floats[33] = green;
1242 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1243 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1245 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1246 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1249 /// color tag printing
1250 static const vec4_t string_colors[] =
1253 // LordHavoc: why on earth is cyan before magenta in Quake3?
1254 // LordHavoc: note: Doom3 uses white for [0] and [7]
1255 {0.0, 0.0, 0.0, 1.0}, // black
1256 {1.0, 0.0, 0.0, 1.0}, // red
1257 {0.0, 1.0, 0.0, 1.0}, // green
1258 {1.0, 1.0, 0.0, 1.0}, // yellow
1259 {0.0, 0.0, 1.0, 1.0}, // blue
1260 {0.0, 1.0, 1.0, 1.0}, // cyan
1261 {1.0, 0.0, 1.0, 1.0}, // magenta
1262 {1.0, 1.0, 1.0, 1.0}, // white
1263 // [515]'s BX_COLOREDTEXT extension
1264 {1.0, 1.0, 1.0, 0.5}, // half transparent
1265 {0.5, 0.5, 0.5, 1.0} // half brightness
1266 // Black's color table
1267 //{1.0, 1.0, 1.0, 1.0},
1268 //{1.0, 0.0, 0.0, 1.0},
1269 //{0.0, 1.0, 0.0, 1.0},
1270 //{0.0, 0.0, 1.0, 1.0},
1271 //{1.0, 1.0, 0.0, 1.0},
1272 //{0.0, 1.0, 1.0, 1.0},
1273 //{1.0, 0.0, 1.0, 1.0},
1274 //{0.1, 0.1, 0.1, 1.0}
1277 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1279 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1281 float C = r_textcontrast.value;
1282 float B = r_textbrightness.value;
1283 if (colorindex & 0x10000) // that bit means RGB color
1285 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1286 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1287 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1288 color[3] = (colorindex & 0xf) / 15.0;
1291 Vector4Copy(string_colors[colorindex], color);
1292 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1295 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1296 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1300 // NOTE: this function always draws exactly one character if maxwidth <= 0
1301 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)
1303 const char *text_start = text;
1304 int colorindex = STRING_COLOR_DEFAULT;
1307 Uchar ch, mapch, nextch;
1308 Uchar prevch = 0; // used for kerning
1313 ft2_font_map_t *fontmap = NULL;
1314 ft2_font_map_t *map = NULL;
1315 //ft2_font_map_t *prevmap = NULL;
1316 ft2_font_t *ft2 = fnt->ft2;
1318 qboolean snap = true;
1319 qboolean least_one = false;
1320 float dw; // display w
1321 //float dh; // display h
1322 const float *width_of;
1329 // do this in the end
1330 w *= fnt->settings.scale;
1331 h *= fnt->settings.scale;
1333 // find the most fitting size:
1337 map_index = Font_IndexForSize(ft2, h, &w, &h);
1339 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1340 fontmap = Font_MapForIndex(ft2, map_index);
1349 if (!outcolor || *outcolor == -1)
1350 colorindex = STRING_COLOR_DEFAULT;
1352 colorindex = *outcolor;
1354 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1355 // ftbase_x = snap_to_pixel_x(0);
1360 maxwidth = -maxwidth;
1364 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1367 width_of = fontmap->width_of;
1369 width_of = fnt->width_of;
1371 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1374 nextch = ch = u8_getnchar(text, &text, bytes_left);
1375 i = text - text_start;
1378 if (ch == ' ' && !fontmap)
1380 if(!least_one || i0) // never skip the first character
1381 if(x + width_of[(int) ' '] * dw > maxwidth)
1384 break; // oops, can't draw this
1386 x += width_of[(int) ' '] * dw;
1389 // i points to the char after ^
1390 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1392 ch = *text; // colors are ascii, so no u8_ needed
1393 if (ch <= '9' && ch >= '0') // ^[0-9] found
1395 colorindex = ch - '0';
1400 // i points to the char after ^...
1401 // i+3 points to 3 in ^x123
1402 // i+3 == *maxlen would mean that char is missing
1403 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1405 // building colorindex...
1406 ch = tolower(text[1]);
1407 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1408 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1409 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1410 else tempcolorindex = 0;
1413 ch = tolower(text[2]);
1414 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1415 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1416 else tempcolorindex = 0;
1419 ch = tolower(text[3]);
1420 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1421 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1422 else tempcolorindex = 0;
1425 colorindex = tempcolorindex | 0xf;
1426 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1434 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1443 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1450 map = ft2_oldstyle_map;
1452 if(!least_one || i0) // never skip the first character
1453 if(x + width_of[ch] * dw > maxwidth)
1456 break; // oops, can't draw this
1458 x += width_of[ch] * dw;
1460 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1462 map = FontMap_FindForChar(fontmap, ch);
1465 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1471 mapch = ch - map->start;
1472 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1474 x += map->glyphs[mapch].advance_x * dw;
1483 *outcolor = colorindex;
1488 float DrawQ_Color[4];
1489 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)
1491 int shadow, colorindex = STRING_COLOR_DEFAULT;
1493 float x = startx, y, s, t, u, v, thisw;
1494 float *av, *at, *ac;
1496 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1497 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1498 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1499 Uchar ch, mapch, nextch;
1500 Uchar prevch = 0; // used for kerning
1503 //ft2_font_map_t *prevmap = NULL; // the previous map
1504 ft2_font_map_t *map = NULL; // the currently used map
1505 ft2_font_map_t *fontmap = NULL; // the font map for the size
1507 const char *text_start = text;
1509 ft2_font_t *ft2 = fnt->ft2;
1510 qboolean snap = true;
1514 const float *width_of;
1517 tw = R_TextureWidth(fnt->tex);
1518 th = R_TextureHeight(fnt->tex);
1526 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1527 w *= fnt->settings.scale;
1528 h *= fnt->settings.scale;
1533 map_index = Font_IndexForSize(ft2, h, &w, &h);
1535 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1536 fontmap = Font_MapForIndex(ft2, map_index);
1542 // draw the font at its baseline when using freetype
1544 ftbase_y = dh * (4.5/6.0);
1549 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1550 if(!r_draw2d.integer && !r_draw2d_force)
1551 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1553 // R_Mesh_ResetTextureState();
1555 R_Mesh_TexBind(0, fnt->tex);
1556 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1563 //ftbase_x = snap_to_pixel_x(ftbase_x);
1566 startx = snap_to_pixel_x(startx, 0.4);
1567 starty = snap_to_pixel_y(starty, 0.4);
1568 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1571 pix_x = vid.width / vid_conwidth.value;
1572 pix_y = vid.height / vid_conheight.value;
1575 width_of = fontmap->width_of;
1577 width_of = fnt->width_of;
1579 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1584 if (!outcolor || *outcolor == -1)
1585 colorindex = STRING_COLOR_DEFAULT;
1587 colorindex = *outcolor;
1589 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1596 x += r_textshadow.value * vid.width / vid_conwidth.value;
1597 y += r_textshadow.value * vid.height / vid_conheight.value;
1600 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1602 nextch = ch = u8_getnchar(text, &text, bytes_left);
1603 i = text - text_start;
1606 if (ch == ' ' && !fontmap)
1608 x += width_of[(int) ' '] * dw;
1611 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1613 ch = *text; // colors are ascii, so no u8_ needed
1614 if (ch <= '9' && ch >= '0') // ^[0-9] found
1616 colorindex = ch - '0';
1617 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1622 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1624 // building colorindex...
1625 ch = tolower(text[1]);
1626 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1627 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1628 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1629 else tempcolorindex = 0;
1632 ch = tolower(text[2]);
1633 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1634 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1635 else tempcolorindex = 0;
1638 ch = tolower(text[3]);
1639 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1640 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1641 else tempcolorindex = 0;
1644 colorindex = tempcolorindex | 0xf;
1645 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1646 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1647 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1655 else if (ch == STRING_COLOR_TAG)
1664 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1665 // this way we don't need to rebind fnt->tex for every old-style character
1666 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1669 x += 1.0/pix_x * r_textshadow.value;
1670 y += 1.0/pix_y * r_textshadow.value;
1672 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1680 if (map != ft2_oldstyle_map)
1684 // switching from freetype to non-freetype rendering
1685 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1686 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1692 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1693 map = ft2_oldstyle_map;
1697 //num = (unsigned char) text[i];
1698 //thisw = fnt->width_of[num];
1699 thisw = fnt->width_of[ch];
1700 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1701 s = (ch & 15)*0.0625f + (0.5f / tw);
1702 t = (ch >> 4)*0.0625f + (0.5f / th);
1703 u = 0.0625f * thisw - (1.0f / tw);
1704 v = 0.0625f - (1.0f / th);
1705 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1706 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1707 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1708 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1709 at[ 0] = s ; at[ 1] = t ;
1710 at[ 2] = s+u ; at[ 3] = t ;
1711 at[ 4] = s+u ; at[ 5] = t+v ;
1712 at[ 6] = s ; at[ 7] = t+v ;
1713 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1714 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1715 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1716 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1721 if (batchcount >= QUADELEMENTS_MAXQUADS)
1723 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1724 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1730 x += width_of[ch] * dw;
1732 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1734 // new charmap - need to render
1737 // we need a different character map, render what we currently have:
1738 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1739 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1746 map = FontMap_FindForChar(fontmap, ch);
1749 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1756 // this shouldn't happen
1761 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1);
1764 mapch = ch - map->start;
1765 thisw = map->glyphs[mapch].advance_x;
1769 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1776 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1777 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1778 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1779 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1780 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1781 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1782 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1783 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1784 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1785 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1786 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1787 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1796 if (batchcount >= QUADELEMENTS_MAXQUADS)
1798 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1799 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1812 x -= 1.0/pix_x * r_textshadow.value;
1813 y -= 1.0/pix_y * r_textshadow.value;
1819 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1820 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1824 *outcolor = colorindex;
1826 // note: this relies on the proper text (not shadow) being drawn last
1830 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)
1832 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1835 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)
1837 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1840 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1842 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1845 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1847 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1852 // no ^xrgb management
1853 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1855 int color, numchars = 0;
1856 char *outputend2c = output2c + maxoutchars - 2;
1857 if (!outcolor || *outcolor == -1)
1858 color = STRING_COLOR_DEFAULT;
1862 maxreadchars = 1<<30;
1863 textend = text + maxreadchars;
1864 while (text != textend && *text)
1866 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1868 if (text[1] == STRING_COLOR_TAG)
1870 else if (text[1] >= '0' && text[1] <= '9')
1872 color = text[1] - '0';
1877 if (output2c >= outputend2c)
1879 *output2c++ = *text++;
1880 *output2c++ = color;
1883 output2c[0] = output2c[1] = 0;
1890 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)
1894 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1895 if(!r_draw2d.integer && !r_draw2d_force)
1898 // R_Mesh_ResetTextureState();
1904 height = pic->height;
1905 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1908 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1910 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1911 floats[0] = floats[9] = x;
1912 floats[1] = floats[4] = y;
1913 floats[3] = floats[6] = x + width;
1914 floats[7] = floats[10] = y + height;
1915 floats[12] = s1;floats[13] = t1;
1916 floats[14] = s2;floats[15] = t2;
1917 floats[16] = s4;floats[17] = t4;
1918 floats[18] = s3;floats[19] = t3;
1919 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1920 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1921 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1922 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1924 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1925 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1928 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1932 if(!r_draw2d.integer && !r_draw2d_force)
1934 DrawQ_ProcessDrawFlag(flags, hasalpha);
1936 // R_Mesh_ResetTextureState();
1937 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1939 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1940 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1943 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1947 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1948 if(!r_draw2d.integer && !r_draw2d_force)
1952 switch(vid.renderpath)
1954 case RENDERPATH_GL11:
1955 case RENDERPATH_GL13:
1956 case RENDERPATH_GL20:
1958 qglBegin(GL_LINE_LOOP);
1959 for (num = 0;num < mesh->num_vertices;num++)
1961 if (mesh->data_color4f)
1962 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]);
1963 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1968 case RENDERPATH_D3D9:
1969 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1971 case RENDERPATH_D3D10:
1972 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1974 case RENDERPATH_D3D11:
1975 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1977 case RENDERPATH_SOFT:
1978 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1980 case RENDERPATH_GLES1:
1981 case RENDERPATH_GLES2:
1982 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1987 //[515]: this is old, delete
1988 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1990 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1991 if(!r_draw2d.integer && !r_draw2d_force)
1994 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1996 switch(vid.renderpath)
1998 case RENDERPATH_GL11:
1999 case RENDERPATH_GL13:
2000 case RENDERPATH_GL20:
2003 //qglLineWidth(width);CHECKGLERROR
2005 GL_Color(r,g,b,alpha);
2008 qglVertex2f(x1, y1);
2009 qglVertex2f(x2, y2);
2013 case RENDERPATH_D3D9:
2014 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2016 case RENDERPATH_D3D10:
2017 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2019 case RENDERPATH_D3D11:
2020 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2022 case RENDERPATH_SOFT:
2023 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2025 case RENDERPATH_GLES1:
2026 case RENDERPATH_GLES2:
2027 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2032 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2035 qboolean hasalpha = false;
2036 for (i = 0;i < numlines*2;i++)
2037 if (color4f[i*4+3] < 1.0f)
2040 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2042 if(!r_draw2d.integer && !r_draw2d_force)
2045 switch(vid.renderpath)
2047 case RENDERPATH_GL11:
2048 case RENDERPATH_GL13:
2049 case RENDERPATH_GL20:
2052 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2054 //qglLineWidth(width);CHECKGLERROR
2057 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2058 qglDrawArrays(GL_LINES, 0, numlines*2);
2061 case RENDERPATH_D3D9:
2062 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2064 case RENDERPATH_D3D10:
2065 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2067 case RENDERPATH_D3D11:
2068 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2070 case RENDERPATH_SOFT:
2071 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2073 case RENDERPATH_GLES1:
2074 case RENDERPATH_GLES2:
2075 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2080 void DrawQ_SetClipArea(float x, float y, float width, float height)
2085 // We have to convert the con coords into real coords
2086 // OGL uses top to bottom
2087 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2088 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2089 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2090 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2091 switch(vid.renderpath)
2093 case RENDERPATH_GL11:
2094 case RENDERPATH_GL13:
2095 case RENDERPATH_GL20:
2096 case RENDERPATH_GLES1:
2097 case RENDERPATH_GLES2:
2098 case RENDERPATH_SOFT:
2099 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2101 case RENDERPATH_D3D9:
2102 GL_Scissor(ix, iy, iw, ih);
2104 case RENDERPATH_D3D10:
2105 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2107 case RENDERPATH_D3D11:
2108 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2112 GL_ScissorTest(true);
2115 void DrawQ_ResetClipArea(void)
2118 GL_ScissorTest(false);
2121 void DrawQ_Finish(void)
2123 r_refdef.draw2dstage = 0;
2126 void DrawQ_RecalcView(void)
2128 if(r_refdef.draw2dstage)
2129 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2132 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2133 void R_DrawGamma(void)
2136 switch(vid.renderpath)
2138 case RENDERPATH_GL20:
2139 case RENDERPATH_D3D9:
2140 case RENDERPATH_D3D10:
2141 case RENDERPATH_D3D11:
2142 case RENDERPATH_GLES2:
2143 if (vid_usinghwgamma || v_glslgamma.integer)
2146 case RENDERPATH_GL11:
2147 case RENDERPATH_GL13:
2148 if (vid_usinghwgamma)
2151 case RENDERPATH_GLES1:
2152 case RENDERPATH_SOFT:
2155 // all the blends ignore depth
2156 // R_Mesh_ResetTextureState();
2157 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2159 GL_DepthRange(0, 1);
2160 GL_PolygonOffset(0, 0);
2161 GL_DepthTest(false);
2162 if (v_color_enable.integer)
2164 c[0] = v_color_white_r.value;
2165 c[1] = v_color_white_g.value;
2166 c[2] = v_color_white_b.value;
2169 c[0] = c[1] = c[2] = v_contrast.value;
2170 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2172 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2173 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2175 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2176 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2177 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2178 VectorScale(c, 0.5, c);
2181 if (v_color_enable.integer)
2183 c[0] = v_color_black_r.value;
2184 c[1] = v_color_black_g.value;
2185 c[2] = v_color_black_b.value;
2188 c[0] = c[1] = c[2] = v_brightness.value;
2189 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2191 GL_BlendFunc(GL_ONE, GL_ONE);
2192 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2193 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2194 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);