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];
321 // check whether the picture has already been cached
322 crc = CRC_Block((unsigned char *)path, strlen(path));
323 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
324 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
325 if (!strcmp (path, pic->name))
328 if (numcachepics == MAX_CACHED_PICS)
330 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
331 // FIXME: support NULL in callers?
332 return cachepics; // return the first one
334 pic = cachepics + (numcachepics++);
335 strlcpy (pic->name, path, sizeof(pic->name));
337 pic->chain = cachepichash[hashkey];
338 cachepichash[hashkey] = pic;
340 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
341 pic->tex = CL_GetDynTexture( path );
342 // if so, set the width/height, too
344 pic->width = R_TextureWidth(pic->tex);
345 pic->height = R_TextureHeight(pic->tex);
346 // we're done now (early-out)
350 pic->texflags = TEXF_ALPHA;
351 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
352 pic->texflags |= TEXF_CLAMP;
353 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
354 pic->texflags |= TEXF_COMPRESS;
356 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
358 // load a high quality image from disk if possible
359 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer, NULL);
360 if (pixels == NULL && !strncmp(path, "gfx/", 4))
361 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer, NULL);
364 pic->width = image_width;
365 pic->height = image_height;
367 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, -1, NULL);
371 pic->autoload = false;
372 // never compress the fallback images
373 pic->texflags &= ~TEXF_COMPRESS;
376 // now read the low quality version (wad or lmp file), and take the pic
377 // size from that even if we don't upload the texture, this way the pics
378 // show up the right size in the menu even if they were replaced with
379 // higher or lower resolution versions
380 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
381 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
383 if (developer_loading.integer)
384 Con_Printf("loading lump \"%s\"\n", path);
388 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
389 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
390 // if no high quality replacement image was found, upload the original low quality texture
392 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
396 else if ((lmpdata = W_GetLumpName (path + 4)))
398 if (developer_loading.integer)
399 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
401 if (!strcmp(path, "gfx/conchars"))
403 // conchars is a raw image and with color 0 as transparent instead of 255
406 // if no high quality replacement image was found, upload the original low quality texture
408 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
412 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
413 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
414 // if no high quality replacement image was found, upload the original low quality texture
416 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
425 else if (pic->tex == NULL)
427 // if it's not found on disk, generate an image
428 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
429 pic->width = R_TextureWidth(pic->tex);
430 pic->height = R_TextureHeight(pic->tex);
436 cachepic_t *Draw_CachePic (const char *path)
438 return Draw_CachePic_Flags (path, 0);
443 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
445 if (pic->autoload && !pic->tex)
447 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
448 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
449 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
450 if (pic->tex == NULL)
451 pic->tex = draw_generatepic(pic->name, true);
453 pic->lastusedframe = draw_frame;
457 void Draw_Frame(void)
461 static double nextpurgetime;
462 if (nextpurgetime > realtime)
464 nextpurgetime = realtime + 0.05;
465 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
467 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
469 R_FreeTexture(pic->tex);
476 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
481 crc = CRC_Block((unsigned char *)picname, strlen(picname));
482 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
483 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
484 if (!strcmp (picname, pic->name))
489 if (pic->tex && pic->width == width && pic->height == height)
491 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
499 if (numcachepics == MAX_CACHED_PICS)
501 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
502 // FIXME: support NULL in callers?
503 return cachepics; // return the first one
505 pic = cachepics + (numcachepics++);
506 strlcpy (pic->name, picname, sizeof(pic->name));
508 pic->chain = cachepichash[hashkey];
509 cachepichash[hashkey] = pic;
514 pic->height = height;
516 R_FreeTexture(pic->tex);
517 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, -1, NULL);
521 void Draw_FreePic(const char *picname)
526 // this doesn't really free the pic, but does free it's texture
527 crc = CRC_Block((unsigned char *)picname, strlen(picname));
528 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
529 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
531 if (!strcmp (picname, pic->name) && pic->tex)
533 R_FreeTexture(pic->tex);
542 static float snap_to_pixel_x(float x, float roundUpAt);
543 extern int con_linewidth; // to force rewrapping
544 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
548 char widthfile[MAX_QPATH];
550 fs_offset_t widthbufsize;
552 if(override || !fnt->texpath[0])
554 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
555 // load the cvars when the font is FIRST loader
556 fnt->settings.scale = scale;
557 fnt->settings.voffset = voffset;
558 fnt->settings.antialias = r_font_antialias.integer;
559 fnt->settings.hinting = r_font_hinting.integer;
560 fnt->settings.outline = r_font_postprocess_outline.value;
561 fnt->settings.blur = r_font_postprocess_blur.value;
562 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
563 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
564 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
567 if (fnt->settings.scale <= 0)
568 fnt->settings.scale = 1;
570 if(drawtexturepool == NULL)
571 return; // before gl_draw_start, so will be loaded later
575 // clear freetype font
576 Font_UnloadFont(fnt->ft2);
581 if(fnt->req_face != -1)
583 if(!Font_LoadFont(fnt->texpath, fnt))
584 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
587 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
588 if(fnt->tex == r_texture_notexture)
590 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
592 if (!fnt->fallbacks[i][0])
594 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
595 if(fnt->tex != r_texture_notexture)
598 if(fnt->tex == r_texture_notexture)
600 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
601 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
604 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
607 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
609 // unspecified width == 1 (base width)
610 for(i = 1; i < 256; ++i)
611 fnt->width_of[i] = 1;
613 // FIXME load "name.width", if it fails, fill all with 1
614 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
616 float extraspacing = 0;
617 const char *p = widthbuf;
622 if(!COM_ParseToken_Simple(&p, false, false))
640 fnt->width_of[ch] = atof(com_token) + extraspacing;
643 for (i = 0; i < MAX_FONT_SIZES; ++i)
645 //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
646 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
649 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
655 if(!strcmp(com_token, "extraspacing"))
657 if(!COM_ParseToken_Simple(&p, false, false))
659 extraspacing = atof(com_token);
661 else if(!strcmp(com_token, "scale"))
663 if(!COM_ParseToken_Simple(&p, false, false))
665 fnt->settings.scale = atof(com_token);
669 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
670 if(!COM_ParseToken_Simple(&p, false, false))
680 maxwidth = fnt->width_of[1];
681 for(i = 2; i < 256; ++i)
682 maxwidth = max(maxwidth, fnt->width_of[i]);
683 fnt->maxwidth = maxwidth;
685 // fix up maxwidth for overlap
686 fnt->maxwidth *= fnt->settings.scale;
688 if(fnt == FONT_CONSOLE)
689 con_linewidth = -1; // rewrap console in next frame
692 extern cvar_t developer_font;
693 dp_font_t *FindFont(const char *title, qboolean allocate_new)
698 for(i = 0; i < dp_fonts.maxsize; ++i)
699 if(!strcmp(dp_fonts.f[i].title, title))
700 return &dp_fonts.f[i];
701 // if not found - try allocate
704 // find any font with empty title
705 for(i = 0; i < dp_fonts.maxsize; ++i)
707 if(!strcmp(dp_fonts.f[i].title, ""))
709 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
710 return &dp_fonts.f[i];
713 // if no any 'free' fonts - expand buffer
714 i = dp_fonts.maxsize;
715 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
716 if (developer_font.integer)
717 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
718 dp_fonts.f = Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
719 // register a font in first expanded slot
720 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
721 return &dp_fonts.f[i];
726 static float snap_to_pixel_x(float x, float roundUpAt)
728 float pixelpos = x * vid.width / vid_conwidth.value;
729 int snap = (int) pixelpos;
730 if (pixelpos - snap >= roundUpAt) ++snap;
731 return ((float)snap * vid_conwidth.value / vid.width);
733 x = (int)(x * vid.width / vid_conwidth.value);
734 x = (x * vid_conwidth.value / vid.width);
739 static float snap_to_pixel_y(float y, float roundUpAt)
741 float pixelpos = y * vid.height / vid_conheight.value;
742 int snap = (int) pixelpos;
743 if (pixelpos - snap > roundUpAt) ++snap;
744 return ((float)snap * vid_conheight.value / vid.height);
746 y = (int)(y * vid.height / vid_conheight.value);
747 y = (y * vid_conheight.value / vid.height);
752 static void LoadFont_f(void)
756 const char *filelist, *c, *cm;
757 float sz, scale, voffset;
758 char mainfont[MAX_QPATH];
762 Con_Printf("Available font commands:\n");
763 for(i = 0; i < dp_fonts.maxsize; ++i)
764 if (dp_fonts.f[i].title[0])
765 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
766 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
767 "can specify multiple fonts and faces\n"
768 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
769 "to load face 2 of the font gfx/vera-sans and use face 1\n"
770 "of gfx/fallback as fallback font.\n"
771 "You can also specify a list of font sizes to load, like this:\n"
772 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
773 "In many cases, 8 12 16 24 32 should be a good choice.\n"
775 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
776 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
780 f = FindFont(Cmd_Argv(1), true);
783 Con_Printf("font function not found\n");
788 filelist = "gfx/conchars";
790 filelist = Cmd_Argv(2);
792 memset(f->fallbacks, 0, sizeof(f->fallbacks));
793 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
795 // first font is handled "normally"
796 c = strchr(filelist, ':');
797 cm = strchr(filelist, ',');
798 if(c && (!cm || c < cm))
799 f->req_face = atoi(c+1);
806 if(!c || (c - filelist) > MAX_QPATH)
807 strlcpy(mainfont, filelist, sizeof(mainfont));
810 memcpy(mainfont, filelist, c - filelist);
811 mainfont[c - filelist] = 0;
814 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
816 c = strchr(filelist, ',');
822 c = strchr(filelist, ':');
823 cm = strchr(filelist, ',');
824 if(c && (!cm || c < cm))
825 f->fallback_faces[i] = atoi(c+1);
828 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
831 if(!c || (c-filelist) > MAX_QPATH)
833 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
837 memcpy(f->fallbacks[i], filelist, c - filelist);
838 f->fallbacks[i][c - filelist] = 0;
842 // for now: by default load only one size: the default size
844 for(i = 1; i < MAX_FONT_SIZES; ++i)
845 f->req_sizes[i] = -1;
851 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
854 if (!strcmp(Cmd_Argv(i), "scale"))
858 scale = atof(Cmd_Argv(i));
861 if (!strcmp(Cmd_Argv(i), "voffset"))
865 voffset = atof(Cmd_Argv(i));
868 // parse one of sizes
869 sz = atof(Cmd_Argv(i));
870 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
872 f->req_sizes[sizes] = sz;
878 LoadFont(true, mainfont, f, scale, voffset);
886 static void gl_draw_start(void)
889 drawtexturepool = R_AllocTexturePool();
892 memset(cachepichash, 0, sizeof(cachepichash));
896 // load default font textures
897 for(i = 0; i < dp_fonts.maxsize; ++i)
898 if (dp_fonts.f[i].title[0])
899 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
901 // draw the loading screen so people have something to see in the newly opened window
902 SCR_UpdateLoadingScreen(true);
905 static void gl_draw_shutdown(void)
909 R_FreeTexturePool(&drawtexturepool);
912 memset(cachepichash, 0, sizeof(cachepichash));
915 static void gl_draw_newmap(void)
920 void GL_Draw_Init (void)
924 Cvar_RegisterVariable(&r_font_postprocess_blur);
925 Cvar_RegisterVariable(&r_font_postprocess_outline);
926 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
927 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
928 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
929 Cvar_RegisterVariable(&r_font_hinting);
930 Cvar_RegisterVariable(&r_font_antialias);
931 Cvar_RegisterVariable(&r_textshadow);
932 Cvar_RegisterVariable(&r_textbrightness);
933 Cvar_RegisterVariable(&r_textcontrast);
935 // allocate fonts storage
936 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
937 dp_fonts.maxsize = MAX_FONTS;
938 dp_fonts.f = Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
939 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
941 // assign starting font names
942 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
943 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
944 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
945 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
946 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
947 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
948 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
949 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
950 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
951 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
952 if(!FONT_USER(i)->title[0])
953 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
955 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
956 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
959 void _DrawQ_Setup(void)
961 r_viewport_t viewport;
962 if (r_refdef.draw2dstage)
964 r_refdef.draw2dstage = true;
966 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);
967 R_SetViewport(&viewport);
968 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
969 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
970 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
971 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
972 R_EntityMatrix(&identitymatrix);
976 GL_PolygonOffset(0, 0);
980 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
983 static void _DrawQ_ProcessDrawFlag(int flags)
987 if(flags == DRAWFLAG_ADDITIVE)
988 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
989 else if(flags == DRAWFLAG_MODULATE)
990 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
991 else if(flags == DRAWFLAG_2XMODULATE)
992 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
993 else if(flags == DRAWFLAG_SCREEN)
994 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
996 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
999 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1003 _DrawQ_ProcessDrawFlag(flags);
1005 R_Mesh_ResetTextureState();
1006 floats[12] = 0.0f;floats[13] = 0.0f;
1007 floats[14] = 1.0f;floats[15] = 0.0f;
1008 floats[16] = 1.0f;floats[17] = 1.0f;
1009 floats[18] = 0.0f;floats[19] = 1.0f;
1010 floats[20] = floats[24] = floats[28] = floats[32] = red;
1011 floats[21] = floats[25] = floats[29] = floats[33] = green;
1012 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1013 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1019 height = pic->height;
1020 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1023 // AK07: lets be texel correct on the corners
1025 float horz_offset = 0.5f / pic->width;
1026 float vert_offset = 0.5f / pic->height;
1028 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1029 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1030 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1031 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1036 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1038 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1039 floats[0] = floats[9] = x;
1040 floats[1] = floats[4] = y;
1041 floats[3] = floats[6] = x + width;
1042 floats[7] = floats[10] = y + height;
1044 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1045 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1048 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)
1051 float af = DEG2RAD(-angle); // forward
1052 float ar = DEG2RAD(-angle + 90); // right
1053 float sinaf = sin(af);
1054 float cosaf = cos(af);
1055 float sinar = sin(ar);
1056 float cosar = cos(ar);
1058 _DrawQ_ProcessDrawFlag(flags);
1060 R_Mesh_ResetTextureState();
1066 height = pic->height;
1067 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1070 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1072 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1075 floats[0] = x - cosaf*org_x - cosar*org_y;
1076 floats[1] = y - sinaf*org_x - sinar*org_y;
1079 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1080 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1083 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1084 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1087 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1088 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1090 floats[12] = 0.0f;floats[13] = 0.0f;
1091 floats[14] = 1.0f;floats[15] = 0.0f;
1092 floats[16] = 1.0f;floats[17] = 1.0f;
1093 floats[18] = 0.0f;floats[19] = 1.0f;
1094 floats[20] = floats[24] = floats[28] = floats[32] = red;
1095 floats[21] = floats[25] = floats[29] = floats[33] = green;
1096 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1097 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1099 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1100 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1103 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1107 _DrawQ_ProcessDrawFlag(flags);
1109 R_Mesh_ResetTextureState();
1110 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1112 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1113 floats[0] = floats[9] = x;
1114 floats[1] = floats[4] = y;
1115 floats[3] = floats[6] = x + width;
1116 floats[7] = floats[10] = y + height;
1117 floats[12] = 0.0f;floats[13] = 0.0f;
1118 floats[14] = 1.0f;floats[15] = 0.0f;
1119 floats[16] = 1.0f;floats[17] = 1.0f;
1120 floats[18] = 0.0f;floats[19] = 1.0f;
1121 floats[20] = floats[24] = floats[28] = floats[32] = red;
1122 floats[21] = floats[25] = floats[29] = floats[33] = green;
1123 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1124 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1126 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1127 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1130 /// color tag printing
1131 static const vec4_t string_colors[] =
1134 // LordHavoc: why on earth is cyan before magenta in Quake3?
1135 // LordHavoc: note: Doom3 uses white for [0] and [7]
1136 {0.0, 0.0, 0.0, 1.0}, // black
1137 {1.0, 0.0, 0.0, 1.0}, // red
1138 {0.0, 1.0, 0.0, 1.0}, // green
1139 {1.0, 1.0, 0.0, 1.0}, // yellow
1140 {0.0, 0.0, 1.0, 1.0}, // blue
1141 {0.0, 1.0, 1.0, 1.0}, // cyan
1142 {1.0, 0.0, 1.0, 1.0}, // magenta
1143 {1.0, 1.0, 1.0, 1.0}, // white
1144 // [515]'s BX_COLOREDTEXT extension
1145 {1.0, 1.0, 1.0, 0.5}, // half transparent
1146 {0.5, 0.5, 0.5, 1.0} // half brightness
1147 // Black's color table
1148 //{1.0, 1.0, 1.0, 1.0},
1149 //{1.0, 0.0, 0.0, 1.0},
1150 //{0.0, 1.0, 0.0, 1.0},
1151 //{0.0, 0.0, 1.0, 1.0},
1152 //{1.0, 1.0, 0.0, 1.0},
1153 //{0.0, 1.0, 1.0, 1.0},
1154 //{1.0, 0.0, 1.0, 1.0},
1155 //{0.1, 0.1, 0.1, 1.0}
1158 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1160 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1162 float C = r_textcontrast.value;
1163 float B = r_textbrightness.value;
1164 if (colorindex & 0x10000) // that bit means RGB color
1166 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1167 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1168 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1169 color[3] = (colorindex & 0xf) / 15.0;
1172 Vector4Copy(string_colors[colorindex], color);
1173 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1176 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1177 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1181 // NOTE: this function always draws exactly one character if maxwidth <= 0
1182 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)
1184 const char *text_start = text;
1185 int colorindex = STRING_COLOR_DEFAULT;
1188 Uchar ch, mapch, nextch;
1189 Uchar prevch = 0; // used for kerning
1194 ft2_font_map_t *fontmap = NULL;
1195 ft2_font_map_t *map = NULL;
1196 //ft2_font_map_t *prevmap = NULL;
1197 ft2_font_t *ft2 = fnt->ft2;
1199 qboolean snap = true;
1200 qboolean least_one = false;
1201 float dw; // display w
1202 //float dh; // display h
1203 const float *width_of;
1210 // do this in the end
1211 w *= fnt->settings.scale;
1212 h *= fnt->settings.scale;
1214 // find the most fitting size:
1218 map_index = Font_IndexForSize(ft2, h, &w, &h);
1220 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1221 fontmap = Font_MapForIndex(ft2, map_index);
1230 if (!outcolor || *outcolor == -1)
1231 colorindex = STRING_COLOR_DEFAULT;
1233 colorindex = *outcolor;
1235 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1236 // ftbase_x = snap_to_pixel_x(0);
1241 maxwidth = -maxwidth;
1245 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1248 width_of = fontmap->width_of;
1250 width_of = fnt->width_of;
1252 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1255 nextch = ch = u8_getnchar(text, &text, bytes_left);
1256 i = text - text_start;
1259 if (ch == ' ' && !fontmap)
1261 if(!least_one || i0) // never skip the first character
1262 if(x + width_of[(int) ' '] * dw > maxwidth)
1265 break; // oops, can't draw this
1267 x += width_of[(int) ' '] * dw;
1270 // i points to the char after ^
1271 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1273 ch = *text; // colors are ascii, so no u8_ needed
1274 if (ch <= '9' && ch >= '0') // ^[0-9] found
1276 colorindex = ch - '0';
1281 // i points to the char after ^...
1282 // i+3 points to 3 in ^x123
1283 // i+3 == *maxlen would mean that char is missing
1284 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1286 // building colorindex...
1287 ch = tolower(text[1]);
1288 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1289 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1290 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1291 else tempcolorindex = 0;
1294 ch = tolower(text[2]);
1295 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1296 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1297 else tempcolorindex = 0;
1300 ch = tolower(text[3]);
1301 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1302 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1303 else tempcolorindex = 0;
1306 colorindex = tempcolorindex | 0xf;
1307 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1315 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1324 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1331 map = ft2_oldstyle_map;
1333 if(!least_one || i0) // never skip the first character
1334 if(x + width_of[ch] * dw > maxwidth)
1337 break; // oops, can't draw this
1339 x += width_of[ch] * dw;
1341 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1343 map = FontMap_FindForChar(fontmap, ch);
1346 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1352 mapch = ch - map->start;
1353 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1355 x += map->glyphs[mapch].advance_x * dw;
1364 *outcolor = colorindex;
1369 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)
1371 int shadow, colorindex = STRING_COLOR_DEFAULT;
1373 float x = startx, y, s, t, u, v, thisw;
1374 float *av, *at, *ac;
1377 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1378 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1379 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1380 Uchar ch, mapch, nextch;
1381 Uchar prevch = 0; // used for kerning
1384 //ft2_font_map_t *prevmap = NULL; // the previous map
1385 ft2_font_map_t *map = NULL; // the currently used map
1386 ft2_font_map_t *fontmap = NULL; // the font map for the size
1388 const char *text_start = text;
1390 ft2_font_t *ft2 = fnt->ft2;
1391 qboolean snap = true;
1395 const float *width_of;
1398 tw = R_TextureWidth(fnt->tex);
1399 th = R_TextureHeight(fnt->tex);
1407 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1408 w *= fnt->settings.scale;
1409 h *= fnt->settings.scale;
1414 map_index = Font_IndexForSize(ft2, h, &w, &h);
1416 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1417 fontmap = Font_MapForIndex(ft2, map_index);
1423 // draw the font at its baseline when using freetype
1425 ftbase_y = dh * (4.5/6.0);
1430 _DrawQ_ProcessDrawFlag(flags);
1432 R_Mesh_ResetTextureState();
1434 R_Mesh_TexBind(0, fnt->tex);
1435 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1442 //ftbase_x = snap_to_pixel_x(ftbase_x);
1445 startx = snap_to_pixel_x(startx, 0.4);
1446 starty = snap_to_pixel_y(starty, 0.4);
1447 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1450 pix_x = vid.width / vid_conwidth.value;
1451 pix_y = vid.height / vid_conheight.value;
1454 width_of = fontmap->width_of;
1456 width_of = fnt->width_of;
1458 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1463 if (!outcolor || *outcolor == -1)
1464 colorindex = STRING_COLOR_DEFAULT;
1466 colorindex = *outcolor;
1468 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1475 x += r_textshadow.value * vid.width / vid_conwidth.value;
1476 y += r_textshadow.value * vid.height / vid_conheight.value;
1479 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1481 nextch = ch = u8_getnchar(text, &text, bytes_left);
1482 i = text - text_start;
1485 if (ch == ' ' && !fontmap)
1487 x += width_of[(int) ' '] * dw;
1490 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1492 ch = *text; // colors are ascii, so no u8_ needed
1493 if (ch <= '9' && ch >= '0') // ^[0-9] found
1495 colorindex = ch - '0';
1496 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1501 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1503 // building colorindex...
1504 ch = tolower(text[1]);
1505 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1506 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1507 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1508 else tempcolorindex = 0;
1511 ch = tolower(text[2]);
1512 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1513 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1514 else tempcolorindex = 0;
1517 ch = tolower(text[3]);
1518 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1519 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1520 else tempcolorindex = 0;
1523 colorindex = tempcolorindex | 0xf;
1524 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1525 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1526 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1534 else if (ch == STRING_COLOR_TAG)
1543 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1544 // this way we don't need to rebind fnt->tex for every old-style character
1545 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1548 x += 1.0/pix_x * r_textshadow.value;
1549 y += 1.0/pix_y * r_textshadow.value;
1551 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1559 if (map != ft2_oldstyle_map)
1563 // switching from freetype to non-freetype rendering
1564 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1565 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1571 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1572 map = ft2_oldstyle_map;
1576 //num = (unsigned char) text[i];
1577 //thisw = fnt->width_of[num];
1578 thisw = fnt->width_of[ch];
1579 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1580 s = (ch & 15)*0.0625f + (0.5f / tw);
1581 t = (ch >> 4)*0.0625f + (0.5f / th);
1582 u = 0.0625f * thisw - (1.0f / tw);
1583 v = 0.0625f - (1.0f / th);
1584 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1585 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1586 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1587 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1588 at[ 0] = s ; at[ 1] = t ;
1589 at[ 2] = s+u ; at[ 3] = t ;
1590 at[ 4] = s+u ; at[ 5] = t+v ;
1591 at[ 6] = s ; at[ 7] = t+v ;
1592 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1593 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1594 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1595 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1600 if (batchcount >= QUADELEMENTS_MAXQUADS)
1602 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1603 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1609 x += width_of[ch] * dw;
1611 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1613 // new charmap - need to render
1616 // we need a different character map, render what we currently have:
1617 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1618 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1625 map = FontMap_FindForChar(fontmap, ch);
1628 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1635 // this shouldn't happen
1640 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1643 mapch = ch - map->start;
1644 thisw = map->glyphs[mapch].advance_x;
1648 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1655 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1656 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1657 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1658 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1659 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1660 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1661 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1662 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1663 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1664 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1665 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1666 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1675 if (batchcount >= QUADELEMENTS_MAXQUADS)
1677 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1678 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1690 x -= 1.0/pix_x * r_textshadow.value;
1691 y -= 1.0/pix_y * r_textshadow.value;
1697 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1698 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1702 *outcolor = colorindex;
1704 // note: this relies on the proper text (not shadow) being drawn last
1708 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)
1710 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1713 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)
1715 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1718 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1720 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1723 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1725 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1730 // no ^xrgb management
1731 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1733 int color, numchars = 0;
1734 char *outputend2c = output2c + maxoutchars - 2;
1735 if (!outcolor || *outcolor == -1)
1736 color = STRING_COLOR_DEFAULT;
1740 maxreadchars = 1<<30;
1741 textend = text + maxreadchars;
1742 while (text != textend && *text)
1744 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1746 if (text[1] == STRING_COLOR_TAG)
1748 else if (text[1] >= '0' && text[1] <= '9')
1750 color = text[1] - '0';
1755 if (output2c >= outputend2c)
1757 *output2c++ = *text++;
1758 *output2c++ = color;
1761 output2c[0] = output2c[1] = 0;
1768 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)
1772 _DrawQ_ProcessDrawFlag(flags);
1774 R_Mesh_ResetTextureState();
1780 height = pic->height;
1781 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1784 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1786 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1787 floats[0] = floats[9] = x;
1788 floats[1] = floats[4] = y;
1789 floats[3] = floats[6] = x + width;
1790 floats[7] = floats[10] = y + height;
1791 floats[12] = s1;floats[13] = t1;
1792 floats[14] = s2;floats[15] = t2;
1793 floats[16] = s4;floats[17] = t4;
1794 floats[18] = s3;floats[19] = t3;
1795 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1796 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1797 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1798 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1800 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1801 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1804 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1806 _DrawQ_ProcessDrawFlag(flags);
1808 R_Mesh_ResetTextureState();
1809 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1811 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1812 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1815 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1819 _DrawQ_ProcessDrawFlag(flags);
1823 qglBegin(GL_LINE_LOOP);
1824 for (num = 0;num < mesh->num_vertices;num++)
1826 if (mesh->data_color4f)
1827 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]);
1828 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1834 //[515]: this is old, delete
1835 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1837 _DrawQ_ProcessDrawFlag(flags);
1839 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1842 //qglLineWidth(width);CHECKGLERROR
1844 GL_Color(r,g,b,alpha);
1847 qglVertex2f(x1, y1);
1848 qglVertex2f(x2, y2);
1853 void DrawQ_SetClipArea(float x, float y, float width, float height)
1858 // We have to convert the con coords into real coords
1859 // OGL uses top to bottom
1860 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1861 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1862 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1863 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1864 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1866 GL_ScissorTest(true);
1869 void DrawQ_ResetClipArea(void)
1872 GL_ScissorTest(false);
1875 void DrawQ_Finish(void)
1877 r_refdef.draw2dstage = false;
1880 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1881 void R_DrawGamma(void)
1884 switch(vid.renderpath)
1886 case RENDERPATH_GL20:
1887 case RENDERPATH_CGGL:
1888 if (vid_usinghwgamma || v_glslgamma.integer)
1891 case RENDERPATH_GL13:
1892 case RENDERPATH_GL11:
1893 if (vid_usinghwgamma)
1897 // all the blends ignore depth
1898 R_Mesh_ResetTextureState();
1899 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1901 GL_DepthRange(0, 1);
1902 GL_PolygonOffset(0, 0);
1903 GL_DepthTest(false);
1904 if (v_color_enable.integer)
1906 c[0] = v_color_white_r.value;
1907 c[1] = v_color_white_g.value;
1908 c[2] = v_color_white_b.value;
1911 c[0] = c[1] = c[2] = v_contrast.value;
1912 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1914 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1915 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1917 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1918 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1919 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1920 VectorScale(c, 0.5, c);
1923 if (v_color_enable.integer)
1925 c[0] = v_color_black_r.value;
1926 c[1] = v_color_black_g.value;
1927 c[2] = v_color_black_b.value;
1930 c[0] = c[1] = c[2] = v_brightness.value;
1931 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1933 GL_BlendFunc(GL_ONE, GL_ONE);
1934 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1935 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1936 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);