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 static 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;
311 // FIXME: move this to client somehow
312 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
315 unsigned char *pixels;
318 unsigned char *lmpdata;
319 char lmpname[MAX_QPATH];
323 texflags = TEXF_ALPHA;
324 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
325 texflags |= TEXF_CLAMP;
326 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
327 texflags |= TEXF_COMPRESS;
329 // check whether the picture has already been cached
330 crc = CRC_Block((unsigned char *)path, strlen(path));
331 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
332 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
333 if (!strcmp (path, pic->name))
334 if(!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
336 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
337 pic->autoload = false; // persist it
341 if (numcachepics == MAX_CACHED_PICS)
343 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
344 // FIXME: support NULL in callers?
345 return cachepics; // return the first one
347 pic = cachepics + (numcachepics++);
348 strlcpy (pic->name, path, sizeof(pic->name));
350 pic->chain = cachepichash[hashkey];
351 cachepichash[hashkey] = pic;
353 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
354 pic->tex = CL_GetDynTexture( path );
355 // if so, set the width/height, too
357 pic->width = R_TextureWidth(pic->tex);
358 pic->height = R_TextureHeight(pic->tex);
359 // we're done now (early-out)
363 pic->hasalpha = true; // assume alpha unless we know it has none
364 pic->texflags = texflags;
365 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
367 // load a high quality image from disk if possible
368 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer != 0, NULL);
369 if (pixels == NULL && !strncmp(path, "gfx/", 4))
370 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer != 0, NULL);
373 pic->hasalpha = false;
374 if (pic->texflags & TEXF_ALPHA)
376 for (j = 3;j < image_width * image_height * 4;j += 4)
380 pic->hasalpha = true;
386 pic->width = image_width;
387 pic->height = image_height;
389 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
393 pic->autoload = false;
394 // never compress the fallback images
395 pic->texflags &= ~TEXF_COMPRESS;
398 // now read the low quality version (wad or lmp file), and take the pic
399 // size from that even if we don't upload the texture, this way the pics
400 // show up the right size in the menu even if they were replaced with
401 // higher or lower resolution versions
402 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
403 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
405 if (developer_loading.integer)
406 Con_Printf("loading lump \"%s\"\n", path);
410 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
411 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
412 // if no high quality replacement image was found, upload the original low quality texture
414 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
418 else if ((lmpdata = W_GetLumpName (path + 4)))
420 if (developer_loading.integer)
421 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
423 if (!strcmp(path, "gfx/conchars"))
425 // conchars is a raw image and with color 0 as transparent instead of 255
428 // if no high quality replacement image was found, upload the original low quality texture
430 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
434 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
435 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
436 // if no high quality replacement image was found, upload the original low quality texture
438 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
447 else if (pic->tex == NULL)
449 // if it's not found on disk, generate an image
450 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
451 pic->width = R_TextureWidth(pic->tex);
452 pic->height = R_TextureHeight(pic->tex);
458 cachepic_t *Draw_CachePic (const char *path)
460 return Draw_CachePic_Flags (path, 0); // default to persistent!
465 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
467 if (pic->autoload && !pic->tex)
469 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer != 0);
470 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
471 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer != 0);
472 if (pic->tex == NULL)
473 pic->tex = draw_generatepic(pic->name, true);
475 pic->lastusedframe = draw_frame;
479 void Draw_Frame(void)
483 static double nextpurgetime;
484 if (nextpurgetime > realtime)
486 nextpurgetime = realtime + 0.05;
487 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
489 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
491 R_FreeTexture(pic->tex);
498 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
503 crc = CRC_Block((unsigned char *)picname, strlen(picname));
504 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
505 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
506 if (!strcmp (picname, pic->name))
511 if (pic->tex && pic->width == width && pic->height == height)
513 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
521 if (numcachepics == MAX_CACHED_PICS)
523 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
524 // FIXME: support NULL in callers?
525 return cachepics; // return the first one
527 pic = cachepics + (numcachepics++);
528 strlcpy (pic->name, picname, sizeof(pic->name));
530 pic->chain = cachepichash[hashkey];
531 cachepichash[hashkey] = pic;
536 pic->height = height;
538 R_FreeTexture(pic->tex);
539 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
543 void Draw_FreePic(const char *picname)
548 // this doesn't really free the pic, but does free it's texture
549 crc = CRC_Block((unsigned char *)picname, strlen(picname));
550 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
551 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
553 if (!strcmp (picname, pic->name) && pic->tex)
555 R_FreeTexture(pic->tex);
564 static float snap_to_pixel_x(float x, float roundUpAt);
565 extern int con_linewidth; // to force rewrapping
566 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
570 char widthfile[MAX_QPATH];
572 fs_offset_t widthbufsize;
574 if(override || !fnt->texpath[0])
576 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
577 // load the cvars when the font is FIRST loader
578 fnt->settings.scale = scale;
579 fnt->settings.voffset = voffset;
580 fnt->settings.antialias = r_font_antialias.integer;
581 fnt->settings.hinting = r_font_hinting.integer;
582 fnt->settings.outline = r_font_postprocess_outline.value;
583 fnt->settings.blur = r_font_postprocess_blur.value;
584 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
585 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
586 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
589 if (fnt->settings.scale <= 0)
590 fnt->settings.scale = 1;
592 if(drawtexturepool == NULL)
593 return; // before gl_draw_start, so will be loaded later
597 // clear freetype font
598 Font_UnloadFont(fnt->ft2);
603 if(fnt->req_face != -1)
605 if(!Font_LoadFont(fnt->texpath, fnt))
606 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
609 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
610 if(fnt->tex == r_texture_notexture)
612 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
614 if (!fnt->fallbacks[i][0])
616 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
617 if(fnt->tex != r_texture_notexture)
620 if(fnt->tex == r_texture_notexture)
622 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
623 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
626 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
629 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
631 // unspecified width == 1 (base width)
632 for(ch = 0; ch < 256; ++ch)
633 fnt->width_of[ch] = 1;
635 // FIXME load "name.width", if it fails, fill all with 1
636 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
638 float extraspacing = 0;
639 const char *p = widthbuf;
644 if(!COM_ParseToken_Simple(&p, false, false))
662 fnt->width_of[ch] = atof(com_token) + extraspacing;
666 if(!strcmp(com_token, "extraspacing"))
668 if(!COM_ParseToken_Simple(&p, false, false))
670 extraspacing = atof(com_token);
672 else if(!strcmp(com_token, "scale"))
674 if(!COM_ParseToken_Simple(&p, false, false))
676 fnt->settings.scale = atof(com_token);
680 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
681 if(!COM_ParseToken_Simple(&p, false, false))
693 for (i = 0; i < MAX_FONT_SIZES; ++i)
695 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
698 for(ch = 0; ch < 256; ++ch)
699 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
703 maxwidth = fnt->width_of[0];
704 for(i = 1; i < 256; ++i)
705 maxwidth = max(maxwidth, fnt->width_of[i]);
706 fnt->maxwidth = maxwidth;
708 // fix up maxwidth for overlap
709 fnt->maxwidth *= fnt->settings.scale;
711 if(fnt == FONT_CONSOLE)
712 con_linewidth = -1; // rewrap console in next frame
715 extern cvar_t developer_font;
716 dp_font_t *FindFont(const char *title, qboolean allocate_new)
721 for(i = 0; i < dp_fonts.maxsize; ++i)
722 if(!strcmp(dp_fonts.f[i].title, title))
723 return &dp_fonts.f[i];
724 // if not found - try allocate
727 // find any font with empty title
728 for(i = 0; i < dp_fonts.maxsize; ++i)
730 if(!strcmp(dp_fonts.f[i].title, ""))
732 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
733 return &dp_fonts.f[i];
736 // if no any 'free' fonts - expand buffer
737 i = dp_fonts.maxsize;
738 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
739 if (developer_font.integer)
740 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
741 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
742 // register a font in first expanded slot
743 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
744 return &dp_fonts.f[i];
749 static float snap_to_pixel_x(float x, float roundUpAt)
751 float pixelpos = x * vid.width / vid_conwidth.value;
752 int snap = (int) pixelpos;
753 if (pixelpos - snap >= roundUpAt) ++snap;
754 return ((float)snap * vid_conwidth.value / vid.width);
756 x = (int)(x * vid.width / vid_conwidth.value);
757 x = (x * vid_conwidth.value / vid.width);
762 static float snap_to_pixel_y(float y, float roundUpAt)
764 float pixelpos = y * vid.height / vid_conheight.value;
765 int snap = (int) pixelpos;
766 if (pixelpos - snap > roundUpAt) ++snap;
767 return ((float)snap * vid_conheight.value / vid.height);
769 y = (int)(y * vid.height / vid_conheight.value);
770 y = (y * vid_conheight.value / vid.height);
775 static void LoadFont_f(void)
779 const char *filelist, *c, *cm;
780 float sz, scale, voffset;
781 char mainfont[MAX_QPATH];
785 Con_Printf("Available font commands:\n");
786 for(i = 0; i < dp_fonts.maxsize; ++i)
787 if (dp_fonts.f[i].title[0])
788 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
789 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
790 "can specify multiple fonts and faces\n"
791 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
792 "to load face 2 of the font gfx/vera-sans and use face 1\n"
793 "of gfx/fallback as fallback font.\n"
794 "You can also specify a list of font sizes to load, like this:\n"
795 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
796 "In many cases, 8 12 16 24 32 should be a good choice.\n"
798 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
799 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
803 f = FindFont(Cmd_Argv(1), true);
806 Con_Printf("font function not found\n");
811 filelist = "gfx/conchars";
813 filelist = Cmd_Argv(2);
815 memset(f->fallbacks, 0, sizeof(f->fallbacks));
816 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
818 // first font is handled "normally"
819 c = strchr(filelist, ':');
820 cm = strchr(filelist, ',');
821 if(c && (!cm || c < cm))
822 f->req_face = atoi(c+1);
829 if(!c || (c - filelist) > MAX_QPATH)
830 strlcpy(mainfont, filelist, sizeof(mainfont));
833 memcpy(mainfont, filelist, c - filelist);
834 mainfont[c - filelist] = 0;
837 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
839 c = strchr(filelist, ',');
845 c = strchr(filelist, ':');
846 cm = strchr(filelist, ',');
847 if(c && (!cm || c < cm))
848 f->fallback_faces[i] = atoi(c+1);
851 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
854 if(!c || (c-filelist) > MAX_QPATH)
856 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
860 memcpy(f->fallbacks[i], filelist, c - filelist);
861 f->fallbacks[i][c - filelist] = 0;
865 // for now: by default load only one size: the default size
867 for(i = 1; i < MAX_FONT_SIZES; ++i)
868 f->req_sizes[i] = -1;
874 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
877 if (!strcmp(Cmd_Argv(i), "scale"))
881 scale = atof(Cmd_Argv(i));
884 if (!strcmp(Cmd_Argv(i), "voffset"))
888 voffset = atof(Cmd_Argv(i));
893 continue; // no slot for other sizes
895 // parse one of sizes
896 sz = atof(Cmd_Argv(i));
897 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
899 // search for duplicated sizes
901 for (j=0; j<sizes; j++)
902 if (f->req_sizes[j] == sz)
905 continue; // sz already in req_sizes, don't add it again
907 if (sizes == MAX_FONT_SIZES)
909 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
913 f->req_sizes[sizes] = sz;
919 LoadFont(true, mainfont, f, scale, voffset);
927 static void gl_draw_start(void)
930 drawtexturepool = R_AllocTexturePool();
933 memset(cachepichash, 0, sizeof(cachepichash));
937 // load default font textures
938 for(i = 0; i < dp_fonts.maxsize; ++i)
939 if (dp_fonts.f[i].title[0])
940 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
942 // draw the loading screen so people have something to see in the newly opened window
943 SCR_UpdateLoadingScreen(true);
946 static void gl_draw_shutdown(void)
950 R_FreeTexturePool(&drawtexturepool);
953 memset(cachepichash, 0, sizeof(cachepichash));
956 static void gl_draw_newmap(void)
961 void GL_Draw_Init (void)
965 Cvar_RegisterVariable(&r_font_postprocess_blur);
966 Cvar_RegisterVariable(&r_font_postprocess_outline);
967 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
968 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
969 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
970 Cvar_RegisterVariable(&r_font_hinting);
971 Cvar_RegisterVariable(&r_font_antialias);
972 Cvar_RegisterVariable(&r_textshadow);
973 Cvar_RegisterVariable(&r_textbrightness);
974 Cvar_RegisterVariable(&r_textcontrast);
976 // allocate fonts storage
977 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
978 dp_fonts.maxsize = MAX_FONTS;
979 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
980 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
982 // assign starting font names
983 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
984 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
985 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
986 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
987 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
988 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
989 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
990 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
991 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
992 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
993 if(!FONT_USER(i)->title[0])
994 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
996 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
997 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1000 static void _DrawQ_Setup(void)
1002 r_viewport_t viewport;
1003 if (r_refdef.draw2dstage == 1)
1005 r_refdef.draw2dstage = 1;
1007 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);
1008 R_SetViewport(&viewport);
1009 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1010 GL_DepthFunc(GL_LEQUAL);
1011 GL_PolygonOffset(0,0);
1012 GL_CullFace(GL_NONE);
1013 R_EntityMatrix(&identitymatrix);
1015 GL_DepthRange(0, 1);
1016 GL_PolygonOffset(0, 0);
1017 GL_DepthTest(false);
1019 GL_AlphaTest(false);
1022 qboolean r_draw2d_force = false;
1023 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1027 if(!r_draw2d.integer && !r_draw2d_force)
1029 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1031 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1033 if(flags == DRAWFLAG_ADDITIVE)
1035 GL_DepthMask(false);
1036 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1038 else if(flags == DRAWFLAG_MODULATE)
1040 GL_DepthMask(false);
1041 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1043 else if(flags == DRAWFLAG_2XMODULATE)
1045 GL_DepthMask(false);
1046 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1048 else if(flags == DRAWFLAG_SCREEN)
1050 GL_DepthMask(false);
1051 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1055 GL_DepthMask(false);
1056 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1061 GL_BlendFunc(GL_ONE, GL_ZERO);
1065 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1069 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1070 if(!r_draw2d.integer && !r_draw2d_force)
1073 R_Mesh_ResetTextureState();
1074 floats[12] = 0.0f;floats[13] = 0.0f;
1075 floats[14] = 1.0f;floats[15] = 0.0f;
1076 floats[16] = 1.0f;floats[17] = 1.0f;
1077 floats[18] = 0.0f;floats[19] = 1.0f;
1078 floats[20] = floats[24] = floats[28] = floats[32] = red;
1079 floats[21] = floats[25] = floats[29] = floats[33] = green;
1080 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1081 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1087 height = pic->height;
1088 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1091 // AK07: lets be texel correct on the corners
1093 float horz_offset = 0.5f / pic->width;
1094 float vert_offset = 0.5f / pic->height;
1096 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1097 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1098 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1099 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1104 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1106 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1107 floats[0] = floats[9] = x;
1108 floats[1] = floats[4] = y;
1109 floats[3] = floats[6] = x + width;
1110 floats[7] = floats[10] = y + height;
1112 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1113 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1116 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)
1119 float af = DEG2RAD(-angle); // forward
1120 float ar = DEG2RAD(-angle + 90); // right
1121 float sinaf = sin(af);
1122 float cosaf = cos(af);
1123 float sinar = sin(ar);
1124 float cosar = cos(ar);
1126 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1127 if(!r_draw2d.integer && !r_draw2d_force)
1130 R_Mesh_ResetTextureState();
1136 height = pic->height;
1137 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1140 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1142 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1145 floats[0] = x - cosaf*org_x - cosar*org_y;
1146 floats[1] = y - sinaf*org_x - sinar*org_y;
1149 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1150 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1153 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1154 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1157 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1158 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1160 floats[12] = 0.0f;floats[13] = 0.0f;
1161 floats[14] = 1.0f;floats[15] = 0.0f;
1162 floats[16] = 1.0f;floats[17] = 1.0f;
1163 floats[18] = 0.0f;floats[19] = 1.0f;
1164 floats[20] = floats[24] = floats[28] = floats[32] = red;
1165 floats[21] = floats[25] = floats[29] = floats[33] = green;
1166 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1167 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1169 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1170 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1173 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1177 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1178 if(!r_draw2d.integer && !r_draw2d_force)
1181 R_Mesh_ResetTextureState();
1182 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1184 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1185 floats[0] = floats[9] = x;
1186 floats[1] = floats[4] = y;
1187 floats[3] = floats[6] = x + width;
1188 floats[7] = floats[10] = y + height;
1189 floats[12] = 0.0f;floats[13] = 0.0f;
1190 floats[14] = 1.0f;floats[15] = 0.0f;
1191 floats[16] = 1.0f;floats[17] = 1.0f;
1192 floats[18] = 0.0f;floats[19] = 1.0f;
1193 floats[20] = floats[24] = floats[28] = floats[32] = red;
1194 floats[21] = floats[25] = floats[29] = floats[33] = green;
1195 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1196 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1198 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1199 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1202 /// color tag printing
1203 static const vec4_t string_colors[] =
1206 // LordHavoc: why on earth is cyan before magenta in Quake3?
1207 // LordHavoc: note: Doom3 uses white for [0] and [7]
1208 {0.0, 0.0, 0.0, 1.0}, // black
1209 {1.0, 0.0, 0.0, 1.0}, // red
1210 {0.0, 1.0, 0.0, 1.0}, // green
1211 {1.0, 1.0, 0.0, 1.0}, // yellow
1212 {0.0, 0.0, 1.0, 1.0}, // blue
1213 {0.0, 1.0, 1.0, 1.0}, // cyan
1214 {1.0, 0.0, 1.0, 1.0}, // magenta
1215 {1.0, 1.0, 1.0, 1.0}, // white
1216 // [515]'s BX_COLOREDTEXT extension
1217 {1.0, 1.0, 1.0, 0.5}, // half transparent
1218 {0.5, 0.5, 0.5, 1.0} // half brightness
1219 // Black's color table
1220 //{1.0, 1.0, 1.0, 1.0},
1221 //{1.0, 0.0, 0.0, 1.0},
1222 //{0.0, 1.0, 0.0, 1.0},
1223 //{0.0, 0.0, 1.0, 1.0},
1224 //{1.0, 1.0, 0.0, 1.0},
1225 //{0.0, 1.0, 1.0, 1.0},
1226 //{1.0, 0.0, 1.0, 1.0},
1227 //{0.1, 0.1, 0.1, 1.0}
1230 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1232 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1234 float C = r_textcontrast.value;
1235 float B = r_textbrightness.value;
1236 if (colorindex & 0x10000) // that bit means RGB color
1238 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1239 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1240 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1241 color[3] = (colorindex & 0xf) / 15.0;
1244 Vector4Copy(string_colors[colorindex], color);
1245 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1248 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1249 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1253 // NOTE: this function always draws exactly one character if maxwidth <= 0
1254 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)
1256 const char *text_start = text;
1257 int colorindex = STRING_COLOR_DEFAULT;
1260 Uchar ch, mapch, nextch;
1261 Uchar prevch = 0; // used for kerning
1266 ft2_font_map_t *fontmap = NULL;
1267 ft2_font_map_t *map = NULL;
1268 //ft2_font_map_t *prevmap = NULL;
1269 ft2_font_t *ft2 = fnt->ft2;
1271 qboolean snap = true;
1272 qboolean least_one = false;
1273 float dw; // display w
1274 //float dh; // display h
1275 const float *width_of;
1282 // do this in the end
1283 w *= fnt->settings.scale;
1284 h *= fnt->settings.scale;
1286 // find the most fitting size:
1290 map_index = Font_IndexForSize(ft2, h, &w, &h);
1292 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1293 fontmap = Font_MapForIndex(ft2, map_index);
1302 if (!outcolor || *outcolor == -1)
1303 colorindex = STRING_COLOR_DEFAULT;
1305 colorindex = *outcolor;
1307 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1308 // ftbase_x = snap_to_pixel_x(0);
1313 maxwidth = -maxwidth;
1317 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1320 width_of = fontmap->width_of;
1322 width_of = fnt->width_of;
1324 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1327 nextch = ch = u8_getnchar(text, &text, bytes_left);
1328 i = text - text_start;
1331 if (ch == ' ' && !fontmap)
1333 if(!least_one || i0) // never skip the first character
1334 if(x + width_of[(int) ' '] * dw > maxwidth)
1337 break; // oops, can't draw this
1339 x += width_of[(int) ' '] * dw;
1342 // i points to the char after ^
1343 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1345 ch = *text; // colors are ascii, so no u8_ needed
1346 if (ch <= '9' && ch >= '0') // ^[0-9] found
1348 colorindex = ch - '0';
1353 // i points to the char after ^...
1354 // i+3 points to 3 in ^x123
1355 // i+3 == *maxlen would mean that char is missing
1356 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1358 // building colorindex...
1359 ch = tolower(text[1]);
1360 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1361 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1362 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1363 else tempcolorindex = 0;
1366 ch = tolower(text[2]);
1367 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1368 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1369 else tempcolorindex = 0;
1372 ch = tolower(text[3]);
1373 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1374 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1375 else tempcolorindex = 0;
1378 colorindex = tempcolorindex | 0xf;
1379 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1387 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1396 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1403 map = ft2_oldstyle_map;
1405 if(!least_one || i0) // never skip the first character
1406 if(x + width_of[ch] * dw > maxwidth)
1409 break; // oops, can't draw this
1411 x += width_of[ch] * dw;
1413 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1415 map = FontMap_FindForChar(fontmap, ch);
1418 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1424 mapch = ch - map->start;
1425 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1427 x += map->glyphs[mapch].advance_x * dw;
1436 *outcolor = colorindex;
1441 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)
1443 int shadow, colorindex = STRING_COLOR_DEFAULT;
1445 float x = startx, y, s, t, u, v, thisw;
1446 float *av, *at, *ac;
1449 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1450 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1451 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1452 Uchar ch, mapch, nextch;
1453 Uchar prevch = 0; // used for kerning
1456 //ft2_font_map_t *prevmap = NULL; // the previous map
1457 ft2_font_map_t *map = NULL; // the currently used map
1458 ft2_font_map_t *fontmap = NULL; // the font map for the size
1460 const char *text_start = text;
1462 ft2_font_t *ft2 = fnt->ft2;
1463 qboolean snap = true;
1467 const float *width_of;
1470 tw = R_TextureWidth(fnt->tex);
1471 th = R_TextureHeight(fnt->tex);
1479 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1480 w *= fnt->settings.scale;
1481 h *= fnt->settings.scale;
1486 map_index = Font_IndexForSize(ft2, h, &w, &h);
1488 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1489 fontmap = Font_MapForIndex(ft2, map_index);
1495 // draw the font at its baseline when using freetype
1497 ftbase_y = dh * (4.5/6.0);
1502 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1503 if(!r_draw2d.integer && !r_draw2d_force)
1504 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1506 R_Mesh_ResetTextureState();
1508 R_Mesh_TexBind(0, fnt->tex);
1509 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1516 //ftbase_x = snap_to_pixel_x(ftbase_x);
1519 startx = snap_to_pixel_x(startx, 0.4);
1520 starty = snap_to_pixel_y(starty, 0.4);
1521 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1524 pix_x = vid.width / vid_conwidth.value;
1525 pix_y = vid.height / vid_conheight.value;
1528 width_of = fontmap->width_of;
1530 width_of = fnt->width_of;
1532 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1537 if (!outcolor || *outcolor == -1)
1538 colorindex = STRING_COLOR_DEFAULT;
1540 colorindex = *outcolor;
1542 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1549 x += r_textshadow.value * vid.width / vid_conwidth.value;
1550 y += r_textshadow.value * vid.height / vid_conheight.value;
1553 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1555 nextch = ch = u8_getnchar(text, &text, bytes_left);
1556 i = text - text_start;
1559 if (ch == ' ' && !fontmap)
1561 x += width_of[(int) ' '] * dw;
1564 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1566 ch = *text; // colors are ascii, so no u8_ needed
1567 if (ch <= '9' && ch >= '0') // ^[0-9] found
1569 colorindex = ch - '0';
1570 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1575 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1577 // building colorindex...
1578 ch = tolower(text[1]);
1579 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1580 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1581 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1582 else tempcolorindex = 0;
1585 ch = tolower(text[2]);
1586 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1587 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1588 else tempcolorindex = 0;
1591 ch = tolower(text[3]);
1592 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1593 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1594 else tempcolorindex = 0;
1597 colorindex = tempcolorindex | 0xf;
1598 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1599 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1600 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1608 else if (ch == STRING_COLOR_TAG)
1617 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1618 // this way we don't need to rebind fnt->tex for every old-style character
1619 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1622 x += 1.0/pix_x * r_textshadow.value;
1623 y += 1.0/pix_y * r_textshadow.value;
1625 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1633 if (map != ft2_oldstyle_map)
1637 // switching from freetype to non-freetype rendering
1638 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1639 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1645 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1646 map = ft2_oldstyle_map;
1650 //num = (unsigned char) text[i];
1651 //thisw = fnt->width_of[num];
1652 thisw = fnt->width_of[ch];
1653 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1654 s = (ch & 15)*0.0625f + (0.5f / tw);
1655 t = (ch >> 4)*0.0625f + (0.5f / th);
1656 u = 0.0625f * thisw - (1.0f / tw);
1657 v = 0.0625f - (1.0f / th);
1658 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1659 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1660 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1661 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1662 at[ 0] = s ; at[ 1] = t ;
1663 at[ 2] = s+u ; at[ 3] = t ;
1664 at[ 4] = s+u ; at[ 5] = t+v ;
1665 at[ 6] = s ; at[ 7] = t+v ;
1666 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1667 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1668 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1669 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1674 if (batchcount >= QUADELEMENTS_MAXQUADS)
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 x += width_of[ch] * dw;
1685 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1687 // new charmap - need to render
1690 // we need a different character map, render what we currently have:
1691 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1692 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1699 map = FontMap_FindForChar(fontmap, ch);
1702 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1709 // this shouldn't happen
1714 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1717 mapch = ch - map->start;
1718 thisw = map->glyphs[mapch].advance_x;
1722 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1729 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1730 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1731 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1732 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1733 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1734 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1735 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1736 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1737 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1738 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1739 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1740 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1749 if (batchcount >= QUADELEMENTS_MAXQUADS)
1751 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1752 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1764 x -= 1.0/pix_x * r_textshadow.value;
1765 y -= 1.0/pix_y * r_textshadow.value;
1771 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1772 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1776 *outcolor = colorindex;
1778 // note: this relies on the proper text (not shadow) being drawn last
1782 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)
1784 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1787 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)
1789 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1792 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1794 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1797 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1799 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1804 // no ^xrgb management
1805 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1807 int color, numchars = 0;
1808 char *outputend2c = output2c + maxoutchars - 2;
1809 if (!outcolor || *outcolor == -1)
1810 color = STRING_COLOR_DEFAULT;
1814 maxreadchars = 1<<30;
1815 textend = text + maxreadchars;
1816 while (text != textend && *text)
1818 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1820 if (text[1] == STRING_COLOR_TAG)
1822 else if (text[1] >= '0' && text[1] <= '9')
1824 color = text[1] - '0';
1829 if (output2c >= outputend2c)
1831 *output2c++ = *text++;
1832 *output2c++ = color;
1835 output2c[0] = output2c[1] = 0;
1842 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)
1846 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1847 if(!r_draw2d.integer && !r_draw2d_force)
1850 R_Mesh_ResetTextureState();
1856 height = pic->height;
1857 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1860 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1862 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1863 floats[0] = floats[9] = x;
1864 floats[1] = floats[4] = y;
1865 floats[3] = floats[6] = x + width;
1866 floats[7] = floats[10] = y + height;
1867 floats[12] = s1;floats[13] = t1;
1868 floats[14] = s2;floats[15] = t2;
1869 floats[16] = s4;floats[17] = t4;
1870 floats[18] = s3;floats[19] = t3;
1871 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1872 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1873 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1874 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1876 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1877 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1880 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1884 if(!r_draw2d.integer && !r_draw2d_force)
1886 DrawQ_ProcessDrawFlag(flags, hasalpha);
1888 R_Mesh_ResetTextureState();
1889 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1891 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1892 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1895 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1899 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1900 if(!r_draw2d.integer && !r_draw2d_force)
1904 switch(vid.renderpath)
1906 case RENDERPATH_GL11:
1907 case RENDERPATH_GL13:
1908 case RENDERPATH_GL20:
1909 case RENDERPATH_CGGL:
1911 qglBegin(GL_LINE_LOOP);
1912 for (num = 0;num < mesh->num_vertices;num++)
1914 if (mesh->data_color4f)
1915 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]);
1916 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1921 case RENDERPATH_D3D9:
1922 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1924 case RENDERPATH_D3D10:
1925 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1927 case RENDERPATH_D3D11:
1928 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1933 //[515]: this is old, delete
1934 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1936 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1937 if(!r_draw2d.integer && !r_draw2d_force)
1940 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1942 switch(vid.renderpath)
1944 case RENDERPATH_GL11:
1945 case RENDERPATH_GL13:
1946 case RENDERPATH_GL20:
1947 case RENDERPATH_CGGL:
1950 //qglLineWidth(width);CHECKGLERROR
1952 GL_Color(r,g,b,alpha);
1955 qglVertex2f(x1, y1);
1956 qglVertex2f(x2, y2);
1960 case RENDERPATH_D3D9:
1961 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1963 case RENDERPATH_D3D10:
1964 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1966 case RENDERPATH_D3D11:
1967 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1972 void DrawQ_SetClipArea(float x, float y, float width, float height)
1977 // We have to convert the con coords into real coords
1978 // OGL uses top to bottom
1979 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1980 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1981 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1982 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1983 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1985 GL_ScissorTest(true);
1988 void DrawQ_ResetClipArea(void)
1991 GL_ScissorTest(false);
1994 void DrawQ_Finish(void)
1996 r_refdef.draw2dstage = 0;
1999 void DrawQ_RecalcView(void)
2001 if(r_refdef.draw2dstage)
2002 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2005 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2006 void R_DrawGamma(void)
2009 switch(vid.renderpath)
2011 case RENDERPATH_GL20:
2012 case RENDERPATH_CGGL:
2013 case RENDERPATH_D3D9:
2014 case RENDERPATH_D3D10:
2015 case RENDERPATH_D3D11:
2016 if (vid_usinghwgamma || v_glslgamma.integer)
2019 case RENDERPATH_GL13:
2020 case RENDERPATH_GL11:
2021 if (vid_usinghwgamma)
2025 // all the blends ignore depth
2026 R_Mesh_ResetTextureState();
2027 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2029 GL_DepthRange(0, 1);
2030 GL_PolygonOffset(0, 0);
2031 GL_DepthTest(false);
2032 if (v_color_enable.integer)
2034 c[0] = v_color_white_r.value;
2035 c[1] = v_color_white_g.value;
2036 c[2] = v_color_white_b.value;
2039 c[0] = c[1] = c[2] = v_contrast.value;
2040 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2042 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2043 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2045 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2046 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2047 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2048 VectorScale(c, 0.5, c);
2051 if (v_color_enable.integer)
2053 c[0] = v_color_black_r.value;
2054 c[1] = v_color_black_g.value;
2055 c[2] = v_color_black_b.value;
2058 c[0] = c[1] = c[2] = v_brightness.value;
2059 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2061 GL_BlendFunc(GL_ONE, GL_ONE);
2062 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2063 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2064 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);