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)
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) | TEXF_ALLOWUPDATES, -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));
891 // parse one of sizes
892 sz = atof(Cmd_Argv(i));
893 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
895 f->req_sizes[sizes] = sz;
901 LoadFont(true, mainfont, f, scale, voffset);
909 static void gl_draw_start(void)
912 drawtexturepool = R_AllocTexturePool();
915 memset(cachepichash, 0, sizeof(cachepichash));
919 // load default font textures
920 for(i = 0; i < dp_fonts.maxsize; ++i)
921 if (dp_fonts.f[i].title[0])
922 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
924 // draw the loading screen so people have something to see in the newly opened window
925 SCR_UpdateLoadingScreen(true);
928 static void gl_draw_shutdown(void)
932 R_FreeTexturePool(&drawtexturepool);
935 memset(cachepichash, 0, sizeof(cachepichash));
938 static void gl_draw_newmap(void)
943 void GL_Draw_Init (void)
947 Cvar_RegisterVariable(&r_font_postprocess_blur);
948 Cvar_RegisterVariable(&r_font_postprocess_outline);
949 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
950 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
951 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
952 Cvar_RegisterVariable(&r_font_hinting);
953 Cvar_RegisterVariable(&r_font_antialias);
954 Cvar_RegisterVariable(&r_textshadow);
955 Cvar_RegisterVariable(&r_textbrightness);
956 Cvar_RegisterVariable(&r_textcontrast);
958 // allocate fonts storage
959 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
960 dp_fonts.maxsize = MAX_FONTS;
961 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
962 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
964 // assign starting font names
965 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
966 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
967 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
968 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
969 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
970 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
971 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
972 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
973 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
974 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
975 if(!FONT_USER(i)->title[0])
976 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
978 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
979 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
982 static void _DrawQ_Setup(void)
984 r_viewport_t viewport;
985 if (r_refdef.draw2dstage)
987 r_refdef.draw2dstage = true;
989 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);
990 R_SetViewport(&viewport);
991 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
992 GL_DepthFunc(GL_LEQUAL);
993 GL_PolygonOffset(0,0);
994 GL_CullFace(GL_NONE);
995 R_EntityMatrix(&identitymatrix);
998 GL_PolygonOffset(0, 0);
1001 GL_AlphaTest(false);
1004 qboolean r_draw2d_force = false;
1005 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1009 if(!r_draw2d.integer && !r_draw2d_force)
1011 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1013 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1015 if(flags == DRAWFLAG_ADDITIVE)
1017 GL_DepthMask(false);
1018 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1020 else if(flags == DRAWFLAG_MODULATE)
1022 GL_DepthMask(false);
1023 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1025 else if(flags == DRAWFLAG_2XMODULATE)
1027 GL_DepthMask(false);
1028 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1030 else if(flags == DRAWFLAG_SCREEN)
1032 GL_DepthMask(false);
1033 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1037 GL_DepthMask(false);
1038 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1043 GL_BlendFunc(GL_ONE, GL_ZERO);
1047 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1051 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1052 if(!r_draw2d.integer && !r_draw2d_force)
1055 R_Mesh_ResetTextureState();
1056 floats[12] = 0.0f;floats[13] = 0.0f;
1057 floats[14] = 1.0f;floats[15] = 0.0f;
1058 floats[16] = 1.0f;floats[17] = 1.0f;
1059 floats[18] = 0.0f;floats[19] = 1.0f;
1060 floats[20] = floats[24] = floats[28] = floats[32] = red;
1061 floats[21] = floats[25] = floats[29] = floats[33] = green;
1062 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1063 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1069 height = pic->height;
1070 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1073 // AK07: lets be texel correct on the corners
1075 float horz_offset = 0.5f / pic->width;
1076 float vert_offset = 0.5f / pic->height;
1078 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1079 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1080 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1081 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1086 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1088 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1089 floats[0] = floats[9] = x;
1090 floats[1] = floats[4] = y;
1091 floats[3] = floats[6] = x + width;
1092 floats[7] = floats[10] = y + height;
1094 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1095 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1098 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)
1101 float af = DEG2RAD(-angle); // forward
1102 float ar = DEG2RAD(-angle + 90); // right
1103 float sinaf = sin(af);
1104 float cosaf = cos(af);
1105 float sinar = sin(ar);
1106 float cosar = cos(ar);
1108 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1109 if(!r_draw2d.integer && !r_draw2d_force)
1112 R_Mesh_ResetTextureState();
1118 height = pic->height;
1119 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1122 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1124 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1127 floats[0] = x - cosaf*org_x - cosar*org_y;
1128 floats[1] = y - sinaf*org_x - sinar*org_y;
1131 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1132 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1135 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1136 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1139 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1140 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1142 floats[12] = 0.0f;floats[13] = 0.0f;
1143 floats[14] = 1.0f;floats[15] = 0.0f;
1144 floats[16] = 1.0f;floats[17] = 1.0f;
1145 floats[18] = 0.0f;floats[19] = 1.0f;
1146 floats[20] = floats[24] = floats[28] = floats[32] = red;
1147 floats[21] = floats[25] = floats[29] = floats[33] = green;
1148 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1149 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1151 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1152 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1155 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1159 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1160 if(!r_draw2d.integer && !r_draw2d_force)
1163 R_Mesh_ResetTextureState();
1164 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1166 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1167 floats[0] = floats[9] = x;
1168 floats[1] = floats[4] = y;
1169 floats[3] = floats[6] = x + width;
1170 floats[7] = floats[10] = y + height;
1171 floats[12] = 0.0f;floats[13] = 0.0f;
1172 floats[14] = 1.0f;floats[15] = 0.0f;
1173 floats[16] = 1.0f;floats[17] = 1.0f;
1174 floats[18] = 0.0f;floats[19] = 1.0f;
1175 floats[20] = floats[24] = floats[28] = floats[32] = red;
1176 floats[21] = floats[25] = floats[29] = floats[33] = green;
1177 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1178 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1180 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1181 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1184 /// color tag printing
1185 static const vec4_t string_colors[] =
1188 // LordHavoc: why on earth is cyan before magenta in Quake3?
1189 // LordHavoc: note: Doom3 uses white for [0] and [7]
1190 {0.0, 0.0, 0.0, 1.0}, // black
1191 {1.0, 0.0, 0.0, 1.0}, // red
1192 {0.0, 1.0, 0.0, 1.0}, // green
1193 {1.0, 1.0, 0.0, 1.0}, // yellow
1194 {0.0, 0.0, 1.0, 1.0}, // blue
1195 {0.0, 1.0, 1.0, 1.0}, // cyan
1196 {1.0, 0.0, 1.0, 1.0}, // magenta
1197 {1.0, 1.0, 1.0, 1.0}, // white
1198 // [515]'s BX_COLOREDTEXT extension
1199 {1.0, 1.0, 1.0, 0.5}, // half transparent
1200 {0.5, 0.5, 0.5, 1.0} // half brightness
1201 // Black's color table
1202 //{1.0, 1.0, 1.0, 1.0},
1203 //{1.0, 0.0, 0.0, 1.0},
1204 //{0.0, 1.0, 0.0, 1.0},
1205 //{0.0, 0.0, 1.0, 1.0},
1206 //{1.0, 1.0, 0.0, 1.0},
1207 //{0.0, 1.0, 1.0, 1.0},
1208 //{1.0, 0.0, 1.0, 1.0},
1209 //{0.1, 0.1, 0.1, 1.0}
1212 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1214 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1216 float C = r_textcontrast.value;
1217 float B = r_textbrightness.value;
1218 if (colorindex & 0x10000) // that bit means RGB color
1220 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1221 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1222 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1223 color[3] = (colorindex & 0xf) / 15.0;
1226 Vector4Copy(string_colors[colorindex], color);
1227 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1230 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1231 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1235 // NOTE: this function always draws exactly one character if maxwidth <= 0
1236 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)
1238 const char *text_start = text;
1239 int colorindex = STRING_COLOR_DEFAULT;
1242 Uchar ch, mapch, nextch;
1243 Uchar prevch = 0; // used for kerning
1248 ft2_font_map_t *fontmap = NULL;
1249 ft2_font_map_t *map = NULL;
1250 //ft2_font_map_t *prevmap = NULL;
1251 ft2_font_t *ft2 = fnt->ft2;
1253 qboolean snap = true;
1254 qboolean least_one = false;
1255 float dw; // display w
1256 //float dh; // display h
1257 const float *width_of;
1264 // do this in the end
1265 w *= fnt->settings.scale;
1266 h *= fnt->settings.scale;
1268 // find the most fitting size:
1272 map_index = Font_IndexForSize(ft2, h, &w, &h);
1274 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1275 fontmap = Font_MapForIndex(ft2, map_index);
1284 if (!outcolor || *outcolor == -1)
1285 colorindex = STRING_COLOR_DEFAULT;
1287 colorindex = *outcolor;
1289 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1290 // ftbase_x = snap_to_pixel_x(0);
1295 maxwidth = -maxwidth;
1299 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1302 width_of = fontmap->width_of;
1304 width_of = fnt->width_of;
1306 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1309 nextch = ch = u8_getnchar(text, &text, bytes_left);
1310 i = text - text_start;
1313 if (ch == ' ' && !fontmap)
1315 if(!least_one || i0) // never skip the first character
1316 if(x + width_of[(int) ' '] * dw > maxwidth)
1319 break; // oops, can't draw this
1321 x += width_of[(int) ' '] * dw;
1324 // i points to the char after ^
1325 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1327 ch = *text; // colors are ascii, so no u8_ needed
1328 if (ch <= '9' && ch >= '0') // ^[0-9] found
1330 colorindex = ch - '0';
1335 // i points to the char after ^...
1336 // i+3 points to 3 in ^x123
1337 // i+3 == *maxlen would mean that char is missing
1338 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1340 // building colorindex...
1341 ch = tolower(text[1]);
1342 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1343 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1344 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1345 else tempcolorindex = 0;
1348 ch = tolower(text[2]);
1349 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1350 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1351 else tempcolorindex = 0;
1354 ch = tolower(text[3]);
1355 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1356 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1357 else tempcolorindex = 0;
1360 colorindex = tempcolorindex | 0xf;
1361 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1369 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1378 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1385 map = ft2_oldstyle_map;
1387 if(!least_one || i0) // never skip the first character
1388 if(x + width_of[ch] * dw > maxwidth)
1391 break; // oops, can't draw this
1393 x += width_of[ch] * dw;
1395 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1397 map = FontMap_FindForChar(fontmap, ch);
1400 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1406 mapch = ch - map->start;
1407 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1409 x += map->glyphs[mapch].advance_x * dw;
1418 *outcolor = colorindex;
1423 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)
1425 int shadow, colorindex = STRING_COLOR_DEFAULT;
1427 float x = startx, y, s, t, u, v, thisw;
1428 float *av, *at, *ac;
1431 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1432 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1433 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1434 Uchar ch, mapch, nextch;
1435 Uchar prevch = 0; // used for kerning
1438 //ft2_font_map_t *prevmap = NULL; // the previous map
1439 ft2_font_map_t *map = NULL; // the currently used map
1440 ft2_font_map_t *fontmap = NULL; // the font map for the size
1442 const char *text_start = text;
1444 ft2_font_t *ft2 = fnt->ft2;
1445 qboolean snap = true;
1449 const float *width_of;
1452 tw = R_TextureWidth(fnt->tex);
1453 th = R_TextureHeight(fnt->tex);
1461 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1462 w *= fnt->settings.scale;
1463 h *= fnt->settings.scale;
1468 map_index = Font_IndexForSize(ft2, h, &w, &h);
1470 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1471 fontmap = Font_MapForIndex(ft2, map_index);
1477 // draw the font at its baseline when using freetype
1479 ftbase_y = dh * (4.5/6.0);
1484 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1485 if(!r_draw2d.integer && !r_draw2d_force)
1486 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1488 R_Mesh_ResetTextureState();
1490 R_Mesh_TexBind(0, fnt->tex);
1491 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1498 //ftbase_x = snap_to_pixel_x(ftbase_x);
1501 startx = snap_to_pixel_x(startx, 0.4);
1502 starty = snap_to_pixel_y(starty, 0.4);
1503 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1506 pix_x = vid.width / vid_conwidth.value;
1507 pix_y = vid.height / vid_conheight.value;
1510 width_of = fontmap->width_of;
1512 width_of = fnt->width_of;
1514 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1519 if (!outcolor || *outcolor == -1)
1520 colorindex = STRING_COLOR_DEFAULT;
1522 colorindex = *outcolor;
1524 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1531 x += r_textshadow.value * vid.width / vid_conwidth.value;
1532 y += r_textshadow.value * vid.height / vid_conheight.value;
1535 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1537 nextch = ch = u8_getnchar(text, &text, bytes_left);
1538 i = text - text_start;
1541 if (ch == ' ' && !fontmap)
1543 x += width_of[(int) ' '] * dw;
1546 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1548 ch = *text; // colors are ascii, so no u8_ needed
1549 if (ch <= '9' && ch >= '0') // ^[0-9] found
1551 colorindex = ch - '0';
1552 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1557 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1559 // building colorindex...
1560 ch = tolower(text[1]);
1561 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1562 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1563 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1564 else tempcolorindex = 0;
1567 ch = tolower(text[2]);
1568 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1569 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1570 else tempcolorindex = 0;
1573 ch = tolower(text[3]);
1574 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1575 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1576 else tempcolorindex = 0;
1579 colorindex = tempcolorindex | 0xf;
1580 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1581 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1582 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1590 else if (ch == STRING_COLOR_TAG)
1599 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1600 // this way we don't need to rebind fnt->tex for every old-style character
1601 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1604 x += 1.0/pix_x * r_textshadow.value;
1605 y += 1.0/pix_y * r_textshadow.value;
1607 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1615 if (map != ft2_oldstyle_map)
1619 // switching from freetype to non-freetype rendering
1620 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1621 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1627 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1628 map = ft2_oldstyle_map;
1632 //num = (unsigned char) text[i];
1633 //thisw = fnt->width_of[num];
1634 thisw = fnt->width_of[ch];
1635 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1636 s = (ch & 15)*0.0625f + (0.5f / tw);
1637 t = (ch >> 4)*0.0625f + (0.5f / th);
1638 u = 0.0625f * thisw - (1.0f / tw);
1639 v = 0.0625f - (1.0f / th);
1640 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1641 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1642 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1643 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1644 at[ 0] = s ; at[ 1] = t ;
1645 at[ 2] = s+u ; at[ 3] = t ;
1646 at[ 4] = s+u ; at[ 5] = t+v ;
1647 at[ 6] = s ; at[ 7] = t+v ;
1648 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1649 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1650 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1651 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1656 if (batchcount >= QUADELEMENTS_MAXQUADS)
1658 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1659 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1665 x += width_of[ch] * dw;
1667 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1669 // new charmap - need to render
1672 // we need a different character map, render what we currently have:
1673 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1674 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1681 map = FontMap_FindForChar(fontmap, ch);
1684 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1691 // this shouldn't happen
1696 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1699 mapch = ch - map->start;
1700 thisw = map->glyphs[mapch].advance_x;
1704 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1711 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1712 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1713 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1714 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1715 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1716 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1717 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1718 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1719 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1720 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1721 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1722 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1731 if (batchcount >= QUADELEMENTS_MAXQUADS)
1733 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1734 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1746 x -= 1.0/pix_x * r_textshadow.value;
1747 y -= 1.0/pix_y * r_textshadow.value;
1753 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1754 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1758 *outcolor = colorindex;
1760 // note: this relies on the proper text (not shadow) being drawn last
1764 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)
1766 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1769 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)
1771 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1774 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1776 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1779 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1781 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1786 // no ^xrgb management
1787 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1789 int color, numchars = 0;
1790 char *outputend2c = output2c + maxoutchars - 2;
1791 if (!outcolor || *outcolor == -1)
1792 color = STRING_COLOR_DEFAULT;
1796 maxreadchars = 1<<30;
1797 textend = text + maxreadchars;
1798 while (text != textend && *text)
1800 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1802 if (text[1] == STRING_COLOR_TAG)
1804 else if (text[1] >= '0' && text[1] <= '9')
1806 color = text[1] - '0';
1811 if (output2c >= outputend2c)
1813 *output2c++ = *text++;
1814 *output2c++ = color;
1817 output2c[0] = output2c[1] = 0;
1824 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)
1828 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1829 if(!r_draw2d.integer && !r_draw2d_force)
1832 R_Mesh_ResetTextureState();
1838 height = pic->height;
1839 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1842 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1844 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1845 floats[0] = floats[9] = x;
1846 floats[1] = floats[4] = y;
1847 floats[3] = floats[6] = x + width;
1848 floats[7] = floats[10] = y + height;
1849 floats[12] = s1;floats[13] = t1;
1850 floats[14] = s2;floats[15] = t2;
1851 floats[16] = s4;floats[17] = t4;
1852 floats[18] = s3;floats[19] = t3;
1853 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1854 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1855 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1856 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1858 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1859 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1862 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1866 if(!r_draw2d.integer && !r_draw2d_force)
1868 DrawQ_ProcessDrawFlag(flags, hasalpha);
1870 R_Mesh_ResetTextureState();
1871 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1873 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1874 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1877 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1881 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1882 if(!r_draw2d.integer && !r_draw2d_force)
1886 switch(vid.renderpath)
1888 case RENDERPATH_GL11:
1889 case RENDERPATH_GL13:
1890 case RENDERPATH_GL20:
1891 case RENDERPATH_CGGL:
1893 qglBegin(GL_LINE_LOOP);
1894 for (num = 0;num < mesh->num_vertices;num++)
1896 if (mesh->data_color4f)
1897 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]);
1898 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1903 case RENDERPATH_D3D9:
1904 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1906 case RENDERPATH_D3D10:
1907 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1909 case RENDERPATH_D3D11:
1910 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1915 //[515]: this is old, delete
1916 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1918 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1919 if(!r_draw2d.integer && !r_draw2d_force)
1922 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1924 switch(vid.renderpath)
1926 case RENDERPATH_GL11:
1927 case RENDERPATH_GL13:
1928 case RENDERPATH_GL20:
1929 case RENDERPATH_CGGL:
1932 //qglLineWidth(width);CHECKGLERROR
1934 GL_Color(r,g,b,alpha);
1937 qglVertex2f(x1, y1);
1938 qglVertex2f(x2, y2);
1942 case RENDERPATH_D3D9:
1943 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1945 case RENDERPATH_D3D10:
1946 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1948 case RENDERPATH_D3D11:
1949 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1954 void DrawQ_SetClipArea(float x, float y, float width, float height)
1959 // We have to convert the con coords into real coords
1960 // OGL uses top to bottom
1961 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1962 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1963 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1964 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1965 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1967 GL_ScissorTest(true);
1970 void DrawQ_ResetClipArea(void)
1973 GL_ScissorTest(false);
1976 void DrawQ_Finish(void)
1978 r_refdef.draw2dstage = false;
1981 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1982 void R_DrawGamma(void)
1985 switch(vid.renderpath)
1987 case RENDERPATH_GL20:
1988 case RENDERPATH_CGGL:
1989 case RENDERPATH_D3D9:
1990 case RENDERPATH_D3D10:
1991 case RENDERPATH_D3D11:
1992 if (vid_usinghwgamma || v_glslgamma.integer)
1995 case RENDERPATH_GL13:
1996 case RENDERPATH_GL11:
1997 if (vid_usinghwgamma)
2001 // all the blends ignore depth
2002 R_Mesh_ResetTextureState();
2003 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2005 GL_DepthRange(0, 1);
2006 GL_PolygonOffset(0, 0);
2007 GL_DepthTest(false);
2008 if (v_color_enable.integer)
2010 c[0] = v_color_white_r.value;
2011 c[1] = v_color_white_g.value;
2012 c[2] = v_color_white_b.value;
2015 c[0] = c[1] = c[2] = v_contrast.value;
2016 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2018 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2019 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2021 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2022 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2023 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2024 VectorScale(c, 0.5, c);
2027 if (v_color_enable.integer)
2029 c[0] = v_color_black_r.value;
2030 c[1] = v_color_black_g.value;
2031 c[2] = v_color_black_b.value;
2034 c[0] = c[1] = c[2] = v_brightness.value;
2035 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2037 GL_BlendFunc(GL_ONE, GL_ONE);
2038 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2039 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2040 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);