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];
323 texflags = TEXF_ALPHA;
324 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
325 texflags |= TEXF_CLAMP;
326 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
327 texflags |= TEXF_COMPRESS;
329 // check whether the picture has already been cached
330 crc = CRC_Block((unsigned char *)path, strlen(path));
331 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
332 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
333 if (!strcmp (path, pic->name))
334 if(!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
336 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
339 pic->autoload = false; // persist it
341 goto reload; // load it below, and then persist
346 if (numcachepics == MAX_CACHED_PICS)
348 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
349 // FIXME: support NULL in callers?
350 return cachepics; // return the first one
352 pic = cachepics + (numcachepics++);
353 strlcpy (pic->name, path, sizeof(pic->name));
355 pic->chain = cachepichash[hashkey];
356 cachepichash[hashkey] = pic;
359 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
360 pic->tex = CL_GetDynTexture( path );
361 // if so, set the width/height, too
363 pic->width = R_TextureWidth(pic->tex);
364 pic->height = R_TextureHeight(pic->tex);
365 // we're done now (early-out)
369 pic->hasalpha = true; // assume alpha unless we know it has none
370 pic->texflags = texflags;
371 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
373 // load a high quality image from disk if possible
374 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer, NULL);
375 if (pixels == NULL && !strncmp(path, "gfx/", 4))
376 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer, NULL);
379 pic->hasalpha = false;
380 if (pic->texflags & TEXF_ALPHA)
382 for (j = 3;j < image_width * image_height * 4;j += 4)
386 pic->hasalpha = true;
392 pic->width = image_width;
393 pic->height = image_height;
395 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
399 pic->autoload = false;
400 // never compress the fallback images
401 pic->texflags &= ~TEXF_COMPRESS;
404 // now read the low quality version (wad or lmp file), and take the pic
405 // size from that even if we don't upload the texture, this way the pics
406 // show up the right size in the menu even if they were replaced with
407 // higher or lower resolution versions
408 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
409 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
411 if (developer_loading.integer)
412 Con_Printf("loading lump \"%s\"\n", path);
416 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
417 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
418 // if no high quality replacement image was found, upload the original low quality texture
420 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
424 else if ((lmpdata = W_GetLumpName (path + 4)))
426 if (developer_loading.integer)
427 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
429 if (!strcmp(path, "gfx/conchars"))
431 // conchars is a raw image and with color 0 as transparent instead of 255
434 // if no high quality replacement image was found, upload the original low quality texture
436 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
440 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
441 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
442 // if no high quality replacement image was found, upload the original low quality texture
444 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
453 else if (pic->tex == NULL)
455 // if it's not found on disk, generate an image
456 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
457 pic->width = R_TextureWidth(pic->tex);
458 pic->height = R_TextureHeight(pic->tex);
464 cachepic_t *Draw_CachePic (const char *path)
466 return Draw_CachePic_Flags (path, 0); // default to persistent!
471 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
473 if (pic->autoload && !pic->tex)
475 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
476 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
477 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
478 if (pic->tex == NULL)
479 pic->tex = draw_generatepic(pic->name, true);
481 pic->lastusedframe = draw_frame;
485 void Draw_Frame(void)
489 static double nextpurgetime;
490 if (nextpurgetime > realtime)
492 nextpurgetime = realtime + 0.05;
493 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
495 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
497 R_FreeTexture(pic->tex);
504 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
509 crc = CRC_Block((unsigned char *)picname, strlen(picname));
510 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
511 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
512 if (!strcmp (picname, pic->name))
517 if (pic->tex && pic->width == width && pic->height == height)
519 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
527 if (numcachepics == MAX_CACHED_PICS)
529 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
530 // FIXME: support NULL in callers?
531 return cachepics; // return the first one
533 pic = cachepics + (numcachepics++);
534 strlcpy (pic->name, picname, sizeof(pic->name));
536 pic->chain = cachepichash[hashkey];
537 cachepichash[hashkey] = pic;
542 pic->height = height;
544 R_FreeTexture(pic->tex);
545 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
549 void Draw_FreePic(const char *picname)
554 // this doesn't really free the pic, but does free it's texture
555 crc = CRC_Block((unsigned char *)picname, strlen(picname));
556 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
557 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
559 if (!strcmp (picname, pic->name) && pic->tex)
561 R_FreeTexture(pic->tex);
570 static float snap_to_pixel_x(float x, float roundUpAt);
571 extern int con_linewidth; // to force rewrapping
572 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
576 char widthfile[MAX_QPATH];
578 fs_offset_t widthbufsize;
580 if(override || !fnt->texpath[0])
582 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
583 // load the cvars when the font is FIRST loader
584 fnt->settings.scale = scale;
585 fnt->settings.voffset = voffset;
586 fnt->settings.antialias = r_font_antialias.integer;
587 fnt->settings.hinting = r_font_hinting.integer;
588 fnt->settings.outline = r_font_postprocess_outline.value;
589 fnt->settings.blur = r_font_postprocess_blur.value;
590 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
591 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
592 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
595 if (fnt->settings.scale <= 0)
596 fnt->settings.scale = 1;
598 if(drawtexturepool == NULL)
599 return; // before gl_draw_start, so will be loaded later
603 // clear freetype font
604 Font_UnloadFont(fnt->ft2);
609 if(fnt->req_face != -1)
611 if(!Font_LoadFont(fnt->texpath, fnt))
612 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
615 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
616 if(fnt->tex == r_texture_notexture)
618 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
620 if (!fnt->fallbacks[i][0])
622 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
623 if(fnt->tex != r_texture_notexture)
626 if(fnt->tex == r_texture_notexture)
628 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
629 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
632 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
635 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
637 // unspecified width == 1 (base width)
638 for(ch = 0; ch < 256; ++ch)
639 fnt->width_of[ch] = 1;
641 // FIXME load "name.width", if it fails, fill all with 1
642 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
644 float extraspacing = 0;
645 const char *p = widthbuf;
650 if(!COM_ParseToken_Simple(&p, false, false))
668 fnt->width_of[ch] = atof(com_token) + extraspacing;
672 if(!strcmp(com_token, "extraspacing"))
674 if(!COM_ParseToken_Simple(&p, false, false))
676 extraspacing = atof(com_token);
678 else if(!strcmp(com_token, "scale"))
680 if(!COM_ParseToken_Simple(&p, false, false))
682 fnt->settings.scale = atof(com_token);
686 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
687 if(!COM_ParseToken_Simple(&p, false, false))
699 for (i = 0; i < MAX_FONT_SIZES; ++i)
701 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
704 for(ch = 0; ch < 256; ++ch)
705 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
709 maxwidth = fnt->width_of[0];
710 for(i = 1; i < 256; ++i)
711 maxwidth = max(maxwidth, fnt->width_of[i]);
712 fnt->maxwidth = maxwidth;
714 // fix up maxwidth for overlap
715 fnt->maxwidth *= fnt->settings.scale;
717 if(fnt == FONT_CONSOLE)
718 con_linewidth = -1; // rewrap console in next frame
721 extern cvar_t developer_font;
722 dp_font_t *FindFont(const char *title, qboolean allocate_new)
727 for(i = 0; i < dp_fonts.maxsize; ++i)
728 if(!strcmp(dp_fonts.f[i].title, title))
729 return &dp_fonts.f[i];
730 // if not found - try allocate
733 // find any font with empty title
734 for(i = 0; i < dp_fonts.maxsize; ++i)
736 if(!strcmp(dp_fonts.f[i].title, ""))
738 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
739 return &dp_fonts.f[i];
742 // if no any 'free' fonts - expand buffer
743 i = dp_fonts.maxsize;
744 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
745 if (developer_font.integer)
746 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
747 dp_fonts.f = Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
748 // register a font in first expanded slot
749 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
750 return &dp_fonts.f[i];
755 static float snap_to_pixel_x(float x, float roundUpAt)
757 float pixelpos = x * vid.width / vid_conwidth.value;
758 int snap = (int) pixelpos;
759 if (pixelpos - snap >= roundUpAt) ++snap;
760 return ((float)snap * vid_conwidth.value / vid.width);
762 x = (int)(x * vid.width / vid_conwidth.value);
763 x = (x * vid_conwidth.value / vid.width);
768 static float snap_to_pixel_y(float y, float roundUpAt)
770 float pixelpos = y * vid.height / vid_conheight.value;
771 int snap = (int) pixelpos;
772 if (pixelpos - snap > roundUpAt) ++snap;
773 return ((float)snap * vid_conheight.value / vid.height);
775 y = (int)(y * vid.height / vid_conheight.value);
776 y = (y * vid_conheight.value / vid.height);
781 static void LoadFont_f(void)
785 const char *filelist, *c, *cm;
786 float sz, scale, voffset;
787 char mainfont[MAX_QPATH];
791 Con_Printf("Available font commands:\n");
792 for(i = 0; i < dp_fonts.maxsize; ++i)
793 if (dp_fonts.f[i].title[0])
794 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
795 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
796 "can specify multiple fonts and faces\n"
797 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
798 "to load face 2 of the font gfx/vera-sans and use face 1\n"
799 "of gfx/fallback as fallback font.\n"
800 "You can also specify a list of font sizes to load, like this:\n"
801 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
802 "In many cases, 8 12 16 24 32 should be a good choice.\n"
804 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
805 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
809 f = FindFont(Cmd_Argv(1), true);
812 Con_Printf("font function not found\n");
817 filelist = "gfx/conchars";
819 filelist = Cmd_Argv(2);
821 memset(f->fallbacks, 0, sizeof(f->fallbacks));
822 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
824 // first font is handled "normally"
825 c = strchr(filelist, ':');
826 cm = strchr(filelist, ',');
827 if(c && (!cm || c < cm))
828 f->req_face = atoi(c+1);
835 if(!c || (c - filelist) > MAX_QPATH)
836 strlcpy(mainfont, filelist, sizeof(mainfont));
839 memcpy(mainfont, filelist, c - filelist);
840 mainfont[c - filelist] = 0;
843 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
845 c = strchr(filelist, ',');
851 c = strchr(filelist, ':');
852 cm = strchr(filelist, ',');
853 if(c && (!cm || c < cm))
854 f->fallback_faces[i] = atoi(c+1);
857 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
860 if(!c || (c-filelist) > MAX_QPATH)
862 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
866 memcpy(f->fallbacks[i], filelist, c - filelist);
867 f->fallbacks[i][c - filelist] = 0;
871 // for now: by default load only one size: the default size
873 for(i = 1; i < MAX_FONT_SIZES; ++i)
874 f->req_sizes[i] = -1;
880 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
883 if (!strcmp(Cmd_Argv(i), "scale"))
887 scale = atof(Cmd_Argv(i));
890 if (!strcmp(Cmd_Argv(i), "voffset"))
894 voffset = atof(Cmd_Argv(i));
899 continue; // no slot for other sizes
901 // parse one of sizes
902 sz = atof(Cmd_Argv(i));
903 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
905 // search for duplicated sizes
907 for (j=0; j<sizes; j++)
908 if (f->req_sizes[j] == sz)
911 continue; // sz already in req_sizes, don't add it again
913 if (sizes == MAX_FONT_SIZES)
915 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
919 f->req_sizes[sizes] = sz;
925 LoadFont(true, mainfont, f, scale, voffset);
933 static void gl_draw_start(void)
936 drawtexturepool = R_AllocTexturePool();
939 memset(cachepichash, 0, sizeof(cachepichash));
943 // load default font textures
944 for(i = 0; i < dp_fonts.maxsize; ++i)
945 if (dp_fonts.f[i].title[0])
946 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
948 // draw the loading screen so people have something to see in the newly opened window
949 SCR_UpdateLoadingScreen(true);
952 static void gl_draw_shutdown(void)
956 R_FreeTexturePool(&drawtexturepool);
959 memset(cachepichash, 0, sizeof(cachepichash));
962 static void gl_draw_newmap(void)
967 void GL_Draw_Init (void)
971 Cvar_RegisterVariable(&r_font_postprocess_blur);
972 Cvar_RegisterVariable(&r_font_postprocess_outline);
973 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
974 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
975 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
976 Cvar_RegisterVariable(&r_font_hinting);
977 Cvar_RegisterVariable(&r_font_antialias);
978 Cvar_RegisterVariable(&r_textshadow);
979 Cvar_RegisterVariable(&r_textbrightness);
980 Cvar_RegisterVariable(&r_textcontrast);
982 // allocate fonts storage
983 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
984 dp_fonts.maxsize = MAX_FONTS;
985 dp_fonts.f = Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
986 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
988 // assign starting font names
989 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
990 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
991 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
992 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
993 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
994 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
995 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
996 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
997 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
998 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
999 if(!FONT_USER(i)->title[0])
1000 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1002 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1003 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1006 static void _DrawQ_Setup(void)
1008 r_viewport_t viewport;
1009 if (r_refdef.draw2dstage == 1)
1011 r_refdef.draw2dstage = 1;
1013 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);
1014 R_SetViewport(&viewport);
1015 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1016 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1017 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
1018 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
1019 R_EntityMatrix(&identitymatrix);
1021 GL_DepthRange(0, 1);
1022 GL_PolygonOffset(0, 0);
1023 GL_DepthTest(false);
1025 GL_AlphaTest(false);
1028 qboolean r_draw2d_force = false;
1029 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1033 if(!r_draw2d.integer && !r_draw2d_force)
1035 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1037 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1039 if(flags == DRAWFLAG_ADDITIVE)
1041 GL_DepthMask(false);
1042 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1044 else if(flags == DRAWFLAG_MODULATE)
1046 GL_DepthMask(false);
1047 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1049 else if(flags == DRAWFLAG_2XMODULATE)
1051 GL_DepthMask(false);
1052 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1054 else if(flags == DRAWFLAG_SCREEN)
1056 GL_DepthMask(false);
1057 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1061 GL_DepthMask(false);
1062 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1067 GL_BlendFunc(GL_ONE, GL_ZERO);
1071 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1075 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1076 if(!r_draw2d.integer && !r_draw2d_force)
1078 GL_Color(red, green, blue, alpha);
1080 R_Mesh_VertexPointer(floats, 0, 0);
1081 R_Mesh_ColorPointer(NULL, 0, 0);
1082 R_Mesh_ResetTextureState();
1088 height = pic->height;
1089 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1090 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1093 floats[12] = 0.0f;floats[13] = 0.0f;
1094 floats[14] = 1.0f;floats[15] = 0.0f;
1095 floats[16] = 1.0f;floats[17] = 1.0f;
1096 floats[18] = 0.0f;floats[19] = 1.0f;
1098 // AK07: lets be texel correct on the corners
1100 float horz_offset = 0.5f / pic->width;
1101 float vert_offset = 0.5f / pic->height;
1103 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1104 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1105 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1106 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1111 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1113 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1114 floats[0] = floats[9] = x;
1115 floats[1] = floats[4] = y;
1116 floats[3] = floats[6] = x + width;
1117 floats[7] = floats[10] = y + height;
1119 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1122 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)
1125 float af = DEG2RAD(-angle); // forward
1126 float ar = DEG2RAD(-angle + 90); // right
1127 float sinaf = sin(af);
1128 float cosaf = cos(af);
1129 float sinar = sin(ar);
1130 float cosar = cos(ar);
1132 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1133 if(!r_draw2d.integer && !r_draw2d_force)
1135 GL_Color(red, green, blue, alpha);
1137 R_Mesh_VertexPointer(floats, 0, 0);
1138 R_Mesh_ColorPointer(NULL, 0, 0);
1139 R_Mesh_ResetTextureState();
1145 height = pic->height;
1146 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1147 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1149 floats[12] = 0.0f;floats[13] = 0.0f;
1150 floats[14] = 1.0f;floats[15] = 0.0f;
1151 floats[16] = 1.0f;floats[17] = 1.0f;
1152 floats[18] = 0.0f;floats[19] = 1.0f;
1155 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1157 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1160 floats[0] = x - cosaf*org_x - cosar*org_y;
1161 floats[1] = y - sinaf*org_x - sinar*org_y;
1164 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1165 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1168 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1169 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1172 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1173 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1175 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1178 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1182 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1183 if(!r_draw2d.integer && !r_draw2d_force)
1185 GL_Color(red, green, blue, alpha);
1187 R_Mesh_VertexPointer(floats, 0, 0);
1188 R_Mesh_ColorPointer(NULL, 0, 0);
1189 R_Mesh_ResetTextureState();
1190 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1192 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1193 floats[0] = floats[9] = x;
1194 floats[1] = floats[4] = y;
1195 floats[3] = floats[6] = x + width;
1196 floats[7] = floats[10] = y + height;
1198 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1201 /// color tag printing
1202 static const vec4_t string_colors[] =
1205 // LordHavoc: why on earth is cyan before magenta in Quake3?
1206 // LordHavoc: note: Doom3 uses white for [0] and [7]
1207 {0.0, 0.0, 0.0, 1.0}, // black
1208 {1.0, 0.0, 0.0, 1.0}, // red
1209 {0.0, 1.0, 0.0, 1.0}, // green
1210 {1.0, 1.0, 0.0, 1.0}, // yellow
1211 {0.0, 0.0, 1.0, 1.0}, // blue
1212 {0.0, 1.0, 1.0, 1.0}, // cyan
1213 {1.0, 0.0, 1.0, 1.0}, // magenta
1214 {1.0, 1.0, 1.0, 1.0}, // white
1215 // [515]'s BX_COLOREDTEXT extension
1216 {1.0, 1.0, 1.0, 0.5}, // half transparent
1217 {0.5, 0.5, 0.5, 1.0} // half brightness
1218 // Black's color table
1219 //{1.0, 1.0, 1.0, 1.0},
1220 //{1.0, 0.0, 0.0, 1.0},
1221 //{0.0, 1.0, 0.0, 1.0},
1222 //{0.0, 0.0, 1.0, 1.0},
1223 //{1.0, 1.0, 0.0, 1.0},
1224 //{0.0, 1.0, 1.0, 1.0},
1225 //{1.0, 0.0, 1.0, 1.0},
1226 //{0.1, 0.1, 0.1, 1.0}
1229 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1231 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1233 float C = r_textcontrast.value;
1234 float B = r_textbrightness.value;
1235 if (colorindex & 0x10000) // that bit means RGB color
1237 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1238 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1239 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1240 color[3] = (colorindex & 0xf) / 15.0;
1243 Vector4Copy(string_colors[colorindex], color);
1244 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1247 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1248 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1252 // NOTE: this function always draws exactly one character if maxwidth <= 0
1253 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)
1255 const char *text_start = text;
1256 int colorindex = STRING_COLOR_DEFAULT;
1259 Uchar ch, mapch, nextch;
1260 Uchar prevch = 0; // used for kerning
1265 ft2_font_map_t *fontmap = NULL;
1266 ft2_font_map_t *map = NULL;
1267 //ft2_font_map_t *prevmap = NULL;
1268 ft2_font_t *ft2 = fnt->ft2;
1270 qboolean snap = true;
1271 qboolean least_one = false;
1272 float dw; // display w
1273 //float dh; // display h
1274 const float *width_of;
1281 // do this in the end
1282 w *= fnt->settings.scale;
1283 h *= fnt->settings.scale;
1285 // find the most fitting size:
1289 map_index = Font_IndexForSize(ft2, h, &w, &h);
1291 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1292 fontmap = Font_MapForIndex(ft2, map_index);
1301 if (!outcolor || *outcolor == -1)
1302 colorindex = STRING_COLOR_DEFAULT;
1304 colorindex = *outcolor;
1306 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1307 // ftbase_x = snap_to_pixel_x(0);
1312 maxwidth = -maxwidth;
1316 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1319 width_of = fontmap->width_of;
1321 width_of = fnt->width_of;
1323 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1326 nextch = ch = u8_getnchar(text, &text, bytes_left);
1327 i = text - text_start;
1330 if (ch == ' ' && !fontmap)
1332 if(!least_one || i0) // never skip the first character
1333 if(x + width_of[(int) ' '] * dw > maxwidth)
1336 break; // oops, can't draw this
1338 x += width_of[(int) ' '] * dw;
1341 // i points to the char after ^
1342 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1344 ch = *text; // colors are ascii, so no u8_ needed
1345 if (ch <= '9' && ch >= '0') // ^[0-9] found
1347 colorindex = ch - '0';
1352 // i points to the char after ^...
1353 // i+3 points to 3 in ^x123
1354 // i+3 == *maxlen would mean that char is missing
1355 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1357 // building colorindex...
1358 ch = tolower(text[1]);
1359 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1360 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1361 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1362 else tempcolorindex = 0;
1365 ch = tolower(text[2]);
1366 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1367 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1368 else tempcolorindex = 0;
1371 ch = tolower(text[3]);
1372 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1373 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1374 else tempcolorindex = 0;
1377 colorindex = tempcolorindex | 0xf;
1378 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1386 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1395 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1402 map = ft2_oldstyle_map;
1404 if(!least_one || i0) // never skip the first character
1405 if(x + width_of[ch] * dw > maxwidth)
1408 break; // oops, can't draw this
1410 x += width_of[ch] * dw;
1412 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1414 map = FontMap_FindForChar(fontmap, ch);
1417 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1423 mapch = ch - map->start;
1424 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1426 x += map->glyphs[mapch].advance_x * dw;
1435 *outcolor = colorindex;
1440 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)
1442 int shadow, colorindex = STRING_COLOR_DEFAULT;
1444 float x = startx, y, s, t, u, v, thisw;
1445 float *av, *at, *ac;
1448 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1449 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1450 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1451 Uchar ch, mapch, nextch;
1452 Uchar prevch = 0; // used for kerning
1455 //ft2_font_map_t *prevmap = NULL; // the previous map
1456 ft2_font_map_t *map = NULL; // the currently used map
1457 ft2_font_map_t *fontmap = NULL; // the font map for the size
1459 const char *text_start = text;
1461 ft2_font_t *ft2 = fnt->ft2;
1462 qboolean snap = true;
1466 const float *width_of;
1469 tw = R_TextureWidth(fnt->tex);
1470 th = R_TextureHeight(fnt->tex);
1478 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1479 w *= fnt->settings.scale;
1480 h *= fnt->settings.scale;
1485 map_index = Font_IndexForSize(ft2, h, &w, &h);
1487 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1488 fontmap = Font_MapForIndex(ft2, map_index);
1494 // draw the font at its baseline when using freetype
1496 ftbase_y = dh * (4.5/6.0);
1501 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1502 if(!r_draw2d.integer && !r_draw2d_force)
1503 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1505 R_Mesh_ColorPointer(color4f, 0, 0);
1506 R_Mesh_ResetTextureState();
1508 R_Mesh_TexBind(0, fnt->tex);
1509 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1510 R_Mesh_VertexPointer(vertex3f, 0, 0);
1511 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1518 //ftbase_x = snap_to_pixel_x(ftbase_x);
1521 startx = snap_to_pixel_x(startx, 0.4);
1522 starty = snap_to_pixel_y(starty, 0.4);
1523 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1526 pix_x = vid.width / vid_conwidth.value;
1527 pix_y = vid.height / vid_conheight.value;
1530 width_of = fontmap->width_of;
1532 width_of = fnt->width_of;
1534 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1539 if (!outcolor || *outcolor == -1)
1540 colorindex = STRING_COLOR_DEFAULT;
1542 colorindex = *outcolor;
1544 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1551 x += r_textshadow.value * vid.width / vid_conwidth.value;
1552 y += r_textshadow.value * vid.height / vid_conheight.value;
1555 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1557 nextch = ch = u8_getnchar(text, &text, bytes_left);
1558 i = text - text_start;
1561 if (ch == ' ' && !fontmap)
1563 x += width_of[(int) ' '] * dw;
1566 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1568 ch = *text; // colors are ascii, so no u8_ needed
1569 if (ch <= '9' && ch >= '0') // ^[0-9] found
1571 colorindex = ch - '0';
1572 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1577 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1579 // building colorindex...
1580 ch = tolower(text[1]);
1581 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1582 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1583 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1584 else tempcolorindex = 0;
1587 ch = tolower(text[2]);
1588 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1589 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1590 else tempcolorindex = 0;
1593 ch = tolower(text[3]);
1594 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1595 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1596 else tempcolorindex = 0;
1599 colorindex = tempcolorindex | 0xf;
1600 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1601 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1602 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1610 else if (ch == STRING_COLOR_TAG)
1619 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1620 // this way we don't need to rebind fnt->tex for every old-style character
1621 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1624 x += 1.0/pix_x * r_textshadow.value;
1625 y += 1.0/pix_y * r_textshadow.value;
1627 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1635 if (map != ft2_oldstyle_map)
1639 // switching from freetype to non-freetype rendering
1640 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1646 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1647 map = ft2_oldstyle_map;
1651 //num = (unsigned char) text[i];
1652 //thisw = fnt->width_of[num];
1653 thisw = fnt->width_of[ch];
1654 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1655 s = (ch & 15)*0.0625f + (0.5f / tw);
1656 t = (ch >> 4)*0.0625f + (0.5f / th);
1657 u = 0.0625f * thisw - (1.0f / tw);
1658 v = 0.0625f - (1.0f / th);
1659 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1660 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1661 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1662 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1663 at[ 0] = s ; at[ 1] = t ;
1664 at[ 2] = s+u ; at[ 3] = t ;
1665 at[ 4] = s+u ; at[ 5] = t+v ;
1666 at[ 6] = s ; at[ 7] = t+v ;
1667 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1668 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1669 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1670 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1675 if (batchcount >= QUADELEMENTS_MAXQUADS)
1677 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1683 x += width_of[ch] * dw;
1685 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1687 // new charmap - need to render
1690 // we need a different character map, render what we currently have:
1691 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1698 map = FontMap_FindForChar(fontmap, ch);
1701 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1708 // this shouldn't happen
1713 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1716 mapch = ch - map->start;
1717 thisw = map->glyphs[mapch].advance_x;
1721 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1728 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1729 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1730 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1731 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1732 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1733 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1734 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1735 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1736 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1737 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1738 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1739 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1748 if (batchcount >= QUADELEMENTS_MAXQUADS)
1750 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1762 x -= 1.0/pix_x * r_textshadow.value;
1763 y -= 1.0/pix_y * r_textshadow.value;
1768 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1771 *outcolor = colorindex;
1773 // note: this relies on the proper text (not shadow) being drawn last
1777 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)
1779 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1782 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)
1784 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1787 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1789 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1792 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1794 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1799 // no ^xrgb management
1800 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1802 int color, numchars = 0;
1803 char *outputend2c = output2c + maxoutchars - 2;
1804 if (!outcolor || *outcolor == -1)
1805 color = STRING_COLOR_DEFAULT;
1809 maxreadchars = 1<<30;
1810 textend = text + maxreadchars;
1811 while (text != textend && *text)
1813 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1815 if (text[1] == STRING_COLOR_TAG)
1817 else if (text[1] >= '0' && text[1] <= '9')
1819 color = text[1] - '0';
1824 if (output2c >= outputend2c)
1826 *output2c++ = *text++;
1827 *output2c++ = color;
1830 output2c[0] = output2c[1] = 0;
1837 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)
1841 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1842 if(!r_draw2d.integer && !r_draw2d_force)
1845 R_Mesh_VertexPointer(floats, 0, 0);
1846 R_Mesh_ColorPointer(floats + 20, 0, 0);
1847 R_Mesh_ResetTextureState();
1853 height = pic->height;
1854 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1855 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1856 floats[12] = s1;floats[13] = t1;
1857 floats[14] = s2;floats[15] = t2;
1858 floats[16] = s4;floats[17] = t4;
1859 floats[18] = s3;floats[19] = t3;
1862 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1864 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1865 floats[0] = floats[9] = x;
1866 floats[1] = floats[4] = y;
1867 floats[3] = floats[6] = x + width;
1868 floats[7] = floats[10] = y + height;
1869 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1870 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1871 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1872 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1874 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1877 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1881 if(!r_draw2d.integer && !r_draw2d_force)
1883 DrawQ_ProcessDrawFlag(flags, hasalpha);
1885 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1886 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1887 R_Mesh_ResetTextureState();
1888 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1889 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1891 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1894 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1898 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1899 if(!r_draw2d.integer && !r_draw2d_force)
1904 qglBegin(GL_LINE_LOOP);
1905 for (num = 0;num < mesh->num_vertices;num++)
1907 if (mesh->data_color4f)
1908 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]);
1909 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1915 //[515]: this is old, delete
1916 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1918 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1919 if(!r_draw2d.integer && !r_draw2d_force)
1922 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1925 //qglLineWidth(width);CHECKGLERROR
1927 GL_Color(r,g,b,alpha);
1930 qglVertex2f(x1, y1);
1931 qglVertex2f(x2, y2);
1936 void DrawQ_SetClipArea(float x, float y, float width, float height)
1941 // We have to convert the con coords into real coords
1942 // OGL uses top to bottom
1943 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1944 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1945 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1946 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1947 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1949 GL_ScissorTest(true);
1952 void DrawQ_ResetClipArea(void)
1955 GL_ScissorTest(false);
1958 void DrawQ_Finish(void)
1960 r_refdef.draw2dstage = 0;
1963 void DrawQ_RecalcView(void)
1965 if(r_refdef.draw2dstage)
1966 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
1969 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1970 void R_DrawGamma(void)
1973 switch(vid.renderpath)
1975 case RENDERPATH_GL20:
1976 case RENDERPATH_CGGL:
1977 if (vid_usinghwgamma || v_glslgamma.integer)
1980 case RENDERPATH_GL13:
1981 case RENDERPATH_GL11:
1982 if (vid_usinghwgamma)
1986 // all the blends ignore depth
1987 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1988 R_Mesh_ColorPointer(NULL, 0, 0);
1989 R_Mesh_ResetTextureState();
1990 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1992 GL_DepthRange(0, 1);
1993 GL_PolygonOffset(0, 0);
1994 GL_DepthTest(false);
1995 if (v_color_enable.integer)
1997 c[0] = v_color_white_r.value;
1998 c[1] = v_color_white_g.value;
1999 c[2] = v_color_white_b.value;
2002 c[0] = c[1] = c[2] = v_contrast.value;
2003 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2005 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2006 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2008 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
2009 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
2010 VectorScale(c, 0.5, c);
2013 if (v_color_enable.integer)
2015 c[0] = v_color_black_r.value;
2016 c[1] = v_color_black_g.value;
2017 c[2] = v_color_black_b.value;
2020 c[0] = c[1] = c[2] = v_brightness.value;
2021 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2023 GL_BlendFunc(GL_ONE, GL_ONE);
2024 GL_Color(c[0], c[1], c[2], 1);
2025 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);