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);
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, 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, 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, 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);
360 if (pixels == NULL && !strncmp(path, "gfx/", 4))
361 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer);
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, 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, 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, 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, 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, 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(drawtexturepool == NULL)
568 return; // before gl_draw_start, so will be loaded later
572 // clear freetype font
573 Font_UnloadFont(fnt->ft2);
578 if(fnt->req_face != -1)
580 if(!Font_LoadFont(fnt->texpath, fnt))
581 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
584 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
585 if(fnt->tex == r_texture_notexture)
587 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
589 if (!fnt->fallbacks[i][0])
591 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
592 if(fnt->tex != r_texture_notexture)
595 if(fnt->tex == r_texture_notexture)
597 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
598 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
601 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
604 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
606 // unspecified width == 1 (base width)
607 for(i = 1; i < 256; ++i)
608 fnt->width_of[i] = 1;
610 // FIXME load "name.width", if it fails, fill all with 1
611 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
613 float extraspacing = 0;
614 const char *p = widthbuf;
619 if(!COM_ParseToken_Simple(&p, false, false))
637 fnt->width_of[ch] = atof(com_token) + extraspacing;
640 for (i = 0; i < MAX_FONT_SIZES; ++i)
642 //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
643 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
646 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
652 if(!strcmp(com_token, "extraspacing"))
654 if(!COM_ParseToken_Simple(&p, false, false))
656 extraspacing = atof(com_token);
658 else if(!strcmp(com_token, "scale"))
660 if(!COM_ParseToken_Simple(&p, false, false))
662 fnt->settings.scale = atof(com_token);
666 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
667 if(!COM_ParseToken_Simple(&p, false, false))
677 maxwidth = fnt->width_of[1];
678 for(i = 2; i < 256; ++i)
679 maxwidth = max(maxwidth, fnt->width_of[i]);
680 fnt->maxwidth = maxwidth;
682 // fix up maxwidth for overlap
683 fnt->maxwidth *= fnt->settings.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(fonts_mempool, 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;
754 float sz, scale, voffset;
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[...] [custom switches] [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"
772 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
773 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
777 f = FindFont(Cmd_Argv(1), true);
780 Con_Printf("font function not found\n");
785 filelist = "gfx/conchars";
787 filelist = Cmd_Argv(2);
789 memset(f->fallbacks, 0, sizeof(f->fallbacks));
790 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
792 // first font is handled "normally"
793 c = strchr(filelist, ':');
794 cm = strchr(filelist, ',');
795 if(c && (!cm || c < cm))
796 f->req_face = atoi(c+1);
803 if(!c || (c - filelist) > MAX_QPATH)
804 strlcpy(mainfont, filelist, sizeof(mainfont));
807 memcpy(mainfont, filelist, c - filelist);
808 mainfont[c - filelist] = 0;
811 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
813 c = strchr(filelist, ',');
819 c = strchr(filelist, ':');
820 cm = strchr(filelist, ',');
821 if(c && (!cm || c < cm))
822 f->fallback_faces[i] = atoi(c+1);
825 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
828 if(!c || (c-filelist) > MAX_QPATH)
830 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
834 memcpy(f->fallbacks[i], filelist, c - filelist);
835 f->fallbacks[i][c - filelist] = 0;
839 // for now: by default load only one size: the default size
841 for(i = 1; i < MAX_FONT_SIZES; ++i)
842 f->req_sizes[i] = -1;
848 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
851 if (!strcmp(Cmd_Argv(i), "scale"))
855 scale = atof(Cmd_Argv(i));
858 if (!strcmp(Cmd_Argv(i), "voffset"))
862 voffset = atof(Cmd_Argv(i));
865 // parse one of sizes
866 sz = atof(Cmd_Argv(i));
867 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
869 f->req_sizes[sizes] = sz;
875 LoadFont(true, mainfont, f, scale, voffset);
883 static void gl_draw_start(void)
886 drawtexturepool = R_AllocTexturePool();
889 memset(cachepichash, 0, sizeof(cachepichash));
893 // load default font textures
894 for(i = 0; i < dp_fonts.maxsize; ++i)
895 if (dp_fonts.f[i].title[0])
896 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
898 // draw the loading screen so people have something to see in the newly opened window
899 SCR_UpdateLoadingScreen(true);
902 static void gl_draw_shutdown(void)
906 R_FreeTexturePool(&drawtexturepool);
909 memset(cachepichash, 0, sizeof(cachepichash));
912 static void gl_draw_newmap(void)
917 void GL_Draw_Init (void)
921 Cvar_RegisterVariable(&r_font_postprocess_blur);
922 Cvar_RegisterVariable(&r_font_postprocess_outline);
923 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
924 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
925 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
926 Cvar_RegisterVariable(&r_font_hinting);
927 Cvar_RegisterVariable(&r_font_antialias);
928 Cvar_RegisterVariable(&r_textshadow);
929 Cvar_RegisterVariable(&r_textbrightness);
930 Cvar_RegisterVariable(&r_textcontrast);
932 // allocate fonts storage
933 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
934 dp_fonts.maxsize = MAX_FONTS;
935 dp_fonts.f = Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
936 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
938 // assign starting font names
939 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
940 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
941 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
942 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
943 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
944 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
945 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
946 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
947 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
948 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
949 if(!FONT_USER(i)->title[0])
950 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
952 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
953 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
956 void _DrawQ_Setup(void)
958 r_viewport_t viewport;
959 if (r_refdef.draw2dstage)
961 r_refdef.draw2dstage = true;
963 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);
964 R_SetViewport(&viewport);
965 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
966 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
967 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
968 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
969 R_EntityMatrix(&identitymatrix);
973 GL_PolygonOffset(0, 0);
977 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
980 static void _DrawQ_ProcessDrawFlag(int flags)
984 if(flags == DRAWFLAG_ADDITIVE)
985 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
986 else if(flags == DRAWFLAG_MODULATE)
987 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
988 else if(flags == DRAWFLAG_2XMODULATE)
989 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
990 else if(flags == DRAWFLAG_SCREEN)
991 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
993 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
996 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1000 _DrawQ_ProcessDrawFlag(flags);
1002 R_Mesh_ResetTextureState();
1003 floats[12] = 0.0f;floats[13] = 0.0f;
1004 floats[14] = 1.0f;floats[15] = 0.0f;
1005 floats[16] = 1.0f;floats[17] = 1.0f;
1006 floats[18] = 0.0f;floats[19] = 1.0f;
1007 floats[20] = floats[24] = floats[28] = floats[32] = red;
1008 floats[21] = floats[25] = floats[29] = floats[33] = green;
1009 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1010 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1016 height = pic->height;
1017 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1020 // AK07: lets be texel correct on the corners
1022 float horz_offset = 0.5f / pic->width;
1023 float vert_offset = 0.5f / pic->height;
1025 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1026 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1027 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1028 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1033 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1035 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1036 floats[0] = floats[9] = x;
1037 floats[1] = floats[4] = y;
1038 floats[3] = floats[6] = x + width;
1039 floats[7] = floats[10] = y + height;
1041 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1042 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1045 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)
1048 float af = DEG2RAD(-angle); // forward
1049 float ar = DEG2RAD(-angle + 90); // right
1050 float sinaf = sin(af);
1051 float cosaf = cos(af);
1052 float sinar = sin(ar);
1053 float cosar = cos(ar);
1055 _DrawQ_ProcessDrawFlag(flags);
1057 R_Mesh_ResetTextureState();
1063 height = pic->height;
1064 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1067 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1069 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1072 floats[0] = x - cosaf*org_x - cosar*org_y;
1073 floats[1] = y - sinaf*org_x - sinar*org_y;
1076 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1077 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1080 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1081 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1084 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1085 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1087 floats[12] = 0.0f;floats[13] = 0.0f;
1088 floats[14] = 1.0f;floats[15] = 0.0f;
1089 floats[16] = 1.0f;floats[17] = 1.0f;
1090 floats[18] = 0.0f;floats[19] = 1.0f;
1091 floats[20] = floats[24] = floats[28] = floats[32] = red;
1092 floats[21] = floats[25] = floats[29] = floats[33] = green;
1093 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1094 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1096 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1097 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1100 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1104 _DrawQ_ProcessDrawFlag(flags);
1106 R_Mesh_ResetTextureState();
1107 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1109 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1110 floats[0] = floats[9] = x;
1111 floats[1] = floats[4] = y;
1112 floats[3] = floats[6] = x + width;
1113 floats[7] = floats[10] = y + height;
1114 floats[12] = 0.0f;floats[13] = 0.0f;
1115 floats[14] = 1.0f;floats[15] = 0.0f;
1116 floats[16] = 1.0f;floats[17] = 1.0f;
1117 floats[18] = 0.0f;floats[19] = 1.0f;
1118 floats[20] = floats[24] = floats[28] = floats[32] = red;
1119 floats[21] = floats[25] = floats[29] = floats[33] = green;
1120 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1121 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1123 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1124 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1127 /// color tag printing
1128 static const vec4_t string_colors[] =
1131 // LordHavoc: why on earth is cyan before magenta in Quake3?
1132 // LordHavoc: note: Doom3 uses white for [0] and [7]
1133 {0.0, 0.0, 0.0, 1.0}, // black
1134 {1.0, 0.0, 0.0, 1.0}, // red
1135 {0.0, 1.0, 0.0, 1.0}, // green
1136 {1.0, 1.0, 0.0, 1.0}, // yellow
1137 {0.0, 0.0, 1.0, 1.0}, // blue
1138 {0.0, 1.0, 1.0, 1.0}, // cyan
1139 {1.0, 0.0, 1.0, 1.0}, // magenta
1140 {1.0, 1.0, 1.0, 1.0}, // white
1141 // [515]'s BX_COLOREDTEXT extension
1142 {1.0, 1.0, 1.0, 0.5}, // half transparent
1143 {0.5, 0.5, 0.5, 1.0} // half brightness
1144 // Black's color table
1145 //{1.0, 1.0, 1.0, 1.0},
1146 //{1.0, 0.0, 0.0, 1.0},
1147 //{0.0, 1.0, 0.0, 1.0},
1148 //{0.0, 0.0, 1.0, 1.0},
1149 //{1.0, 1.0, 0.0, 1.0},
1150 //{0.0, 1.0, 1.0, 1.0},
1151 //{1.0, 0.0, 1.0, 1.0},
1152 //{0.1, 0.1, 0.1, 1.0}
1155 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1157 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1159 float C = r_textcontrast.value;
1160 float B = r_textbrightness.value;
1161 if (colorindex & 0x10000) // that bit means RGB color
1163 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1164 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1165 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1166 color[3] = (colorindex & 0xf) / 15.0;
1169 Vector4Copy(string_colors[colorindex], color);
1170 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1173 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1174 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1178 // NOTE: this function always draws exactly one character if maxwidth <= 0
1179 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)
1181 const char *text_start = text;
1182 int colorindex = STRING_COLOR_DEFAULT;
1185 Uchar ch, mapch, nextch;
1186 Uchar prevch = 0; // used for kerning
1191 ft2_font_map_t *fontmap = NULL;
1192 ft2_font_map_t *map = NULL;
1193 //ft2_font_map_t *prevmap = NULL;
1194 ft2_font_t *ft2 = fnt->ft2;
1196 qboolean snap = true;
1197 qboolean least_one = false;
1198 float dw; // display w
1199 //float dh; // display h
1200 const float *width_of;
1207 // do this in the end
1208 w *= fnt->settings.scale;
1209 h *= fnt->settings.scale;
1211 // find the most fitting size:
1215 map_index = Font_IndexForSize(ft2, h, &w, &h);
1217 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1218 fontmap = Font_MapForIndex(ft2, map_index);
1227 if (!outcolor || *outcolor == -1)
1228 colorindex = STRING_COLOR_DEFAULT;
1230 colorindex = *outcolor;
1232 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1233 // ftbase_x = snap_to_pixel_x(0);
1238 maxwidth = -maxwidth;
1242 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1245 width_of = fontmap->width_of;
1247 width_of = fnt->width_of;
1249 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1252 nextch = ch = u8_getnchar(text, &text, bytes_left);
1253 i = text - text_start;
1256 if (ch == ' ' && !fontmap)
1258 if(!least_one || i0) // never skip the first character
1259 if(x + width_of[(int) ' '] * dw > maxwidth)
1262 break; // oops, can't draw this
1264 x += width_of[(int) ' '] * dw;
1267 // i points to the char after ^
1268 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1270 ch = *text; // colors are ascii, so no u8_ needed
1271 if (ch <= '9' && ch >= '0') // ^[0-9] found
1273 colorindex = ch - '0';
1278 // i points to the char after ^...
1279 // i+3 points to 3 in ^x123
1280 // i+3 == *maxlen would mean that char is missing
1281 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1283 // building colorindex...
1284 ch = tolower(text[1]);
1285 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1286 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1287 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1288 else tempcolorindex = 0;
1291 ch = tolower(text[2]);
1292 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1293 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1294 else tempcolorindex = 0;
1297 ch = tolower(text[3]);
1298 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1299 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1300 else tempcolorindex = 0;
1303 colorindex = tempcolorindex | 0xf;
1304 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1312 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1321 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1328 map = ft2_oldstyle_map;
1330 if(!least_one || i0) // never skip the first character
1331 if(x + width_of[ch] * dw > maxwidth)
1334 break; // oops, can't draw this
1336 x += width_of[ch] * dw;
1338 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1340 map = FontMap_FindForChar(fontmap, ch);
1343 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1349 mapch = ch - map->start;
1350 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1352 x += map->glyphs[mapch].advance_x * dw;
1361 *outcolor = colorindex;
1366 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)
1368 int shadow, colorindex = STRING_COLOR_DEFAULT;
1370 float x = startx, y, s, t, u, v, thisw;
1371 float *av, *at, *ac;
1374 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1375 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1376 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1377 Uchar ch, mapch, nextch;
1378 Uchar prevch = 0; // used for kerning
1381 //ft2_font_map_t *prevmap = NULL; // the previous map
1382 ft2_font_map_t *map = NULL; // the currently used map
1383 ft2_font_map_t *fontmap = NULL; // the font map for the size
1385 const char *text_start = text;
1387 ft2_font_t *ft2 = fnt->ft2;
1388 qboolean snap = true;
1392 const float *width_of;
1395 tw = R_TextureWidth(fnt->tex);
1396 th = R_TextureHeight(fnt->tex);
1404 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1405 w *= fnt->settings.scale;
1406 h *= fnt->settings.scale;
1411 map_index = Font_IndexForSize(ft2, h, &w, &h);
1413 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1414 fontmap = Font_MapForIndex(ft2, map_index);
1420 // draw the font at its baseline when using freetype
1422 ftbase_y = dh * (4.5/6.0);
1427 _DrawQ_ProcessDrawFlag(flags);
1429 R_Mesh_ResetTextureState();
1431 R_Mesh_TexBind(0, fnt->tex);
1432 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1439 //ftbase_x = snap_to_pixel_x(ftbase_x);
1442 startx = snap_to_pixel_x(startx, 0.4);
1443 starty = snap_to_pixel_y(starty, 0.4);
1444 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1447 pix_x = vid.width / vid_conwidth.value;
1448 pix_y = vid.height / vid_conheight.value;
1451 width_of = fontmap->width_of;
1453 width_of = fnt->width_of;
1455 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1460 if (!outcolor || *outcolor == -1)
1461 colorindex = STRING_COLOR_DEFAULT;
1463 colorindex = *outcolor;
1465 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1472 x += r_textshadow.value * vid.width / vid_conwidth.value;
1473 y += r_textshadow.value * vid.height / vid_conheight.value;
1476 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1478 nextch = ch = u8_getnchar(text, &text, bytes_left);
1479 i = text - text_start;
1482 if (ch == ' ' && !fontmap)
1484 x += width_of[(int) ' '] * dw;
1487 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1489 ch = *text; // colors are ascii, so no u8_ needed
1490 if (ch <= '9' && ch >= '0') // ^[0-9] found
1492 colorindex = ch - '0';
1493 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1498 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1500 // building colorindex...
1501 ch = tolower(text[1]);
1502 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1503 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1504 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1505 else tempcolorindex = 0;
1508 ch = tolower(text[2]);
1509 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1510 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1511 else tempcolorindex = 0;
1514 ch = tolower(text[3]);
1515 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1516 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1517 else tempcolorindex = 0;
1520 colorindex = tempcolorindex | 0xf;
1521 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1522 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1523 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1531 else if (ch == STRING_COLOR_TAG)
1540 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1541 // this way we don't need to rebind fnt->tex for every old-style character
1542 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1545 x += 1.0/pix_x * r_textshadow.value;
1546 y += 1.0/pix_y * r_textshadow.value;
1548 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1556 if (map != ft2_oldstyle_map)
1560 // switching from freetype to non-freetype rendering
1561 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1562 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1568 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1569 map = ft2_oldstyle_map;
1573 //num = (unsigned char) text[i];
1574 //thisw = fnt->width_of[num];
1575 thisw = fnt->width_of[ch];
1576 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1577 s = (ch & 15)*0.0625f + (0.5f / tw);
1578 t = (ch >> 4)*0.0625f + (0.5f / th);
1579 u = 0.0625f * thisw - (1.0f / tw);
1580 v = 0.0625f - (1.0f / th);
1581 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1582 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1583 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1584 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1585 at[ 0] = s ; at[ 1] = t ;
1586 at[ 2] = s+u ; at[ 3] = t ;
1587 at[ 4] = s+u ; at[ 5] = t+v ;
1588 at[ 6] = s ; at[ 7] = t+v ;
1589 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1590 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1591 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1592 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1597 if (batchcount >= QUADELEMENTS_MAXQUADS)
1599 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1600 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1606 x += width_of[ch] * dw;
1608 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1610 // new charmap - need to render
1613 // we need a different character map, render what we currently have:
1614 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1615 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1622 map = FontMap_FindForChar(fontmap, ch);
1625 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1632 // this shouldn't happen
1637 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1640 mapch = ch - map->start;
1641 thisw = map->glyphs[mapch].advance_x;
1645 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1652 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1653 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1654 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1655 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1656 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1657 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1658 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1659 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1660 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1661 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1662 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1663 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1672 if (batchcount >= QUADELEMENTS_MAXQUADS)
1674 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1675 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1687 x -= 1.0/pix_x * r_textshadow.value;
1688 y -= 1.0/pix_y * r_textshadow.value;
1694 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1695 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1699 *outcolor = colorindex;
1701 // note: this relies on the proper text (not shadow) being drawn last
1705 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)
1707 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1710 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)
1712 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1715 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1717 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1720 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1722 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1727 // no ^xrgb management
1728 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1730 int color, numchars = 0;
1731 char *outputend2c = output2c + maxoutchars - 2;
1732 if (!outcolor || *outcolor == -1)
1733 color = STRING_COLOR_DEFAULT;
1737 maxreadchars = 1<<30;
1738 textend = text + maxreadchars;
1739 while (text != textend && *text)
1741 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1743 if (text[1] == STRING_COLOR_TAG)
1745 else if (text[1] >= '0' && text[1] <= '9')
1747 color = text[1] - '0';
1752 if (output2c >= outputend2c)
1754 *output2c++ = *text++;
1755 *output2c++ = color;
1758 output2c[0] = output2c[1] = 0;
1765 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)
1769 _DrawQ_ProcessDrawFlag(flags);
1771 R_Mesh_ResetTextureState();
1777 height = pic->height;
1778 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1781 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1783 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1784 floats[0] = floats[9] = x;
1785 floats[1] = floats[4] = y;
1786 floats[3] = floats[6] = x + width;
1787 floats[7] = floats[10] = y + height;
1788 floats[12] = s1;floats[13] = t1;
1789 floats[14] = s2;floats[15] = t2;
1790 floats[16] = s4;floats[17] = t4;
1791 floats[18] = s3;floats[19] = t3;
1792 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1793 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1794 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1795 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1797 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1798 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1801 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1803 _DrawQ_ProcessDrawFlag(flags);
1805 R_Mesh_ResetTextureState();
1806 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1808 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1809 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1812 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1816 _DrawQ_ProcessDrawFlag(flags);
1820 qglBegin(GL_LINE_LOOP);
1821 for (num = 0;num < mesh->num_vertices;num++)
1823 if (mesh->data_color4f)
1824 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]);
1825 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1831 //[515]: this is old, delete
1832 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1834 _DrawQ_ProcessDrawFlag(flags);
1836 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1839 //qglLineWidth(width);CHECKGLERROR
1841 GL_Color(r,g,b,alpha);
1844 qglVertex2f(x1, y1);
1845 qglVertex2f(x2, y2);
1850 void DrawQ_SetClipArea(float x, float y, float width, float height)
1855 // We have to convert the con coords into real coords
1856 // OGL uses top to bottom
1857 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1858 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1859 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1860 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1861 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1863 GL_ScissorTest(true);
1866 void DrawQ_ResetClipArea(void)
1869 GL_ScissorTest(false);
1872 void DrawQ_Finish(void)
1874 r_refdef.draw2dstage = false;
1877 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1878 void R_DrawGamma(void)
1881 switch(vid.renderpath)
1883 case RENDERPATH_GL20:
1884 case RENDERPATH_CGGL:
1885 if (vid_usinghwgamma || v_glslgamma.integer)
1888 case RENDERPATH_GL13:
1889 case RENDERPATH_GL11:
1890 if (vid_usinghwgamma)
1894 // all the blends ignore depth
1895 R_Mesh_ResetTextureState();
1896 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1898 GL_DepthRange(0, 1);
1899 GL_PolygonOffset(0, 0);
1900 GL_DepthTest(false);
1901 if (v_color_enable.integer)
1903 c[0] = v_color_white_r.value;
1904 c[1] = v_color_white_g.value;
1905 c[2] = v_color_white_b.value;
1908 c[0] = c[1] = c[2] = v_contrast.value;
1909 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1911 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1912 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1914 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1915 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1916 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1917 VectorScale(c, 0.5, c);
1920 if (v_color_enable.integer)
1922 c[0] = v_color_black_r.value;
1923 c[1] = v_color_black_g.value;
1924 c[2] = v_color_black_b.value;
1927 c[0] = c[1] = c[2] = v_brightness.value;
1928 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1930 GL_BlendFunc(GL_ONE, GL_ONE);
1931 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1932 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1933 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);