2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include "cl_dyntexture.h"
29 #include "ft2_fontdefs.h"
32 static mempool_t *fonts_mempool = NULL;
34 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
35 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
36 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
46 extern cvar_t v_glslgamma;
48 //=============================================================================
49 /* Support Routines */
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
56 static rtexturepool_t *drawtexturepool;
58 static const unsigned char concharimage[FONT_FILESIZE] =
63 static rtexture_t *draw_generateconchars(void)
70 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
72 for (i = 0;i < 8192;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 83 + (unsigned char)(random * 64);
77 data[i*4+1] = 71 + (unsigned char)(random * 32);
78 data[i*4+0] = 23 + (unsigned char)(random * 16);
81 for (i = 8192;i < 32768;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 95 + (unsigned char)(random * 64);
86 data[i*4+1] = 95 + (unsigned char)(random * 64);
87 data[i*4+0] = 95 + (unsigned char)(random * 64);
90 for (i = 32768;i < 40960;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 83 + (unsigned char)(random * 64);
95 data[i*4+1] = 71 + (unsigned char)(random * 32);
96 data[i*4+0] = 23 + (unsigned char)(random * 16);
99 for (i = 40960;i < 65536;i++)
101 random = lhrandom (0.0,1.0);
102 data[i*4+3] = data[i*4+0];
103 data[i*4+2] = 96 + (unsigned char)(random * 64);
104 data[i*4+1] = 43 + (unsigned char)(random * 32);
105 data[i*4+0] = 27 + (unsigned char)(random * 32);
109 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
112 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
117 static rtexture_t *draw_generateditherpattern(void)
120 unsigned char pixels[8][8];
121 for (y = 0;y < 8;y++)
122 for (x = 0;x < 8;x++)
123 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
127 typedef struct embeddedpic_s
136 static const embeddedpic_t embeddedpics[] =
139 "gfx/prydoncursor001", 16, 16,
158 "ui/mousepointer", 16, 16,
177 "gfx/crosshair1", 16, 16,
196 "gfx/crosshair2", 16, 16,
215 "gfx/crosshair3", 16, 16,
234 "gfx/crosshair4", 16, 16,
253 "gfx/crosshair5", 8, 8,
264 "gfx/crosshair6", 2, 2,
269 "gfx/crosshair7", 16, 16,
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
292 const embeddedpic_t *p;
293 for (p = embeddedpics;p->name;p++)
294 if (!strcmp(name, p->name))
295 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
311 // FIXME: move this to client somehow
312 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
315 unsigned char *pixels;
318 unsigned char *lmpdata;
319 char lmpname[MAX_QPATH];
322 texflags = TEXF_ALPHA;
323 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
324 texflags |= TEXF_CLAMP;
325 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
326 texflags |= TEXF_COMPRESS;
328 // check whether the picture has already been cached
329 crc = CRC_Block((unsigned char *)path, strlen(path));
330 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
331 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
332 if (!strcmp (path, pic->name))
333 if(pic->texflags == texflags)
336 if (numcachepics == MAX_CACHED_PICS)
338 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
339 // FIXME: support NULL in callers?
340 return cachepics; // return the first one
342 pic = cachepics + (numcachepics++);
343 strlcpy (pic->name, path, sizeof(pic->name));
345 pic->chain = cachepichash[hashkey];
346 cachepichash[hashkey] = pic;
348 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
349 pic->tex = CL_GetDynTexture( path );
350 // if so, set the width/height, too
352 pic->width = R_TextureWidth(pic->tex);
353 pic->height = R_TextureHeight(pic->tex);
354 // we're done now (early-out)
358 pic->texflags = texflags;
359 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
361 // load a high quality image from disk if possible
362 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer, NULL);
363 if (pixels == NULL && !strncmp(path, "gfx/", 4))
364 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer, NULL);
367 pic->width = image_width;
368 pic->height = image_height;
370 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, -1, NULL);
374 pic->autoload = false;
375 // never compress the fallback images
376 pic->texflags &= ~TEXF_COMPRESS;
379 // now read the low quality version (wad or lmp file), and take the pic
380 // size from that even if we don't upload the texture, this way the pics
381 // show up the right size in the menu even if they were replaced with
382 // higher or lower resolution versions
383 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
384 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
386 if (developer_loading.integer)
387 Con_Printf("loading lump \"%s\"\n", path);
391 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
392 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
393 // if no high quality replacement image was found, upload the original low quality texture
395 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
399 else if ((lmpdata = W_GetLumpName (path + 4)))
401 if (developer_loading.integer)
402 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
404 if (!strcmp(path, "gfx/conchars"))
406 // conchars is a raw image and with color 0 as transparent instead of 255
409 // if no high quality replacement image was found, upload the original low quality texture
411 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
415 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
416 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
417 // if no high quality replacement image was found, upload the original low quality texture
419 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
428 else if (pic->tex == NULL)
430 // if it's not found on disk, generate an image
431 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
432 pic->width = R_TextureWidth(pic->tex);
433 pic->height = R_TextureHeight(pic->tex);
439 cachepic_t *Draw_CachePic (const char *path)
441 return Draw_CachePic_Flags (path, 0);
446 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
448 if (pic->autoload && !pic->tex)
450 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
451 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
452 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
453 if (pic->tex == NULL)
454 pic->tex = draw_generatepic(pic->name, true);
456 pic->lastusedframe = draw_frame;
460 void Draw_Frame(void)
464 static double nextpurgetime;
465 if (nextpurgetime > realtime)
467 nextpurgetime = realtime + 0.05;
468 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
470 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
472 R_FreeTexture(pic->tex);
479 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
484 crc = CRC_Block((unsigned char *)picname, strlen(picname));
485 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
486 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
487 if (!strcmp (picname, pic->name))
492 if (pic->tex && pic->width == width && pic->height == height)
494 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
502 if (numcachepics == MAX_CACHED_PICS)
504 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
505 // FIXME: support NULL in callers?
506 return cachepics; // return the first one
508 pic = cachepics + (numcachepics++);
509 strlcpy (pic->name, picname, sizeof(pic->name));
511 pic->chain = cachepichash[hashkey];
512 cachepichash[hashkey] = pic;
517 pic->height = height;
519 R_FreeTexture(pic->tex);
520 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, -1, NULL);
524 void Draw_FreePic(const char *picname)
529 // this doesn't really free the pic, but does free it's texture
530 crc = CRC_Block((unsigned char *)picname, strlen(picname));
531 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
532 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
534 if (!strcmp (picname, pic->name) && pic->tex)
536 R_FreeTexture(pic->tex);
545 static float snap_to_pixel_x(float x, float roundUpAt);
546 extern int con_linewidth; // to force rewrapping
547 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
551 char widthfile[MAX_QPATH];
553 fs_offset_t widthbufsize;
555 if(override || !fnt->texpath[0])
557 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
558 // load the cvars when the font is FIRST loader
559 fnt->settings.scale = scale;
560 fnt->settings.voffset = voffset;
561 fnt->settings.antialias = r_font_antialias.integer;
562 fnt->settings.hinting = r_font_hinting.integer;
563 fnt->settings.outline = r_font_postprocess_outline.value;
564 fnt->settings.blur = r_font_postprocess_blur.value;
565 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
566 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
567 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
570 if (fnt->settings.scale <= 0)
571 fnt->settings.scale = 1;
573 if(drawtexturepool == NULL)
574 return; // before gl_draw_start, so will be loaded later
578 // clear freetype font
579 Font_UnloadFont(fnt->ft2);
584 if(fnt->req_face != -1)
586 if(!Font_LoadFont(fnt->texpath, fnt))
587 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
590 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
591 if(fnt->tex == r_texture_notexture)
593 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
595 if (!fnt->fallbacks[i][0])
597 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
598 if(fnt->tex != r_texture_notexture)
601 if(fnt->tex == r_texture_notexture)
603 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
604 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
607 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
610 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
612 // unspecified width == 1 (base width)
613 for(ch = 0; ch < 256; ++ch)
614 fnt->width_of[ch] = 1;
616 // FIXME load "name.width", if it fails, fill all with 1
617 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
619 float extraspacing = 0;
620 const char *p = widthbuf;
625 if(!COM_ParseToken_Simple(&p, false, false))
643 fnt->width_of[ch] = atof(com_token) + extraspacing;
647 if(!strcmp(com_token, "extraspacing"))
649 if(!COM_ParseToken_Simple(&p, false, false))
651 extraspacing = atof(com_token);
653 else if(!strcmp(com_token, "scale"))
655 if(!COM_ParseToken_Simple(&p, false, false))
657 fnt->settings.scale = atof(com_token);
661 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
662 if(!COM_ParseToken_Simple(&p, false, false))
674 for (i = 0; i < MAX_FONT_SIZES; ++i)
676 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
679 for(ch = 0; ch < 256; ++ch)
680 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
684 maxwidth = fnt->width_of[0];
685 for(i = 1; i < 256; ++i)
686 maxwidth = max(maxwidth, fnt->width_of[i]);
687 fnt->maxwidth = maxwidth;
689 // fix up maxwidth for overlap
690 fnt->maxwidth *= fnt->settings.scale;
692 if(fnt == FONT_CONSOLE)
693 con_linewidth = -1; // rewrap console in next frame
696 extern cvar_t developer_font;
697 dp_font_t *FindFont(const char *title, qboolean allocate_new)
702 for(i = 0; i < dp_fonts.maxsize; ++i)
703 if(!strcmp(dp_fonts.f[i].title, title))
704 return &dp_fonts.f[i];
705 // if not found - try allocate
708 // find any font with empty title
709 for(i = 0; i < dp_fonts.maxsize; ++i)
711 if(!strcmp(dp_fonts.f[i].title, ""))
713 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
714 return &dp_fonts.f[i];
717 // if no any 'free' fonts - expand buffer
718 i = dp_fonts.maxsize;
719 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
720 if (developer_font.integer)
721 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
722 dp_fonts.f = Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
723 // register a font in first expanded slot
724 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
725 return &dp_fonts.f[i];
730 static float snap_to_pixel_x(float x, float roundUpAt)
732 float pixelpos = x * vid.width / vid_conwidth.value;
733 int snap = (int) pixelpos;
734 if (pixelpos - snap >= roundUpAt) ++snap;
735 return ((float)snap * vid_conwidth.value / vid.width);
737 x = (int)(x * vid.width / vid_conwidth.value);
738 x = (x * vid_conwidth.value / vid.width);
743 static float snap_to_pixel_y(float y, float roundUpAt)
745 float pixelpos = y * vid.height / vid_conheight.value;
746 int snap = (int) pixelpos;
747 if (pixelpos - snap > roundUpAt) ++snap;
748 return ((float)snap * vid_conheight.value / vid.height);
750 y = (int)(y * vid.height / vid_conheight.value);
751 y = (y * vid_conheight.value / vid.height);
756 static void LoadFont_f(void)
760 const char *filelist, *c, *cm;
761 float sz, scale, voffset;
762 char mainfont[MAX_QPATH];
766 Con_Printf("Available font commands:\n");
767 for(i = 0; i < dp_fonts.maxsize; ++i)
768 if (dp_fonts.f[i].title[0])
769 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
770 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
771 "can specify multiple fonts and faces\n"
772 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
773 "to load face 2 of the font gfx/vera-sans and use face 1\n"
774 "of gfx/fallback as fallback font.\n"
775 "You can also specify a list of font sizes to load, like this:\n"
776 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
777 "In many cases, 8 12 16 24 32 should be a good choice.\n"
779 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
780 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
784 f = FindFont(Cmd_Argv(1), true);
787 Con_Printf("font function not found\n");
792 filelist = "gfx/conchars";
794 filelist = Cmd_Argv(2);
796 memset(f->fallbacks, 0, sizeof(f->fallbacks));
797 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
799 // first font is handled "normally"
800 c = strchr(filelist, ':');
801 cm = strchr(filelist, ',');
802 if(c && (!cm || c < cm))
803 f->req_face = atoi(c+1);
810 if(!c || (c - filelist) > MAX_QPATH)
811 strlcpy(mainfont, filelist, sizeof(mainfont));
814 memcpy(mainfont, filelist, c - filelist);
815 mainfont[c - filelist] = 0;
818 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
820 c = strchr(filelist, ',');
826 c = strchr(filelist, ':');
827 cm = strchr(filelist, ',');
828 if(c && (!cm || c < cm))
829 f->fallback_faces[i] = atoi(c+1);
832 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
835 if(!c || (c-filelist) > MAX_QPATH)
837 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
841 memcpy(f->fallbacks[i], filelist, c - filelist);
842 f->fallbacks[i][c - filelist] = 0;
846 // for now: by default load only one size: the default size
848 for(i = 1; i < MAX_FONT_SIZES; ++i)
849 f->req_sizes[i] = -1;
855 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
858 if (!strcmp(Cmd_Argv(i), "scale"))
862 scale = atof(Cmd_Argv(i));
865 if (!strcmp(Cmd_Argv(i), "voffset"))
869 voffset = atof(Cmd_Argv(i));
872 // parse one of sizes
873 sz = atof(Cmd_Argv(i));
874 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
876 f->req_sizes[sizes] = sz;
882 LoadFont(true, mainfont, f, scale, voffset);
890 static void gl_draw_start(void)
893 drawtexturepool = R_AllocTexturePool();
896 memset(cachepichash, 0, sizeof(cachepichash));
900 // load default font textures
901 for(i = 0; i < dp_fonts.maxsize; ++i)
902 if (dp_fonts.f[i].title[0])
903 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
905 // draw the loading screen so people have something to see in the newly opened window
906 SCR_UpdateLoadingScreen(true);
909 static void gl_draw_shutdown(void)
913 R_FreeTexturePool(&drawtexturepool);
916 memset(cachepichash, 0, sizeof(cachepichash));
919 static void gl_draw_newmap(void)
924 void GL_Draw_Init (void)
928 Cvar_RegisterVariable(&r_font_postprocess_blur);
929 Cvar_RegisterVariable(&r_font_postprocess_outline);
930 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
931 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
932 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
933 Cvar_RegisterVariable(&r_font_hinting);
934 Cvar_RegisterVariable(&r_font_antialias);
935 Cvar_RegisterVariable(&r_textshadow);
936 Cvar_RegisterVariable(&r_textbrightness);
937 Cvar_RegisterVariable(&r_textcontrast);
939 // allocate fonts storage
940 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
941 dp_fonts.maxsize = MAX_FONTS;
942 dp_fonts.f = Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
943 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
945 // assign starting font names
946 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
947 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
948 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
949 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
950 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
951 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
952 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
953 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
954 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
955 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
956 if(!FONT_USER(i)->title[0])
957 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
959 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
960 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
963 void _DrawQ_Setup(void)
965 r_viewport_t viewport;
966 if (r_refdef.draw2dstage)
968 r_refdef.draw2dstage = true;
970 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);
971 R_SetViewport(&viewport);
972 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
973 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
974 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
975 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
976 R_EntityMatrix(&identitymatrix);
980 GL_PolygonOffset(0, 0);
984 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
987 qboolean r_draw2d_force = false;
988 static void _DrawQ_ProcessDrawFlag(int flags)
992 if(!r_draw2d.integer && !r_draw2d_force)
994 if(flags == DRAWFLAG_ADDITIVE)
995 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
996 else if(flags == DRAWFLAG_MODULATE)
997 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
998 else if(flags == DRAWFLAG_2XMODULATE)
999 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
1000 else if(flags == DRAWFLAG_SCREEN)
1001 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
1003 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1006 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1010 _DrawQ_ProcessDrawFlag(flags);
1011 if(!r_draw2d.integer && !r_draw2d_force)
1014 GL_Color(red, green, blue, alpha);
1016 R_Mesh_VertexPointer(floats, 0, 0);
1017 R_Mesh_ColorPointer(NULL, 0, 0);
1018 R_Mesh_ResetTextureState();
1024 height = pic->height;
1025 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1026 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1029 floats[12] = 0.0f;floats[13] = 0.0f;
1030 floats[14] = 1.0f;floats[15] = 0.0f;
1031 floats[16] = 1.0f;floats[17] = 1.0f;
1032 floats[18] = 0.0f;floats[19] = 1.0f;
1034 // AK07: lets be texel correct on the corners
1036 float horz_offset = 0.5f / pic->width;
1037 float vert_offset = 0.5f / pic->height;
1039 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1040 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1041 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1042 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1047 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1049 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1050 floats[0] = floats[9] = x;
1051 floats[1] = floats[4] = y;
1052 floats[3] = floats[6] = x + width;
1053 floats[7] = floats[10] = y + height;
1055 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1058 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)
1061 float af = DEG2RAD(-angle); // forward
1062 float ar = DEG2RAD(-angle + 90); // right
1063 float sinaf = sin(af);
1064 float cosaf = cos(af);
1065 float sinar = sin(ar);
1066 float cosar = cos(ar);
1068 _DrawQ_ProcessDrawFlag(flags);
1069 if(!r_draw2d.integer && !r_draw2d_force)
1072 GL_Color(red, green, blue, alpha);
1074 R_Mesh_VertexPointer(floats, 0, 0);
1075 R_Mesh_ColorPointer(NULL, 0, 0);
1076 R_Mesh_ResetTextureState();
1082 height = pic->height;
1083 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1084 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1086 floats[12] = 0.0f;floats[13] = 0.0f;
1087 floats[14] = 1.0f;floats[15] = 0.0f;
1088 floats[16] = 1.0f;floats[17] = 1.0f;
1089 floats[18] = 0.0f;floats[19] = 1.0f;
1092 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1094 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1097 floats[0] = x - cosaf*org_x - cosar*org_y;
1098 floats[1] = y - sinaf*org_x - sinar*org_y;
1101 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1102 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1105 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1106 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1109 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1110 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1112 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1115 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1119 _DrawQ_ProcessDrawFlag(flags);
1120 if(!r_draw2d.integer && !r_draw2d_force)
1123 GL_Color(red, green, blue, alpha);
1125 R_Mesh_VertexPointer(floats, 0, 0);
1126 R_Mesh_ColorPointer(NULL, 0, 0);
1127 R_Mesh_ResetTextureState();
1128 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1130 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1131 floats[0] = floats[9] = x;
1132 floats[1] = floats[4] = y;
1133 floats[3] = floats[6] = x + width;
1134 floats[7] = floats[10] = y + height;
1136 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1139 /// color tag printing
1140 static const vec4_t string_colors[] =
1143 // LordHavoc: why on earth is cyan before magenta in Quake3?
1144 // LordHavoc: note: Doom3 uses white for [0] and [7]
1145 {0.0, 0.0, 0.0, 1.0}, // black
1146 {1.0, 0.0, 0.0, 1.0}, // red
1147 {0.0, 1.0, 0.0, 1.0}, // green
1148 {1.0, 1.0, 0.0, 1.0}, // yellow
1149 {0.0, 0.0, 1.0, 1.0}, // blue
1150 {0.0, 1.0, 1.0, 1.0}, // cyan
1151 {1.0, 0.0, 1.0, 1.0}, // magenta
1152 {1.0, 1.0, 1.0, 1.0}, // white
1153 // [515]'s BX_COLOREDTEXT extension
1154 {1.0, 1.0, 1.0, 0.5}, // half transparent
1155 {0.5, 0.5, 0.5, 1.0} // half brightness
1156 // Black's color table
1157 //{1.0, 1.0, 1.0, 1.0},
1158 //{1.0, 0.0, 0.0, 1.0},
1159 //{0.0, 1.0, 0.0, 1.0},
1160 //{0.0, 0.0, 1.0, 1.0},
1161 //{1.0, 1.0, 0.0, 1.0},
1162 //{0.0, 1.0, 1.0, 1.0},
1163 //{1.0, 0.0, 1.0, 1.0},
1164 //{0.1, 0.1, 0.1, 1.0}
1167 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1169 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1171 float C = r_textcontrast.value;
1172 float B = r_textbrightness.value;
1173 if (colorindex & 0x10000) // that bit means RGB color
1175 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1176 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1177 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1178 color[3] = (colorindex & 0xf) / 15.0;
1181 Vector4Copy(string_colors[colorindex], color);
1182 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1185 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1186 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1190 // NOTE: this function always draws exactly one character if maxwidth <= 0
1191 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)
1193 const char *text_start = text;
1194 int colorindex = STRING_COLOR_DEFAULT;
1197 Uchar ch, mapch, nextch;
1198 Uchar prevch = 0; // used for kerning
1203 ft2_font_map_t *fontmap = NULL;
1204 ft2_font_map_t *map = NULL;
1205 //ft2_font_map_t *prevmap = NULL;
1206 ft2_font_t *ft2 = fnt->ft2;
1208 qboolean snap = true;
1209 qboolean least_one = false;
1210 float dw; // display w
1211 //float dh; // display h
1212 const float *width_of;
1219 // do this in the end
1220 w *= fnt->settings.scale;
1221 h *= fnt->settings.scale;
1223 // find the most fitting size:
1227 map_index = Font_IndexForSize(ft2, h, &w, &h);
1229 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1230 fontmap = Font_MapForIndex(ft2, map_index);
1239 if (!outcolor || *outcolor == -1)
1240 colorindex = STRING_COLOR_DEFAULT;
1242 colorindex = *outcolor;
1244 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1245 // ftbase_x = snap_to_pixel_x(0);
1250 maxwidth = -maxwidth;
1254 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1257 width_of = fontmap->width_of;
1259 width_of = fnt->width_of;
1261 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1264 nextch = ch = u8_getnchar(text, &text, bytes_left);
1265 i = text - text_start;
1268 if (ch == ' ' && !fontmap)
1270 if(!least_one || i0) // never skip the first character
1271 if(x + width_of[(int) ' '] * dw > maxwidth)
1274 break; // oops, can't draw this
1276 x += width_of[(int) ' '] * dw;
1279 // i points to the char after ^
1280 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1282 ch = *text; // colors are ascii, so no u8_ needed
1283 if (ch <= '9' && ch >= '0') // ^[0-9] found
1285 colorindex = ch - '0';
1290 // i points to the char after ^...
1291 // i+3 points to 3 in ^x123
1292 // i+3 == *maxlen would mean that char is missing
1293 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1295 // building colorindex...
1296 ch = tolower(text[1]);
1297 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1298 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1299 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1300 else tempcolorindex = 0;
1303 ch = tolower(text[2]);
1304 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1305 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1306 else tempcolorindex = 0;
1309 ch = tolower(text[3]);
1310 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1311 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1312 else tempcolorindex = 0;
1315 colorindex = tempcolorindex | 0xf;
1316 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1324 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1333 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1340 map = ft2_oldstyle_map;
1342 if(!least_one || i0) // never skip the first character
1343 if(x + width_of[ch] * dw > maxwidth)
1346 break; // oops, can't draw this
1348 x += width_of[ch] * dw;
1350 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1352 map = FontMap_FindForChar(fontmap, ch);
1355 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1361 mapch = ch - map->start;
1362 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1364 x += map->glyphs[mapch].advance_x * dw;
1373 *outcolor = colorindex;
1378 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)
1380 int shadow, colorindex = STRING_COLOR_DEFAULT;
1382 float x = startx, y, s, t, u, v, thisw;
1383 float *av, *at, *ac;
1386 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1387 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1388 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1389 Uchar ch, mapch, nextch;
1390 Uchar prevch = 0; // used for kerning
1393 //ft2_font_map_t *prevmap = NULL; // the previous map
1394 ft2_font_map_t *map = NULL; // the currently used map
1395 ft2_font_map_t *fontmap = NULL; // the font map for the size
1397 const char *text_start = text;
1399 ft2_font_t *ft2 = fnt->ft2;
1400 qboolean snap = true;
1404 const float *width_of;
1407 tw = R_TextureWidth(fnt->tex);
1408 th = R_TextureHeight(fnt->tex);
1416 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1417 w *= fnt->settings.scale;
1418 h *= fnt->settings.scale;
1423 map_index = Font_IndexForSize(ft2, h, &w, &h);
1425 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1426 fontmap = Font_MapForIndex(ft2, map_index);
1432 // draw the font at its baseline when using freetype
1434 ftbase_y = dh * (4.5/6.0);
1439 _DrawQ_ProcessDrawFlag(flags);
1440 if(!r_draw2d.integer && !r_draw2d_force)
1441 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1443 R_Mesh_ColorPointer(color4f, 0, 0);
1444 R_Mesh_ResetTextureState();
1446 R_Mesh_TexBind(0, fnt->tex);
1447 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1448 R_Mesh_VertexPointer(vertex3f, 0, 0);
1449 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1456 //ftbase_x = snap_to_pixel_x(ftbase_x);
1459 startx = snap_to_pixel_x(startx, 0.4);
1460 starty = snap_to_pixel_y(starty, 0.4);
1461 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1464 pix_x = vid.width / vid_conwidth.value;
1465 pix_y = vid.height / vid_conheight.value;
1468 width_of = fontmap->width_of;
1470 width_of = fnt->width_of;
1472 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1477 if (!outcolor || *outcolor == -1)
1478 colorindex = STRING_COLOR_DEFAULT;
1480 colorindex = *outcolor;
1482 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1489 x += r_textshadow.value * vid.width / vid_conwidth.value;
1490 y += r_textshadow.value * vid.height / vid_conheight.value;
1493 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1495 nextch = ch = u8_getnchar(text, &text, bytes_left);
1496 i = text - text_start;
1499 if (ch == ' ' && !fontmap)
1501 x += width_of[(int) ' '] * dw;
1504 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1506 ch = *text; // colors are ascii, so no u8_ needed
1507 if (ch <= '9' && ch >= '0') // ^[0-9] found
1509 colorindex = ch - '0';
1510 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1515 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1517 // building colorindex...
1518 ch = tolower(text[1]);
1519 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1520 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1521 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1522 else tempcolorindex = 0;
1525 ch = tolower(text[2]);
1526 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1527 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1528 else tempcolorindex = 0;
1531 ch = tolower(text[3]);
1532 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1533 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1534 else tempcolorindex = 0;
1537 colorindex = tempcolorindex | 0xf;
1538 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1539 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1540 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1548 else if (ch == STRING_COLOR_TAG)
1557 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1558 // this way we don't need to rebind fnt->tex for every old-style character
1559 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1562 x += 1.0/pix_x * r_textshadow.value;
1563 y += 1.0/pix_y * r_textshadow.value;
1565 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1573 if (map != ft2_oldstyle_map)
1577 // switching from freetype to non-freetype rendering
1578 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1584 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1585 map = ft2_oldstyle_map;
1589 //num = (unsigned char) text[i];
1590 //thisw = fnt->width_of[num];
1591 thisw = fnt->width_of[ch];
1592 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1593 s = (ch & 15)*0.0625f + (0.5f / tw);
1594 t = (ch >> 4)*0.0625f + (0.5f / th);
1595 u = 0.0625f * thisw - (1.0f / tw);
1596 v = 0.0625f - (1.0f / th);
1597 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1598 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1599 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1600 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1601 at[ 0] = s ; at[ 1] = t ;
1602 at[ 2] = s+u ; at[ 3] = t ;
1603 at[ 4] = s+u ; at[ 5] = t+v ;
1604 at[ 6] = s ; at[ 7] = t+v ;
1605 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1606 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1607 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1608 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1613 if (batchcount >= QUADELEMENTS_MAXQUADS)
1615 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1621 x += width_of[ch] * dw;
1623 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1625 // new charmap - need to render
1628 // we need a different character map, render what we currently have:
1629 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1636 map = FontMap_FindForChar(fontmap, ch);
1639 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1646 // this shouldn't happen
1651 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1654 mapch = ch - map->start;
1655 thisw = map->glyphs[mapch].advance_x;
1659 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1666 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1667 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1668 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1669 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1670 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1671 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1672 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1673 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1674 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1675 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1676 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1677 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1686 if (batchcount >= QUADELEMENTS_MAXQUADS)
1688 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1700 x -= 1.0/pix_x * r_textshadow.value;
1701 y -= 1.0/pix_y * r_textshadow.value;
1706 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1709 *outcolor = colorindex;
1711 // note: this relies on the proper text (not shadow) being drawn last
1715 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)
1717 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1720 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)
1722 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1725 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1727 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1730 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1732 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1737 // no ^xrgb management
1738 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1740 int color, numchars = 0;
1741 char *outputend2c = output2c + maxoutchars - 2;
1742 if (!outcolor || *outcolor == -1)
1743 color = STRING_COLOR_DEFAULT;
1747 maxreadchars = 1<<30;
1748 textend = text + maxreadchars;
1749 while (text != textend && *text)
1751 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1753 if (text[1] == STRING_COLOR_TAG)
1755 else if (text[1] >= '0' && text[1] <= '9')
1757 color = text[1] - '0';
1762 if (output2c >= outputend2c)
1764 *output2c++ = *text++;
1765 *output2c++ = color;
1768 output2c[0] = output2c[1] = 0;
1775 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)
1779 _DrawQ_ProcessDrawFlag(flags);
1780 if(!r_draw2d.integer && !r_draw2d_force)
1783 R_Mesh_VertexPointer(floats, 0, 0);
1784 R_Mesh_ColorPointer(floats + 20, 0, 0);
1785 R_Mesh_ResetTextureState();
1791 height = pic->height;
1792 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1793 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1794 floats[12] = s1;floats[13] = t1;
1795 floats[14] = s2;floats[15] = t2;
1796 floats[16] = s4;floats[17] = t4;
1797 floats[18] = s3;floats[19] = t3;
1800 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1802 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1803 floats[0] = floats[9] = x;
1804 floats[1] = floats[4] = y;
1805 floats[3] = floats[6] = x + width;
1806 floats[7] = floats[10] = y + height;
1807 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1808 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1809 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1810 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1812 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1815 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1817 _DrawQ_ProcessDrawFlag(flags);
1818 if(!r_draw2d.integer && !r_draw2d_force)
1821 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1822 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1823 R_Mesh_ResetTextureState();
1824 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1825 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1827 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1830 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1834 _DrawQ_ProcessDrawFlag(flags);
1835 if(!r_draw2d.integer && !r_draw2d_force)
1840 qglBegin(GL_LINE_LOOP);
1841 for (num = 0;num < mesh->num_vertices;num++)
1843 if (mesh->data_color4f)
1844 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]);
1845 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1851 //[515]: this is old, delete
1852 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1854 _DrawQ_ProcessDrawFlag(flags);
1855 if(!r_draw2d.integer && !r_draw2d_force)
1858 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1861 //qglLineWidth(width);CHECKGLERROR
1863 GL_Color(r,g,b,alpha);
1866 qglVertex2f(x1, y1);
1867 qglVertex2f(x2, y2);
1872 void DrawQ_SetClipArea(float x, float y, float width, float height)
1877 // We have to convert the con coords into real coords
1878 // OGL uses top to bottom
1879 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1880 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1881 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1882 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1883 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1885 GL_ScissorTest(true);
1888 void DrawQ_ResetClipArea(void)
1891 GL_ScissorTest(false);
1894 void DrawQ_Finish(void)
1896 r_refdef.draw2dstage = false;
1899 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1900 void R_DrawGamma(void)
1903 switch(vid.renderpath)
1905 case RENDERPATH_GL20:
1906 case RENDERPATH_CGGL:
1907 if (vid_usinghwgamma || v_glslgamma.integer)
1910 case RENDERPATH_GL13:
1911 case RENDERPATH_GL11:
1912 if (vid_usinghwgamma)
1916 // all the blends ignore depth
1917 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1918 R_Mesh_ColorPointer(NULL, 0, 0);
1919 R_Mesh_ResetTextureState();
1920 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1922 GL_DepthRange(0, 1);
1923 GL_PolygonOffset(0, 0);
1924 GL_DepthTest(false);
1925 if (v_color_enable.integer)
1927 c[0] = v_color_white_r.value;
1928 c[1] = v_color_white_g.value;
1929 c[2] = v_color_white_b.value;
1932 c[0] = c[1] = c[2] = v_contrast.value;
1933 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1935 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1936 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1938 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1939 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1940 VectorScale(c, 0.5, c);
1943 if (v_color_enable.integer)
1945 c[0] = v_color_black_r.value;
1946 c[1] = v_color_black_g.value;
1947 c[2] = v_color_black_b.value;
1950 c[0] = c[1] = c[2] = v_brightness.value;
1951 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1953 GL_BlendFunc(GL_ONE, GL_ONE);
1954 GL_Color(c[0], c[1], c[2], 1);
1955 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);