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"
33 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)"};
34 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)"};
35 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)"};
37 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
38 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
39 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
40 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
42 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
43 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
45 extern cvar_t v_glslgamma;
47 //=============================================================================
48 /* Support Routines */
50 #define FONT_FILESIZE 13468
51 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
52 static cachepic_t cachepics[MAX_CACHED_PICS];
53 static int numcachepics;
55 static rtexturepool_t *drawtexturepool;
57 static const unsigned char concharimage[FONT_FILESIZE] =
62 static rtexture_t *draw_generateconchars(void)
69 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
71 for (i = 0;i < 8192;i++)
73 random = lhrandom (0.0,1.0);
74 data[i*4+3] = data[i*4+0];
75 data[i*4+2] = 83 + (unsigned char)(random * 64);
76 data[i*4+1] = 71 + (unsigned char)(random * 32);
77 data[i*4+0] = 23 + (unsigned char)(random * 16);
80 for (i = 8192;i < 32768;i++)
82 random = lhrandom (0.0,1.0);
83 data[i*4+3] = data[i*4+0];
84 data[i*4+2] = 95 + (unsigned char)(random * 64);
85 data[i*4+1] = 95 + (unsigned char)(random * 64);
86 data[i*4+0] = 95 + (unsigned char)(random * 64);
89 for (i = 32768;i < 40960;i++)
91 random = lhrandom (0.0,1.0);
92 data[i*4+3] = data[i*4+0];
93 data[i*4+2] = 83 + (unsigned char)(random * 64);
94 data[i*4+1] = 71 + (unsigned char)(random * 32);
95 data[i*4+0] = 23 + (unsigned char)(random * 16);
98 for (i = 40960;i < 65536;i++)
100 random = lhrandom (0.0,1.0);
101 data[i*4+3] = data[i*4+0];
102 data[i*4+2] = 96 + (unsigned char)(random * 64);
103 data[i*4+1] = 43 + (unsigned char)(random * 32);
104 data[i*4+0] = 27 + (unsigned char)(random * 32);
108 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
111 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, NULL);
116 static rtexture_t *draw_generateditherpattern(void)
119 unsigned char pixels[8][8];
120 for (y = 0;y < 8;y++)
121 for (x = 0;x < 8;x++)
122 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
123 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, palette_bgra_transparent);
126 typedef struct embeddedpic_s
135 static const embeddedpic_t embeddedpics[] =
138 "gfx/prydoncursor001", 16, 16,
157 "ui/mousepointer", 16, 16,
176 "gfx/crosshair1", 16, 16,
195 "gfx/crosshair2", 16, 16,
214 "gfx/crosshair3", 16, 16,
233 "gfx/crosshair4", 16, 16,
252 "gfx/crosshair5", 8, 8,
263 "gfx/crosshair6", 2, 2,
268 "gfx/crosshair7", 16, 16,
289 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
291 const embeddedpic_t *p;
292 for (p = embeddedpics;p->name;p++)
293 if (!strcmp(name, p->name))
294 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, palette_bgra_embeddedpic);
295 if (!strcmp(name, "gfx/conchars"))
296 return draw_generateconchars();
297 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
298 return draw_generateditherpattern();
300 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
301 return r_texture_notexture;
310 // FIXME: move this to client somehow
311 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
314 unsigned char *pixels;
317 unsigned char *lmpdata;
318 char lmpname[MAX_QPATH];
320 // check whether the picture has already been cached
321 crc = CRC_Block((unsigned char *)path, strlen(path));
322 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
323 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
324 if (!strcmp (path, pic->name))
327 if (numcachepics == MAX_CACHED_PICS)
329 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
330 // FIXME: support NULL in callers?
331 return cachepics; // return the first one
333 pic = cachepics + (numcachepics++);
334 strlcpy (pic->name, path, sizeof(pic->name));
336 pic->chain = cachepichash[hashkey];
337 cachepichash[hashkey] = pic;
339 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
340 pic->tex = CL_GetDynTexture( path );
341 // if so, set the width/height, too
343 pic->width = R_TextureWidth(pic->tex);
344 pic->height = R_TextureHeight(pic->tex);
345 // we're done now (early-out)
349 pic->texflags = TEXF_ALPHA;
350 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
351 pic->texflags |= TEXF_CLAMP;
352 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
353 pic->texflags |= TEXF_COMPRESS;
355 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
357 // load a high quality image from disk if possible
358 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer);
359 if (pixels == NULL && !strncmp(path, "gfx/", 4))
360 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer);
363 pic->width = image_width;
364 pic->height = image_height;
366 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, NULL);
370 pic->autoload = false;
371 // never compress the fallback images
372 pic->texflags &= ~TEXF_COMPRESS;
375 // now read the low quality version (wad or lmp file), and take the pic
376 // size from that even if we don't upload the texture, this way the pics
377 // show up the right size in the menu even if they were replaced with
378 // higher or lower resolution versions
379 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
380 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
382 if (developer_loading.integer)
383 Con_Printf("loading lump \"%s\"\n", path);
387 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
388 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
389 // if no high quality replacement image was found, upload the original low quality texture
391 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
395 else if ((lmpdata = W_GetLumpName (path + 4)))
397 if (developer_loading.integer)
398 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
400 if (!strcmp(path, "gfx/conchars"))
402 // conchars is a raw image and with color 0 as transparent instead of 255
405 // if no high quality replacement image was found, upload the original low quality texture
407 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, palette_bgra_font);
411 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
412 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
413 // if no high quality replacement image was found, upload the original low quality texture
415 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
424 else if (pic->tex == NULL)
426 // if it's not found on disk, generate an image
427 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
428 pic->width = R_TextureWidth(pic->tex);
429 pic->height = R_TextureHeight(pic->tex);
435 cachepic_t *Draw_CachePic (const char *path)
437 return Draw_CachePic_Flags (path, 0);
442 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
444 if (pic->autoload && !pic->tex)
446 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
447 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
448 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
449 if (pic->tex == NULL)
450 pic->tex = draw_generatepic(pic->name, true);
452 pic->lastusedframe = draw_frame;
456 void Draw_Frame(void)
460 static double nextpurgetime;
461 if (nextpurgetime > realtime)
463 nextpurgetime = realtime + 0.05;
464 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
466 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
468 R_FreeTexture(pic->tex);
475 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
480 crc = CRC_Block((unsigned char *)picname, strlen(picname));
481 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
482 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
483 if (!strcmp (picname, pic->name))
488 if (pic->tex && pic->width == width && pic->height == height)
490 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
498 if (numcachepics == MAX_CACHED_PICS)
500 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
501 // FIXME: support NULL in callers?
502 return cachepics; // return the first one
504 pic = cachepics + (numcachepics++);
505 strlcpy (pic->name, picname, sizeof(pic->name));
507 pic->chain = cachepichash[hashkey];
508 cachepichash[hashkey] = pic;
513 pic->height = height;
515 R_FreeTexture(pic->tex);
516 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL);
520 void Draw_FreePic(const char *picname)
525 // this doesn't really free the pic, but does free it's texture
526 crc = CRC_Block((unsigned char *)picname, strlen(picname));
527 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
528 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
530 if (!strcmp (picname, pic->name) && pic->tex)
532 R_FreeTexture(pic->tex);
541 static float snap_to_pixel_x(float x, float roundUpAt);
542 extern int con_linewidth; // to force rewrapping
543 void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
546 float maxwidth, scale;
547 char widthfile[MAX_QPATH];
549 fs_offset_t widthbufsize;
551 if(override || !fnt->texpath[0])
553 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
555 // load the cvars when the font is FIRST loaded
556 fnt->settings.antialias = r_font_antialias.integer;
557 fnt->settings.hinting = r_font_hinting.integer;
558 fnt->settings.outline = r_font_postprocess_outline.value;
559 fnt->settings.blur = r_font_postprocess_blur.value;
560 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
561 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
562 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
565 if(drawtexturepool == NULL)
566 return; // before gl_draw_start, so will be loaded later
570 // clear freetype font
571 Font_UnloadFont(fnt->ft2);
576 if(fnt->req_face != -1)
578 if(!Font_LoadFont(fnt->texpath, fnt))
579 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
582 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
583 if(fnt->tex == r_texture_notexture)
585 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
587 if (!fnt->fallbacks[i][0])
589 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
590 if(fnt->tex != r_texture_notexture)
593 if(fnt->tex == r_texture_notexture)
595 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
596 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
599 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
602 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
604 // unspecified width == 1 (base width)
605 for(i = 1; i < 256; ++i)
606 fnt->width_of[i] = 1;
609 // FIXME load "name.width", if it fails, fill all with 1
610 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
612 float extraspacing = 0;
613 const char *p = widthbuf;
618 if(!COM_ParseToken_Simple(&p, false, false))
636 fnt->width_of[ch] = atof(com_token) + extraspacing;
639 for (i = 0; i < MAX_FONT_SIZES; ++i)
641 //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
642 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
645 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
651 if(!strcmp(com_token, "extraspacing"))
653 if(!COM_ParseToken_Simple(&p, false, false))
655 extraspacing = atof(com_token);
657 else if(!strcmp(com_token, "scale"))
659 if(!COM_ParseToken_Simple(&p, false, false))
661 scale = atof(com_token);
665 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
666 if(!COM_ParseToken_Simple(&p, false, false))
676 maxwidth = fnt->width_of[1];
677 for(i = 2; i < 256; ++i)
678 maxwidth = max(maxwidth, fnt->width_of[i]);
679 fnt->maxwidth = maxwidth;
681 // fix up maxwidth for overlap
682 fnt->maxwidth *= scale;
685 if(fnt == FONT_CONSOLE)
686 con_linewidth = -1; // rewrap console in next frame
689 extern cvar_t developer_font;
690 dp_font_t *FindFont(const char *title, qboolean allocate_new)
695 for(i = 0; i < dp_fonts.maxsize; ++i)
696 if(!strcmp(dp_fonts.f[i].title, title))
697 return &dp_fonts.f[i];
698 // if not found - try allocate
701 // find any font with empty title
702 for(i = 0; i < dp_fonts.maxsize; ++i)
704 if(!strcmp(dp_fonts.f[i].title, ""))
706 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
707 return &dp_fonts.f[i];
710 // if no any 'free' fonts - expand buffer
711 i = dp_fonts.maxsize;
712 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
713 if (developer_font.integer)
714 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
715 dp_fonts.f = Mem_Realloc(tempmempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
716 // register a font in first expanded slot
717 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
718 return &dp_fonts.f[i];
723 static float snap_to_pixel_x(float x, float roundUpAt)
725 float pixelpos = x * vid.width / vid_conwidth.value;
726 int snap = (int) pixelpos;
727 if (pixelpos - snap >= roundUpAt) ++snap;
728 return ((float)snap * vid_conwidth.value / vid.width);
730 x = (int)(x * vid.width / vid_conwidth.value);
731 x = (x * vid_conwidth.value / vid.width);
736 static float snap_to_pixel_y(float y, float roundUpAt)
738 float pixelpos = y * vid.height / vid_conheight.value;
739 int snap = (int) pixelpos;
740 if (pixelpos - snap > roundUpAt) ++snap;
741 return ((float)snap * vid_conheight.value / vid.height);
743 y = (int)(y * vid.height / vid_conheight.value);
744 y = (y * vid_conheight.value / vid.height);
749 static void LoadFont_f(void)
753 const char *filelist, *c, *cm;
755 char mainfont[MAX_QPATH];
759 Con_Printf("Available font commands:\n");
760 for(i = 0; i < dp_fonts.maxsize; ++i)
761 if (dp_fonts.f[i].title[0])
762 Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts.f[i].title);
763 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
764 "can specify multiple fonts and faces\n"
765 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
766 "to load face 2 of the font gfx/vera-sans and use face 1\n"
767 "of gfx/fallback as fallback font.\n"
768 "You can also specify a list of font sizes to load, like this:\n"
769 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
770 "In many cases, 8 12 16 24 32 should be a good choice.\n"
774 f = FindFont(Cmd_Argv(1), true);
777 Con_Printf("font function not found\n");
782 filelist = "gfx/conchars";
784 filelist = Cmd_Argv(2);
786 memset(f->fallbacks, 0, sizeof(f->fallbacks));
787 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
789 // first font is handled "normally"
790 c = strchr(filelist, ':');
791 cm = strchr(filelist, ',');
792 if(c && (!cm || c < cm))
793 f->req_face = atoi(c+1);
800 if(!c || (c - filelist) > MAX_QPATH)
801 strlcpy(mainfont, filelist, sizeof(mainfont));
804 memcpy(mainfont, filelist, c - filelist);
805 mainfont[c - filelist] = 0;
808 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
810 c = strchr(filelist, ',');
816 c = strchr(filelist, ':');
817 cm = strchr(filelist, ',');
818 if(c && (!cm || c < cm))
819 f->fallback_faces[i] = atoi(c+1);
822 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
825 if(!c || (c-filelist) > MAX_QPATH)
827 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
831 memcpy(f->fallbacks[i], filelist, c - filelist);
832 f->fallbacks[i][c - filelist] = 0;
836 // for now: by default load only one size: the default size
838 for(i = 1; i < MAX_FONT_SIZES; ++i)
839 f->req_sizes[i] = -1;
843 for(i = 0; i < Cmd_Argc()-3; ++i)
845 sz = atof(Cmd_Argv(i+3));
846 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
847 f->req_sizes[i] = sz;
851 LoadFont(true, mainfont, f);
859 static void gl_draw_start(void)
862 drawtexturepool = R_AllocTexturePool();
865 memset(cachepichash, 0, sizeof(cachepichash));
869 // load default font textures
870 for(i = 0; i < dp_fonts.maxsize; ++i)
871 if (dp_fonts.f[i].title[0])
872 LoadFont(false, va("gfx/font_%s", &dp_fonts.f[i].title), &dp_fonts.f[i]);
874 // draw the loading screen so people have something to see in the newly opened window
875 SCR_UpdateLoadingScreen(true);
878 static void gl_draw_shutdown(void)
882 R_FreeTexturePool(&drawtexturepool);
885 memset(cachepichash, 0, sizeof(cachepichash));
888 static void gl_draw_newmap(void)
893 void GL_Draw_Init (void)
897 Cvar_RegisterVariable(&r_font_postprocess_blur);
898 Cvar_RegisterVariable(&r_font_postprocess_outline);
899 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
900 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
901 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
902 Cvar_RegisterVariable(&r_font_hinting);
903 Cvar_RegisterVariable(&r_font_antialias);
904 Cvar_RegisterVariable(&r_textshadow);
905 Cvar_RegisterVariable(&r_textbrightness);
906 Cvar_RegisterVariable(&r_textcontrast);
908 // allocate fonts storage
909 dp_fonts.maxsize = MAX_FONTS;
910 dp_fonts.f = Mem_Alloc(tempmempool, sizeof(dp_font_t) * dp_fonts.maxsize);
911 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
913 // assign starting font names
914 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
915 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
916 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
917 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
918 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
919 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
920 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
921 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
922 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
923 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
924 if(!FONT_USER(i)->title[0])
925 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
927 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
928 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
931 void _DrawQ_Setup(void)
933 r_viewport_t viewport;
934 if (r_refdef.draw2dstage)
936 r_refdef.draw2dstage = true;
938 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);
939 R_SetViewport(&viewport);
940 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
941 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
942 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
943 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
944 R_EntityMatrix(&identitymatrix);
948 GL_PolygonOffset(0, 0);
952 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
955 static void _DrawQ_ProcessDrawFlag(int flags)
959 if(flags == DRAWFLAG_ADDITIVE)
960 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
961 else if(flags == DRAWFLAG_MODULATE)
962 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
963 else if(flags == DRAWFLAG_2XMODULATE)
964 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
965 else if(flags == DRAWFLAG_SCREEN)
966 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
968 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
971 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
975 _DrawQ_ProcessDrawFlag(flags);
977 R_Mesh_ResetTextureState();
978 floats[12] = 0.0f;floats[13] = 0.0f;
979 floats[14] = 1.0f;floats[15] = 0.0f;
980 floats[16] = 1.0f;floats[17] = 1.0f;
981 floats[18] = 0.0f;floats[19] = 1.0f;
982 floats[20] = floats[24] = floats[28] = floats[32] = red;
983 floats[21] = floats[25] = floats[29] = floats[33] = green;
984 floats[22] = floats[26] = floats[30] = floats[34] = blue;
985 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
991 height = pic->height;
992 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
995 // AK07: lets be texel correct on the corners
997 float horz_offset = 0.5f / pic->width;
998 float vert_offset = 0.5f / pic->height;
1000 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1001 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1002 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1003 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1008 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1010 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1011 floats[0] = floats[9] = x;
1012 floats[1] = floats[4] = y;
1013 floats[3] = floats[6] = x + width;
1014 floats[7] = floats[10] = y + height;
1016 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1017 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1020 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)
1023 float af = DEG2RAD(-angle); // forward
1024 float ar = DEG2RAD(-angle + 90); // right
1025 float sinaf = sin(af);
1026 float cosaf = cos(af);
1027 float sinar = sin(ar);
1028 float cosar = cos(ar);
1030 _DrawQ_ProcessDrawFlag(flags);
1032 R_Mesh_ResetTextureState();
1038 height = pic->height;
1039 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1042 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1044 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1047 floats[0] = x - cosaf*org_x - cosar*org_y;
1048 floats[1] = y - sinaf*org_x - sinar*org_y;
1051 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1052 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1055 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1056 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1059 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1060 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1062 floats[12] = 0.0f;floats[13] = 0.0f;
1063 floats[14] = 1.0f;floats[15] = 0.0f;
1064 floats[16] = 1.0f;floats[17] = 1.0f;
1065 floats[18] = 0.0f;floats[19] = 1.0f;
1066 floats[20] = floats[24] = floats[28] = floats[32] = red;
1067 floats[21] = floats[25] = floats[29] = floats[33] = green;
1068 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1069 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1071 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1072 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1075 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1079 _DrawQ_ProcessDrawFlag(flags);
1081 R_Mesh_ResetTextureState();
1082 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1084 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1085 floats[0] = floats[9] = x;
1086 floats[1] = floats[4] = y;
1087 floats[3] = floats[6] = x + width;
1088 floats[7] = floats[10] = y + height;
1089 floats[12] = 0.0f;floats[13] = 0.0f;
1090 floats[14] = 1.0f;floats[15] = 0.0f;
1091 floats[16] = 1.0f;floats[17] = 1.0f;
1092 floats[18] = 0.0f;floats[19] = 1.0f;
1093 floats[20] = floats[24] = floats[28] = floats[32] = red;
1094 floats[21] = floats[25] = floats[29] = floats[33] = green;
1095 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1096 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1098 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1099 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1102 /// color tag printing
1103 static const vec4_t string_colors[] =
1106 // LordHavoc: why on earth is cyan before magenta in Quake3?
1107 // LordHavoc: note: Doom3 uses white for [0] and [7]
1108 {0.0, 0.0, 0.0, 1.0}, // black
1109 {1.0, 0.0, 0.0, 1.0}, // red
1110 {0.0, 1.0, 0.0, 1.0}, // green
1111 {1.0, 1.0, 0.0, 1.0}, // yellow
1112 {0.0, 0.0, 1.0, 1.0}, // blue
1113 {0.0, 1.0, 1.0, 1.0}, // cyan
1114 {1.0, 0.0, 1.0, 1.0}, // magenta
1115 {1.0, 1.0, 1.0, 1.0}, // white
1116 // [515]'s BX_COLOREDTEXT extension
1117 {1.0, 1.0, 1.0, 0.5}, // half transparent
1118 {0.5, 0.5, 0.5, 1.0} // half brightness
1119 // Black's color table
1120 //{1.0, 1.0, 1.0, 1.0},
1121 //{1.0, 0.0, 0.0, 1.0},
1122 //{0.0, 1.0, 0.0, 1.0},
1123 //{0.0, 0.0, 1.0, 1.0},
1124 //{1.0, 1.0, 0.0, 1.0},
1125 //{0.0, 1.0, 1.0, 1.0},
1126 //{1.0, 0.0, 1.0, 1.0},
1127 //{0.1, 0.1, 0.1, 1.0}
1130 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1132 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1134 float C = r_textcontrast.value;
1135 float B = r_textbrightness.value;
1136 if (colorindex & 0x10000) // that bit means RGB color
1138 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1139 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1140 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1141 color[3] = (colorindex & 0xf) / 15.0;
1144 Vector4Copy(string_colors[colorindex], color);
1145 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1148 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1149 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1153 // NOTE: this function always draws exactly one character if maxwidth <= 0
1154 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)
1156 const char *text_start = text;
1157 int colorindex = STRING_COLOR_DEFAULT;
1160 Uchar ch, mapch, nextch;
1161 Uchar prevch = 0; // used for kerning
1166 ft2_font_map_t *fontmap = NULL;
1167 ft2_font_map_t *map = NULL;
1168 //ft2_font_map_t *prevmap = NULL;
1169 ft2_font_t *ft2 = fnt->ft2;
1171 qboolean snap = true;
1172 qboolean least_one = false;
1173 float dw; // display w
1174 //float dh; // display h
1175 const float *width_of;
1182 // do this in the end
1186 // find the most fitting size:
1190 map_index = Font_IndexForSize(ft2, h, &w, &h);
1192 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1193 fontmap = Font_MapForIndex(ft2, map_index);
1202 if (!outcolor || *outcolor == -1)
1203 colorindex = STRING_COLOR_DEFAULT;
1205 colorindex = *outcolor;
1207 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1208 // ftbase_x = snap_to_pixel_x(0);
1213 maxwidth = -maxwidth;
1217 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1220 width_of = fontmap->width_of;
1222 width_of = fnt->width_of;
1224 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1227 nextch = ch = u8_getnchar(text, &text, bytes_left);
1228 i = text - text_start;
1231 if (ch == ' ' && !fontmap)
1233 if(!least_one || i0) // never skip the first character
1234 if(x + width_of[(int) ' '] * dw > maxwidth)
1237 break; // oops, can't draw this
1239 x += width_of[(int) ' '] * dw;
1242 // i points to the char after ^
1243 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1245 ch = *text; // colors are ascii, so no u8_ needed
1246 if (ch <= '9' && ch >= '0') // ^[0-9] found
1248 colorindex = ch - '0';
1253 // i points to the char after ^...
1254 // i+3 points to 3 in ^x123
1255 // i+3 == *maxlen would mean that char is missing
1256 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1258 // building colorindex...
1259 ch = tolower(text[1]);
1260 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1261 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1262 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1263 else tempcolorindex = 0;
1266 ch = tolower(text[2]);
1267 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1268 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1269 else tempcolorindex = 0;
1272 ch = tolower(text[3]);
1273 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1274 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1275 else tempcolorindex = 0;
1278 colorindex = tempcolorindex | 0xf;
1279 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1287 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1296 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1303 map = ft2_oldstyle_map;
1305 if(!least_one || i0) // never skip the first character
1306 if(x + width_of[ch] * dw > maxwidth)
1309 break; // oops, can't draw this
1311 x += width_of[ch] * dw;
1313 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1315 map = FontMap_FindForChar(fontmap, ch);
1318 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1324 mapch = ch - map->start;
1325 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1327 x += map->glyphs[mapch].advance_x * dw;
1336 *outcolor = colorindex;
1341 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)
1343 int shadow, colorindex = STRING_COLOR_DEFAULT;
1345 float x = startx, y, s, t, u, v, thisw;
1346 float *av, *at, *ac;
1349 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1350 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1351 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1352 Uchar ch, mapch, nextch;
1353 Uchar prevch = 0; // used for kerning
1356 //ft2_font_map_t *prevmap = NULL; // the previous map
1357 ft2_font_map_t *map = NULL; // the currently used map
1358 ft2_font_map_t *fontmap = NULL; // the font map for the size
1360 const char *text_start = text;
1362 ft2_font_t *ft2 = fnt->ft2;
1363 qboolean snap = true;
1367 const float *width_of;
1370 tw = R_TextureWidth(fnt->tex);
1371 th = R_TextureHeight(fnt->tex);
1379 starty -= (fnt->scale - 1) * h * 0.5; // center
1386 map_index = Font_IndexForSize(ft2, h, &w, &h);
1388 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1389 fontmap = Font_MapForIndex(ft2, map_index);
1395 // draw the font at its baseline when using freetype
1397 ftbase_y = dh * (4.5/6.0);
1402 _DrawQ_ProcessDrawFlag(flags);
1404 R_Mesh_ResetTextureState();
1406 R_Mesh_TexBind(0, fnt->tex);
1407 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1414 //ftbase_x = snap_to_pixel_x(ftbase_x);
1417 startx = snap_to_pixel_x(startx, 0.4);
1418 starty = snap_to_pixel_y(starty, 0.4);
1419 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1422 pix_x = vid.width / vid_conwidth.value;
1423 pix_y = vid.height / vid_conheight.value;
1426 width_of = fontmap->width_of;
1428 width_of = fnt->width_of;
1430 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1435 if (!outcolor || *outcolor == -1)
1436 colorindex = STRING_COLOR_DEFAULT;
1438 colorindex = *outcolor;
1440 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1447 x += r_textshadow.value * vid.width / vid_conwidth.value;
1448 y += r_textshadow.value * vid.height / vid_conheight.value;
1451 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1453 nextch = ch = u8_getnchar(text, &text, bytes_left);
1454 i = text - text_start;
1457 if (ch == ' ' && !fontmap)
1459 x += width_of[(int) ' '] * dw;
1462 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1464 ch = *text; // colors are ascii, so no u8_ needed
1465 if (ch <= '9' && ch >= '0') // ^[0-9] found
1467 colorindex = ch - '0';
1468 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1473 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1475 // building colorindex...
1476 ch = tolower(text[1]);
1477 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1478 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1479 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1480 else tempcolorindex = 0;
1483 ch = tolower(text[2]);
1484 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1485 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1486 else tempcolorindex = 0;
1489 ch = tolower(text[3]);
1490 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1491 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1492 else tempcolorindex = 0;
1495 colorindex = tempcolorindex | 0xf;
1496 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1497 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1498 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1506 else if (ch == STRING_COLOR_TAG)
1515 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1516 // this way we don't need to rebind fnt->tex for every old-style character
1517 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1520 x += 1.0/pix_x * r_textshadow.value;
1521 y += 1.0/pix_y * r_textshadow.value;
1523 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1531 if (map != ft2_oldstyle_map)
1535 // switching from freetype to non-freetype rendering
1536 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1537 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1543 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1544 map = ft2_oldstyle_map;
1548 //num = (unsigned char) text[i];
1549 //thisw = fnt->width_of[num];
1550 thisw = fnt->width_of[ch];
1551 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1552 s = (ch & 15)*0.0625f + (0.5f / tw);
1553 t = (ch >> 4)*0.0625f + (0.5f / th);
1554 u = 0.0625f * thisw - (1.0f / tw);
1555 v = 0.0625f - (1.0f / th);
1556 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1557 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1558 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1559 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1560 at[ 0] = s ; at[ 1] = t ;
1561 at[ 2] = s+u ; at[ 3] = t ;
1562 at[ 4] = s+u ; at[ 5] = t+v ;
1563 at[ 6] = s ; at[ 7] = t+v ;
1564 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1565 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1566 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1567 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1572 if (batchcount >= QUADELEMENTS_MAXQUADS)
1574 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1575 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1581 x += width_of[ch] * dw;
1583 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1585 // new charmap - need to render
1588 // we need a different character map, render what we currently have:
1589 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1590 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1597 map = FontMap_FindForChar(fontmap, ch);
1600 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1607 // this shouldn't happen
1612 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1615 mapch = ch - map->start;
1616 thisw = map->glyphs[mapch].advance_x;
1620 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1627 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1628 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1629 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1630 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1631 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1632 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1633 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1634 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1635 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1636 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1637 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1638 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1647 if (batchcount >= QUADELEMENTS_MAXQUADS)
1649 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1650 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1662 x -= 1.0/pix_x * r_textshadow.value;
1663 y -= 1.0/pix_y * r_textshadow.value;
1669 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1670 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1674 *outcolor = colorindex;
1676 // note: this relies on the proper text (not shadow) being drawn last
1680 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)
1682 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1685 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)
1687 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1690 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1692 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1695 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1697 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1702 // no ^xrgb management
1703 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1705 int color, numchars = 0;
1706 char *outputend2c = output2c + maxoutchars - 2;
1707 if (!outcolor || *outcolor == -1)
1708 color = STRING_COLOR_DEFAULT;
1712 maxreadchars = 1<<30;
1713 textend = text + maxreadchars;
1714 while (text != textend && *text)
1716 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1718 if (text[1] == STRING_COLOR_TAG)
1720 else if (text[1] >= '0' && text[1] <= '9')
1722 color = text[1] - '0';
1727 if (output2c >= outputend2c)
1729 *output2c++ = *text++;
1730 *output2c++ = color;
1733 output2c[0] = output2c[1] = 0;
1740 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)
1744 _DrawQ_ProcessDrawFlag(flags);
1746 R_Mesh_ResetTextureState();
1752 height = pic->height;
1753 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1756 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1758 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1759 floats[0] = floats[9] = x;
1760 floats[1] = floats[4] = y;
1761 floats[3] = floats[6] = x + width;
1762 floats[7] = floats[10] = y + height;
1763 floats[12] = s1;floats[13] = t1;
1764 floats[14] = s2;floats[15] = t2;
1765 floats[16] = s4;floats[17] = t4;
1766 floats[18] = s3;floats[19] = t3;
1767 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1768 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1769 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1770 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1772 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1773 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1776 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1778 _DrawQ_ProcessDrawFlag(flags);
1780 R_Mesh_ResetTextureState();
1781 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1783 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1784 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1787 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1791 _DrawQ_ProcessDrawFlag(flags);
1795 qglBegin(GL_LINE_LOOP);
1796 for (num = 0;num < mesh->num_vertices;num++)
1798 if (mesh->data_color4f)
1799 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]);
1800 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1806 //[515]: this is old, delete
1807 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1809 _DrawQ_ProcessDrawFlag(flags);
1811 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1814 //qglLineWidth(width);CHECKGLERROR
1816 GL_Color(r,g,b,alpha);
1819 qglVertex2f(x1, y1);
1820 qglVertex2f(x2, y2);
1825 void DrawQ_SetClipArea(float x, float y, float width, float height)
1830 // We have to convert the con coords into real coords
1831 // OGL uses top to bottom
1832 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1833 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1834 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1835 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1836 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1838 GL_ScissorTest(true);
1841 void DrawQ_ResetClipArea(void)
1844 GL_ScissorTest(false);
1847 void DrawQ_Finish(void)
1849 r_refdef.draw2dstage = false;
1852 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1853 void R_DrawGamma(void)
1856 switch(vid.renderpath)
1858 case RENDERPATH_GL20:
1859 case RENDERPATH_CGGL:
1860 if (vid_usinghwgamma || v_glslgamma.integer)
1863 case RENDERPATH_GL13:
1864 case RENDERPATH_GL11:
1865 if (vid_usinghwgamma)
1869 // all the blends ignore depth
1870 R_Mesh_ResetTextureState();
1871 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1873 GL_DepthRange(0, 1);
1874 GL_PolygonOffset(0, 0);
1875 GL_DepthTest(false);
1876 if (v_color_enable.integer)
1878 c[0] = v_color_white_r.value;
1879 c[1] = v_color_white_g.value;
1880 c[2] = v_color_white_b.value;
1883 c[0] = c[1] = c[2] = v_contrast.value;
1884 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1886 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1887 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1889 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1890 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1891 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1892 VectorScale(c, 0.5, c);
1895 if (v_color_enable.integer)
1897 c[0] = v_color_black_r.value;
1898 c[1] = v_color_black_g.value;
1899 c[2] = v_color_black_b.value;
1902 c[0] = c[1] = c[2] = v_brightness.value;
1903 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1905 GL_BlendFunc(GL_ONE, GL_ONE);
1906 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1907 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1908 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);