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));
556 // load the cvars when the font is FIRST loaded
557 fnt->settings.antialias = r_font_antialias.integer;
558 fnt->settings.hinting = r_font_hinting.integer;
559 fnt->settings.outline = r_font_postprocess_outline.value;
560 fnt->settings.blur = r_font_postprocess_blur.value;
561 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
562 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
563 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
566 if(drawtexturepool == NULL)
567 return; // before gl_draw_start, so will be loaded later
571 // clear freetype font
572 Font_UnloadFont(fnt->ft2);
577 if(fnt->req_face != -1)
579 if(!Font_LoadFont(fnt->texpath, fnt))
580 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
583 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
584 if(fnt->tex == r_texture_notexture)
586 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
588 if (!fnt->fallbacks[i][0])
590 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
591 if(fnt->tex != r_texture_notexture)
594 if(fnt->tex == r_texture_notexture)
596 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
597 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
600 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
603 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
605 // unspecified width == 1 (base width)
606 for(i = 1; i < 256; ++i)
607 fnt->width_of[i] = 1;
609 // FIXME load "name.width", if it fails, fill all with 1
610 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
612 float extraspacing = 0;
613 const char *p = widthbuf;
618 if(!COM_ParseToken_Simple(&p, false, false))
636 fnt->width_of[ch] = atof(com_token) + extraspacing;
639 for (i = 0; i < MAX_FONT_SIZES; ++i)
641 //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
642 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
645 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
651 if(!strcmp(com_token, "extraspacing"))
653 if(!COM_ParseToken_Simple(&p, false, false))
655 extraspacing = atof(com_token);
657 else if(!strcmp(com_token, "scale"))
659 if(!COM_ParseToken_Simple(&p, false, false))
661 scale = atof(com_token);
665 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
666 if(!COM_ParseToken_Simple(&p, false, false))
676 maxwidth = fnt->width_of[1];
677 for(i = 2; i < 256; ++i)
678 maxwidth = max(maxwidth, fnt->width_of[i]);
679 fnt->maxwidth = maxwidth;
681 // fix up maxwidth for overlap
682 fnt->maxwidth *= scale;
684 fnt->voffset = voffset;
686 if(fnt == FONT_CONSOLE)
687 con_linewidth = -1; // rewrap console in next frame
690 extern cvar_t developer_font;
691 dp_font_t *FindFont(const char *title, qboolean allocate_new)
696 for(i = 0; i < dp_fonts.maxsize; ++i)
697 if(!strcmp(dp_fonts.f[i].title, title))
698 return &dp_fonts.f[i];
699 // if not found - try allocate
702 // find any font with empty title
703 for(i = 0; i < dp_fonts.maxsize; ++i)
705 if(!strcmp(dp_fonts.f[i].title, ""))
707 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
708 return &dp_fonts.f[i];
711 // if no any 'free' fonts - expand buffer
712 i = dp_fonts.maxsize;
713 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
714 if (developer_font.integer)
715 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
716 dp_fonts.f = Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
717 // register a font in first expanded slot
718 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
719 return &dp_fonts.f[i];
724 static float snap_to_pixel_x(float x, float roundUpAt)
726 float pixelpos = x * vid.width / vid_conwidth.value;
727 int snap = (int) pixelpos;
728 if (pixelpos - snap >= roundUpAt) ++snap;
729 return ((float)snap * vid_conwidth.value / vid.width);
731 x = (int)(x * vid.width / vid_conwidth.value);
732 x = (x * vid_conwidth.value / vid.width);
737 static float snap_to_pixel_y(float y, float roundUpAt)
739 float pixelpos = y * vid.height / vid_conheight.value;
740 int snap = (int) pixelpos;
741 if (pixelpos - snap > roundUpAt) ++snap;
742 return ((float)snap * vid_conheight.value / vid.height);
744 y = (int)(y * vid.height / vid_conheight.value);
745 y = (y * vid_conheight.value / vid.height);
750 static void LoadFont_f(void)
754 const char *filelist, *c, *cm;
756 char mainfont[MAX_QPATH];
760 Con_Printf("Available font commands:\n");
761 for(i = 0; i < dp_fonts.maxsize; ++i)
762 if (dp_fonts.f[i].title[0])
763 Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts.f[i].title);
764 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
765 "can specify multiple fonts and faces\n"
766 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
767 "to load face 2 of the font gfx/vera-sans and use face 1\n"
768 "of gfx/fallback as fallback font.\n"
769 "You can also specify a list of font sizes to load, like this:\n"
770 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
771 "In many cases, 8 12 16 24 32 should be a good choice.\n"
775 f = FindFont(Cmd_Argv(1), true);
778 Con_Printf("font function not found\n");
783 filelist = "gfx/conchars";
785 filelist = Cmd_Argv(2);
787 memset(f->fallbacks, 0, sizeof(f->fallbacks));
788 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
790 // first font is handled "normally"
791 c = strchr(filelist, ':');
792 cm = strchr(filelist, ',');
793 if(c && (!cm || c < cm))
794 f->req_face = atoi(c+1);
801 if(!c || (c - filelist) > MAX_QPATH)
802 strlcpy(mainfont, filelist, sizeof(mainfont));
805 memcpy(mainfont, filelist, c - filelist);
806 mainfont[c - filelist] = 0;
809 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
811 c = strchr(filelist, ',');
817 c = strchr(filelist, ':');
818 cm = strchr(filelist, ',');
819 if(c && (!cm || c < cm))
820 f->fallback_faces[i] = atoi(c+1);
823 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
826 if(!c || (c-filelist) > MAX_QPATH)
828 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
832 memcpy(f->fallbacks[i], filelist, c - filelist);
833 f->fallbacks[i][c - filelist] = 0;
837 // for now: by default load only one size: the default size
839 for(i = 1; i < MAX_FONT_SIZES; ++i)
840 f->req_sizes[i] = -1;
844 for(i = 0; i < Cmd_Argc()-3; ++i)
846 sz = atof(Cmd_Argv(i+3));
847 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
848 f->req_sizes[i] = sz;
852 LoadFont(true, mainfont, f, 1, 0);
860 static void gl_draw_start(void)
863 drawtexturepool = R_AllocTexturePool();
866 memset(cachepichash, 0, sizeof(cachepichash));
870 // load default font textures
871 for(i = 0; i < dp_fonts.maxsize; ++i)
872 if (dp_fonts.f[i].title[0])
873 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
875 // draw the loading screen so people have something to see in the newly opened window
876 SCR_UpdateLoadingScreen(true);
879 static void gl_draw_shutdown(void)
883 R_FreeTexturePool(&drawtexturepool);
886 memset(cachepichash, 0, sizeof(cachepichash));
889 static void gl_draw_newmap(void)
894 void GL_Draw_Init (void)
898 Cvar_RegisterVariable(&r_font_postprocess_blur);
899 Cvar_RegisterVariable(&r_font_postprocess_outline);
900 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
901 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
902 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
903 Cvar_RegisterVariable(&r_font_hinting);
904 Cvar_RegisterVariable(&r_font_antialias);
905 Cvar_RegisterVariable(&r_textshadow);
906 Cvar_RegisterVariable(&r_textbrightness);
907 Cvar_RegisterVariable(&r_textcontrast);
909 // allocate fonts storage
910 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
911 dp_fonts.maxsize = MAX_FONTS;
912 dp_fonts.f = Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
913 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
915 // assign starting font names
916 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
917 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
918 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
919 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
920 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
921 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
922 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
923 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
924 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
925 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
926 if(!FONT_USER(i)->title[0])
927 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
929 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
930 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
933 void _DrawQ_Setup(void)
935 r_viewport_t viewport;
936 if (r_refdef.draw2dstage)
938 r_refdef.draw2dstage = true;
940 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);
941 R_SetViewport(&viewport);
942 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
943 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
944 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
945 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
946 R_EntityMatrix(&identitymatrix);
950 GL_PolygonOffset(0, 0);
954 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
957 static void _DrawQ_ProcessDrawFlag(int flags)
961 if(flags == DRAWFLAG_ADDITIVE)
962 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
963 else if(flags == DRAWFLAG_MODULATE)
964 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
965 else if(flags == DRAWFLAG_2XMODULATE)
966 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
967 else if(flags == DRAWFLAG_SCREEN)
968 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
970 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
973 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
977 _DrawQ_ProcessDrawFlag(flags);
979 R_Mesh_ResetTextureState();
980 floats[12] = 0.0f;floats[13] = 0.0f;
981 floats[14] = 1.0f;floats[15] = 0.0f;
982 floats[16] = 1.0f;floats[17] = 1.0f;
983 floats[18] = 0.0f;floats[19] = 1.0f;
984 floats[20] = floats[24] = floats[28] = floats[32] = red;
985 floats[21] = floats[25] = floats[29] = floats[33] = green;
986 floats[22] = floats[26] = floats[30] = floats[34] = blue;
987 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
993 height = pic->height;
994 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
997 // AK07: lets be texel correct on the corners
999 float horz_offset = 0.5f / pic->width;
1000 float vert_offset = 0.5f / pic->height;
1002 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1003 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1004 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1005 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1010 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1012 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1013 floats[0] = floats[9] = x;
1014 floats[1] = floats[4] = y;
1015 floats[3] = floats[6] = x + width;
1016 floats[7] = floats[10] = y + height;
1018 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1019 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1022 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)
1025 float af = DEG2RAD(-angle); // forward
1026 float ar = DEG2RAD(-angle + 90); // right
1027 float sinaf = sin(af);
1028 float cosaf = cos(af);
1029 float sinar = sin(ar);
1030 float cosar = cos(ar);
1032 _DrawQ_ProcessDrawFlag(flags);
1034 R_Mesh_ResetTextureState();
1040 height = pic->height;
1041 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1044 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1046 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1049 floats[0] = x - cosaf*org_x - cosar*org_y;
1050 floats[1] = y - sinaf*org_x - sinar*org_y;
1053 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1054 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1057 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1058 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1061 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1062 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1064 floats[12] = 0.0f;floats[13] = 0.0f;
1065 floats[14] = 1.0f;floats[15] = 0.0f;
1066 floats[16] = 1.0f;floats[17] = 1.0f;
1067 floats[18] = 0.0f;floats[19] = 1.0f;
1068 floats[20] = floats[24] = floats[28] = floats[32] = red;
1069 floats[21] = floats[25] = floats[29] = floats[33] = green;
1070 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1071 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1073 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1074 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1077 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1081 _DrawQ_ProcessDrawFlag(flags);
1083 R_Mesh_ResetTextureState();
1084 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1086 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1087 floats[0] = floats[9] = x;
1088 floats[1] = floats[4] = y;
1089 floats[3] = floats[6] = x + width;
1090 floats[7] = floats[10] = y + height;
1091 floats[12] = 0.0f;floats[13] = 0.0f;
1092 floats[14] = 1.0f;floats[15] = 0.0f;
1093 floats[16] = 1.0f;floats[17] = 1.0f;
1094 floats[18] = 0.0f;floats[19] = 1.0f;
1095 floats[20] = floats[24] = floats[28] = floats[32] = red;
1096 floats[21] = floats[25] = floats[29] = floats[33] = green;
1097 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1098 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1100 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1101 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1104 /// color tag printing
1105 static const vec4_t string_colors[] =
1108 // LordHavoc: why on earth is cyan before magenta in Quake3?
1109 // LordHavoc: note: Doom3 uses white for [0] and [7]
1110 {0.0, 0.0, 0.0, 1.0}, // black
1111 {1.0, 0.0, 0.0, 1.0}, // red
1112 {0.0, 1.0, 0.0, 1.0}, // green
1113 {1.0, 1.0, 0.0, 1.0}, // yellow
1114 {0.0, 0.0, 1.0, 1.0}, // blue
1115 {0.0, 1.0, 1.0, 1.0}, // cyan
1116 {1.0, 0.0, 1.0, 1.0}, // magenta
1117 {1.0, 1.0, 1.0, 1.0}, // white
1118 // [515]'s BX_COLOREDTEXT extension
1119 {1.0, 1.0, 1.0, 0.5}, // half transparent
1120 {0.5, 0.5, 0.5, 1.0} // half brightness
1121 // Black's color table
1122 //{1.0, 1.0, 1.0, 1.0},
1123 //{1.0, 0.0, 0.0, 1.0},
1124 //{0.0, 1.0, 0.0, 1.0},
1125 //{0.0, 0.0, 1.0, 1.0},
1126 //{1.0, 1.0, 0.0, 1.0},
1127 //{0.0, 1.0, 1.0, 1.0},
1128 //{1.0, 0.0, 1.0, 1.0},
1129 //{0.1, 0.1, 0.1, 1.0}
1132 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1134 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1136 float C = r_textcontrast.value;
1137 float B = r_textbrightness.value;
1138 if (colorindex & 0x10000) // that bit means RGB color
1140 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1141 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1142 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1143 color[3] = (colorindex & 0xf) / 15.0;
1146 Vector4Copy(string_colors[colorindex], color);
1147 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1150 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1151 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1155 // NOTE: this function always draws exactly one character if maxwidth <= 0
1156 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)
1158 const char *text_start = text;
1159 int colorindex = STRING_COLOR_DEFAULT;
1162 Uchar ch, mapch, nextch;
1163 Uchar prevch = 0; // used for kerning
1168 ft2_font_map_t *fontmap = NULL;
1169 ft2_font_map_t *map = NULL;
1170 //ft2_font_map_t *prevmap = NULL;
1171 ft2_font_t *ft2 = fnt->ft2;
1173 qboolean snap = true;
1174 qboolean least_one = false;
1175 float dw; // display w
1176 //float dh; // display h
1177 const float *width_of;
1184 // do this in the end
1188 // find the most fitting size:
1192 map_index = Font_IndexForSize(ft2, h, &w, &h);
1194 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1195 fontmap = Font_MapForIndex(ft2, map_index);
1204 if (!outcolor || *outcolor == -1)
1205 colorindex = STRING_COLOR_DEFAULT;
1207 colorindex = *outcolor;
1209 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1210 // ftbase_x = snap_to_pixel_x(0);
1215 maxwidth = -maxwidth;
1219 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1222 width_of = fontmap->width_of;
1224 width_of = fnt->width_of;
1226 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1229 nextch = ch = u8_getnchar(text, &text, bytes_left);
1230 i = text - text_start;
1233 if (ch == ' ' && !fontmap)
1235 if(!least_one || i0) // never skip the first character
1236 if(x + width_of[(int) ' '] * dw > maxwidth)
1239 break; // oops, can't draw this
1241 x += width_of[(int) ' '] * dw;
1244 // i points to the char after ^
1245 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1247 ch = *text; // colors are ascii, so no u8_ needed
1248 if (ch <= '9' && ch >= '0') // ^[0-9] found
1250 colorindex = ch - '0';
1255 // i points to the char after ^...
1256 // i+3 points to 3 in ^x123
1257 // i+3 == *maxlen would mean that char is missing
1258 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1260 // building colorindex...
1261 ch = tolower(text[1]);
1262 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1263 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1264 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1265 else tempcolorindex = 0;
1268 ch = tolower(text[2]);
1269 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1270 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1271 else tempcolorindex = 0;
1274 ch = tolower(text[3]);
1275 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1276 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1277 else tempcolorindex = 0;
1280 colorindex = tempcolorindex | 0xf;
1281 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1289 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1298 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1305 map = ft2_oldstyle_map;
1307 if(!least_one || i0) // never skip the first character
1308 if(x + width_of[ch] * dw > maxwidth)
1311 break; // oops, can't draw this
1313 x += width_of[ch] * dw;
1315 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1317 map = FontMap_FindForChar(fontmap, ch);
1320 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1326 mapch = ch - map->start;
1327 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1329 x += map->glyphs[mapch].advance_x * dw;
1338 *outcolor = colorindex;
1343 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)
1345 int shadow, colorindex = STRING_COLOR_DEFAULT;
1347 float x = startx, y, s, t, u, v, thisw;
1348 float *av, *at, *ac;
1351 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1352 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1353 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1354 Uchar ch, mapch, nextch;
1355 Uchar prevch = 0; // used for kerning
1358 //ft2_font_map_t *prevmap = NULL; // the previous map
1359 ft2_font_map_t *map = NULL; // the currently used map
1360 ft2_font_map_t *fontmap = NULL; // the font map for the size
1362 const char *text_start = text;
1364 ft2_font_t *ft2 = fnt->ft2;
1365 qboolean snap = true;
1369 const float *width_of;
1372 tw = R_TextureWidth(fnt->tex);
1373 th = R_TextureHeight(fnt->tex);
1381 starty -= (fnt->scale - 1) * h * 0.5 - fnt->voffset*h; // center & offset
1388 map_index = Font_IndexForSize(ft2, h, &w, &h);
1390 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1391 fontmap = Font_MapForIndex(ft2, map_index);
1397 // draw the font at its baseline when using freetype
1399 ftbase_y = dh * (4.5/6.0);
1404 _DrawQ_ProcessDrawFlag(flags);
1406 R_Mesh_ResetTextureState();
1408 R_Mesh_TexBind(0, fnt->tex);
1409 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1416 //ftbase_x = snap_to_pixel_x(ftbase_x);
1419 startx = snap_to_pixel_x(startx, 0.4);
1420 starty = snap_to_pixel_y(starty, 0.4);
1421 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1424 pix_x = vid.width / vid_conwidth.value;
1425 pix_y = vid.height / vid_conheight.value;
1428 width_of = fontmap->width_of;
1430 width_of = fnt->width_of;
1432 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1437 if (!outcolor || *outcolor == -1)
1438 colorindex = STRING_COLOR_DEFAULT;
1440 colorindex = *outcolor;
1442 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1449 x += r_textshadow.value * vid.width / vid_conwidth.value;
1450 y += r_textshadow.value * vid.height / vid_conheight.value;
1453 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1455 nextch = ch = u8_getnchar(text, &text, bytes_left);
1456 i = text - text_start;
1459 if (ch == ' ' && !fontmap)
1461 x += width_of[(int) ' '] * dw;
1464 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1466 ch = *text; // colors are ascii, so no u8_ needed
1467 if (ch <= '9' && ch >= '0') // ^[0-9] found
1469 colorindex = ch - '0';
1470 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1475 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1477 // building colorindex...
1478 ch = tolower(text[1]);
1479 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1480 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1481 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1482 else tempcolorindex = 0;
1485 ch = tolower(text[2]);
1486 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1487 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1488 else tempcolorindex = 0;
1491 ch = tolower(text[3]);
1492 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1493 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1494 else tempcolorindex = 0;
1497 colorindex = tempcolorindex | 0xf;
1498 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1499 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1500 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1508 else if (ch == STRING_COLOR_TAG)
1517 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1518 // this way we don't need to rebind fnt->tex for every old-style character
1519 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1522 x += 1.0/pix_x * r_textshadow.value;
1523 y += 1.0/pix_y * r_textshadow.value;
1525 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1533 if (map != ft2_oldstyle_map)
1537 // switching from freetype to non-freetype rendering
1538 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1539 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1545 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1546 map = ft2_oldstyle_map;
1550 //num = (unsigned char) text[i];
1551 //thisw = fnt->width_of[num];
1552 thisw = fnt->width_of[ch];
1553 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1554 s = (ch & 15)*0.0625f + (0.5f / tw);
1555 t = (ch >> 4)*0.0625f + (0.5f / th);
1556 u = 0.0625f * thisw - (1.0f / tw);
1557 v = 0.0625f - (1.0f / th);
1558 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1559 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1560 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1561 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1562 at[ 0] = s ; at[ 1] = t ;
1563 at[ 2] = s+u ; at[ 3] = t ;
1564 at[ 4] = s+u ; at[ 5] = t+v ;
1565 at[ 6] = s ; at[ 7] = t+v ;
1566 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1567 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1568 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1569 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1574 if (batchcount >= QUADELEMENTS_MAXQUADS)
1576 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1577 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1583 x += width_of[ch] * dw;
1585 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1587 // new charmap - need to render
1590 // we need a different character map, render what we currently have:
1591 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1592 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1599 map = FontMap_FindForChar(fontmap, ch);
1602 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1609 // this shouldn't happen
1614 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1617 mapch = ch - map->start;
1618 thisw = map->glyphs[mapch].advance_x;
1622 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1629 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1630 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1631 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1632 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1633 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1634 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1635 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1636 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1637 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1638 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1639 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1640 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1649 if (batchcount >= QUADELEMENTS_MAXQUADS)
1651 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1652 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1664 x -= 1.0/pix_x * r_textshadow.value;
1665 y -= 1.0/pix_y * r_textshadow.value;
1671 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1672 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1676 *outcolor = colorindex;
1678 // note: this relies on the proper text (not shadow) being drawn last
1682 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)
1684 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1687 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)
1689 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1692 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1694 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1697 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1699 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1704 // no ^xrgb management
1705 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1707 int color, numchars = 0;
1708 char *outputend2c = output2c + maxoutchars - 2;
1709 if (!outcolor || *outcolor == -1)
1710 color = STRING_COLOR_DEFAULT;
1714 maxreadchars = 1<<30;
1715 textend = text + maxreadchars;
1716 while (text != textend && *text)
1718 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1720 if (text[1] == STRING_COLOR_TAG)
1722 else if (text[1] >= '0' && text[1] <= '9')
1724 color = text[1] - '0';
1729 if (output2c >= outputend2c)
1731 *output2c++ = *text++;
1732 *output2c++ = color;
1735 output2c[0] = output2c[1] = 0;
1742 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)
1746 _DrawQ_ProcessDrawFlag(flags);
1748 R_Mesh_ResetTextureState();
1754 height = pic->height;
1755 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1758 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1760 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1761 floats[0] = floats[9] = x;
1762 floats[1] = floats[4] = y;
1763 floats[3] = floats[6] = x + width;
1764 floats[7] = floats[10] = y + height;
1765 floats[12] = s1;floats[13] = t1;
1766 floats[14] = s2;floats[15] = t2;
1767 floats[16] = s4;floats[17] = t4;
1768 floats[18] = s3;floats[19] = t3;
1769 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1770 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1771 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1772 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1774 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1775 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1778 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1780 _DrawQ_ProcessDrawFlag(flags);
1782 R_Mesh_ResetTextureState();
1783 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1785 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1786 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1789 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1793 _DrawQ_ProcessDrawFlag(flags);
1797 qglBegin(GL_LINE_LOOP);
1798 for (num = 0;num < mesh->num_vertices;num++)
1800 if (mesh->data_color4f)
1801 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]);
1802 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1808 //[515]: this is old, delete
1809 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1811 _DrawQ_ProcessDrawFlag(flags);
1813 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1816 //qglLineWidth(width);CHECKGLERROR
1818 GL_Color(r,g,b,alpha);
1821 qglVertex2f(x1, y1);
1822 qglVertex2f(x2, y2);
1827 void DrawQ_SetClipArea(float x, float y, float width, float height)
1832 // We have to convert the con coords into real coords
1833 // OGL uses top to bottom
1834 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1835 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1836 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1837 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1838 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1840 GL_ScissorTest(true);
1843 void DrawQ_ResetClipArea(void)
1846 GL_ScissorTest(false);
1849 void DrawQ_Finish(void)
1851 r_refdef.draw2dstage = false;
1854 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1855 void R_DrawGamma(void)
1858 switch(vid.renderpath)
1860 case RENDERPATH_GL20:
1861 case RENDERPATH_CGGL:
1862 if (vid_usinghwgamma || v_glslgamma.integer)
1865 case RENDERPATH_GL13:
1866 case RENDERPATH_GL11:
1867 if (vid_usinghwgamma)
1871 // all the blends ignore depth
1872 R_Mesh_ResetTextureState();
1873 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1875 GL_DepthRange(0, 1);
1876 GL_PolygonOffset(0, 0);
1877 GL_DepthTest(false);
1878 if (v_color_enable.integer)
1880 c[0] = v_color_white_r.value;
1881 c[1] = v_color_white_g.value;
1882 c[2] = v_color_white_b.value;
1885 c[0] = c[1] = c[2] = v_contrast.value;
1886 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1888 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1889 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1891 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1892 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1893 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1894 VectorScale(c, 0.5, c);
1897 if (v_color_enable.integer)
1899 c[0] = v_color_black_r.value;
1900 c[1] = v_color_black_g.value;
1901 c[2] = v_color_black_b.value;
1904 c[0] = c[1] = c[2] = v_brightness.value;
1905 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1907 GL_BlendFunc(GL_ONE, GL_ONE);
1908 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1909 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1910 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);