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 != 0, NULL);
375 if (pixels == NULL && !strncmp(path, "gfx/", 4))
376 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer != 0, 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 != 0);
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 != 0);
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 = (dp_font_t *)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 = (dp_font_t *)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 GL_DepthFunc(GL_LEQUAL);
1017 GL_PolygonOffset(0,0);
1018 GL_CullFace(GL_NONE);
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)
1079 R_Mesh_ResetTextureState();
1080 floats[12] = 0.0f;floats[13] = 0.0f;
1081 floats[14] = 1.0f;floats[15] = 0.0f;
1082 floats[16] = 1.0f;floats[17] = 1.0f;
1083 floats[18] = 0.0f;floats[19] = 1.0f;
1084 floats[20] = floats[24] = floats[28] = floats[32] = red;
1085 floats[21] = floats[25] = floats[29] = floats[33] = green;
1086 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1087 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1093 height = pic->height;
1094 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1097 // AK07: lets be texel correct on the corners
1099 float horz_offset = 0.5f / pic->width;
1100 float vert_offset = 0.5f / pic->height;
1102 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1103 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1104 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1105 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1110 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1112 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1113 floats[0] = floats[9] = x;
1114 floats[1] = floats[4] = y;
1115 floats[3] = floats[6] = x + width;
1116 floats[7] = floats[10] = y + height;
1118 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1119 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 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)
1136 R_Mesh_ResetTextureState();
1142 height = pic->height;
1143 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1146 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1148 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1151 floats[0] = x - cosaf*org_x - cosar*org_y;
1152 floats[1] = y - sinaf*org_x - sinar*org_y;
1155 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1156 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1159 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1160 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1163 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1164 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1166 floats[12] = 0.0f;floats[13] = 0.0f;
1167 floats[14] = 1.0f;floats[15] = 0.0f;
1168 floats[16] = 1.0f;floats[17] = 1.0f;
1169 floats[18] = 0.0f;floats[19] = 1.0f;
1170 floats[20] = floats[24] = floats[28] = floats[32] = red;
1171 floats[21] = floats[25] = floats[29] = floats[33] = green;
1172 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1173 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1175 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1176 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1179 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1183 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1184 if(!r_draw2d.integer && !r_draw2d_force)
1187 R_Mesh_ResetTextureState();
1188 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1190 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1191 floats[0] = floats[9] = x;
1192 floats[1] = floats[4] = y;
1193 floats[3] = floats[6] = x + width;
1194 floats[7] = floats[10] = y + height;
1195 floats[12] = 0.0f;floats[13] = 0.0f;
1196 floats[14] = 1.0f;floats[15] = 0.0f;
1197 floats[16] = 1.0f;floats[17] = 1.0f;
1198 floats[18] = 0.0f;floats[19] = 1.0f;
1199 floats[20] = floats[24] = floats[28] = floats[32] = red;
1200 floats[21] = floats[25] = floats[29] = floats[33] = green;
1201 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1202 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1204 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1205 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1208 /// color tag printing
1209 static const vec4_t string_colors[] =
1212 // LordHavoc: why on earth is cyan before magenta in Quake3?
1213 // LordHavoc: note: Doom3 uses white for [0] and [7]
1214 {0.0, 0.0, 0.0, 1.0}, // black
1215 {1.0, 0.0, 0.0, 1.0}, // red
1216 {0.0, 1.0, 0.0, 1.0}, // green
1217 {1.0, 1.0, 0.0, 1.0}, // yellow
1218 {0.0, 0.0, 1.0, 1.0}, // blue
1219 {0.0, 1.0, 1.0, 1.0}, // cyan
1220 {1.0, 0.0, 1.0, 1.0}, // magenta
1221 {1.0, 1.0, 1.0, 1.0}, // white
1222 // [515]'s BX_COLOREDTEXT extension
1223 {1.0, 1.0, 1.0, 0.5}, // half transparent
1224 {0.5, 0.5, 0.5, 1.0} // half brightness
1225 // Black's color table
1226 //{1.0, 1.0, 1.0, 1.0},
1227 //{1.0, 0.0, 0.0, 1.0},
1228 //{0.0, 1.0, 0.0, 1.0},
1229 //{0.0, 0.0, 1.0, 1.0},
1230 //{1.0, 1.0, 0.0, 1.0},
1231 //{0.0, 1.0, 1.0, 1.0},
1232 //{1.0, 0.0, 1.0, 1.0},
1233 //{0.1, 0.1, 0.1, 1.0}
1236 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1238 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1240 float C = r_textcontrast.value;
1241 float B = r_textbrightness.value;
1242 if (colorindex & 0x10000) // that bit means RGB color
1244 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1245 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1246 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1247 color[3] = (colorindex & 0xf) / 15.0;
1250 Vector4Copy(string_colors[colorindex], color);
1251 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1254 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1255 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1259 // NOTE: this function always draws exactly one character if maxwidth <= 0
1260 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)
1262 const char *text_start = text;
1263 int colorindex = STRING_COLOR_DEFAULT;
1266 Uchar ch, mapch, nextch;
1267 Uchar prevch = 0; // used for kerning
1272 ft2_font_map_t *fontmap = NULL;
1273 ft2_font_map_t *map = NULL;
1274 //ft2_font_map_t *prevmap = NULL;
1275 ft2_font_t *ft2 = fnt->ft2;
1277 qboolean snap = true;
1278 qboolean least_one = false;
1279 float dw; // display w
1280 //float dh; // display h
1281 const float *width_of;
1288 // do this in the end
1289 w *= fnt->settings.scale;
1290 h *= fnt->settings.scale;
1292 // find the most fitting size:
1296 map_index = Font_IndexForSize(ft2, h, &w, &h);
1298 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1299 fontmap = Font_MapForIndex(ft2, map_index);
1308 if (!outcolor || *outcolor == -1)
1309 colorindex = STRING_COLOR_DEFAULT;
1311 colorindex = *outcolor;
1313 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1314 // ftbase_x = snap_to_pixel_x(0);
1319 maxwidth = -maxwidth;
1323 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1326 width_of = fontmap->width_of;
1328 width_of = fnt->width_of;
1330 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1333 nextch = ch = u8_getnchar(text, &text, bytes_left);
1334 i = text - text_start;
1337 if (ch == ' ' && !fontmap)
1339 if(!least_one || i0) // never skip the first character
1340 if(x + width_of[(int) ' '] * dw > maxwidth)
1343 break; // oops, can't draw this
1345 x += width_of[(int) ' '] * dw;
1348 // i points to the char after ^
1349 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1351 ch = *text; // colors are ascii, so no u8_ needed
1352 if (ch <= '9' && ch >= '0') // ^[0-9] found
1354 colorindex = ch - '0';
1359 // i points to the char after ^...
1360 // i+3 points to 3 in ^x123
1361 // i+3 == *maxlen would mean that char is missing
1362 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1364 // building colorindex...
1365 ch = tolower(text[1]);
1366 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1367 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1368 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1369 else tempcolorindex = 0;
1372 ch = tolower(text[2]);
1373 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1374 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1375 else tempcolorindex = 0;
1378 ch = tolower(text[3]);
1379 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1380 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1381 else tempcolorindex = 0;
1384 colorindex = tempcolorindex | 0xf;
1385 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1393 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1402 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1409 map = ft2_oldstyle_map;
1411 if(!least_one || i0) // never skip the first character
1412 if(x + width_of[ch] * dw > maxwidth)
1415 break; // oops, can't draw this
1417 x += width_of[ch] * dw;
1419 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1421 map = FontMap_FindForChar(fontmap, ch);
1424 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1430 mapch = ch - map->start;
1431 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1433 x += map->glyphs[mapch].advance_x * dw;
1442 *outcolor = colorindex;
1447 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)
1449 int shadow, colorindex = STRING_COLOR_DEFAULT;
1451 float x = startx, y, s, t, u, v, thisw;
1452 float *av, *at, *ac;
1455 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1456 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1457 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1458 Uchar ch, mapch, nextch;
1459 Uchar prevch = 0; // used for kerning
1462 //ft2_font_map_t *prevmap = NULL; // the previous map
1463 ft2_font_map_t *map = NULL; // the currently used map
1464 ft2_font_map_t *fontmap = NULL; // the font map for the size
1466 const char *text_start = text;
1468 ft2_font_t *ft2 = fnt->ft2;
1469 qboolean snap = true;
1473 const float *width_of;
1476 tw = R_TextureWidth(fnt->tex);
1477 th = R_TextureHeight(fnt->tex);
1485 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1486 w *= fnt->settings.scale;
1487 h *= fnt->settings.scale;
1492 map_index = Font_IndexForSize(ft2, h, &w, &h);
1494 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1495 fontmap = Font_MapForIndex(ft2, map_index);
1501 // draw the font at its baseline when using freetype
1503 ftbase_y = dh * (4.5/6.0);
1508 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1509 if(!r_draw2d.integer && !r_draw2d_force)
1510 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1512 R_Mesh_ResetTextureState();
1514 R_Mesh_TexBind(0, fnt->tex);
1515 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1522 //ftbase_x = snap_to_pixel_x(ftbase_x);
1525 startx = snap_to_pixel_x(startx, 0.4);
1526 starty = snap_to_pixel_y(starty, 0.4);
1527 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1530 pix_x = vid.width / vid_conwidth.value;
1531 pix_y = vid.height / vid_conheight.value;
1534 width_of = fontmap->width_of;
1536 width_of = fnt->width_of;
1538 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1543 if (!outcolor || *outcolor == -1)
1544 colorindex = STRING_COLOR_DEFAULT;
1546 colorindex = *outcolor;
1548 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1555 x += r_textshadow.value * vid.width / vid_conwidth.value;
1556 y += r_textshadow.value * vid.height / vid_conheight.value;
1559 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1561 nextch = ch = u8_getnchar(text, &text, bytes_left);
1562 i = text - text_start;
1565 if (ch == ' ' && !fontmap)
1567 x += width_of[(int) ' '] * dw;
1570 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1572 ch = *text; // colors are ascii, so no u8_ needed
1573 if (ch <= '9' && ch >= '0') // ^[0-9] found
1575 colorindex = ch - '0';
1576 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1581 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1583 // building colorindex...
1584 ch = tolower(text[1]);
1585 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1586 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1587 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1588 else tempcolorindex = 0;
1591 ch = tolower(text[2]);
1592 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1593 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1594 else tempcolorindex = 0;
1597 ch = tolower(text[3]);
1598 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1599 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1600 else tempcolorindex = 0;
1603 colorindex = tempcolorindex | 0xf;
1604 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1605 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1606 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1614 else if (ch == STRING_COLOR_TAG)
1623 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1624 // this way we don't need to rebind fnt->tex for every old-style character
1625 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1628 x += 1.0/pix_x * r_textshadow.value;
1629 y += 1.0/pix_y * r_textshadow.value;
1631 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1639 if (map != ft2_oldstyle_map)
1643 // switching from freetype to non-freetype rendering
1644 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1645 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1651 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1652 map = ft2_oldstyle_map;
1656 //num = (unsigned char) text[i];
1657 //thisw = fnt->width_of[num];
1658 thisw = fnt->width_of[ch];
1659 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1660 s = (ch & 15)*0.0625f + (0.5f / tw);
1661 t = (ch >> 4)*0.0625f + (0.5f / th);
1662 u = 0.0625f * thisw - (1.0f / tw);
1663 v = 0.0625f - (1.0f / th);
1664 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1665 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1666 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1667 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1668 at[ 0] = s ; at[ 1] = t ;
1669 at[ 2] = s+u ; at[ 3] = t ;
1670 at[ 4] = s+u ; at[ 5] = t+v ;
1671 at[ 6] = s ; at[ 7] = t+v ;
1672 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1673 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1674 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1675 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1680 if (batchcount >= QUADELEMENTS_MAXQUADS)
1682 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1683 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1689 x += width_of[ch] * dw;
1691 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1693 // new charmap - need to render
1696 // we need a different character map, render what we currently have:
1697 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1698 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1705 map = FontMap_FindForChar(fontmap, ch);
1708 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1715 // this shouldn't happen
1720 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1723 mapch = ch - map->start;
1724 thisw = map->glyphs[mapch].advance_x;
1728 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1735 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1736 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1737 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1738 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1739 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1740 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1741 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1742 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1743 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1744 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1745 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1746 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1755 if (batchcount >= QUADELEMENTS_MAXQUADS)
1757 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1758 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1770 x -= 1.0/pix_x * r_textshadow.value;
1771 y -= 1.0/pix_y * r_textshadow.value;
1777 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1778 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1782 *outcolor = colorindex;
1784 // note: this relies on the proper text (not shadow) being drawn last
1788 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)
1790 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1793 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)
1795 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1798 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1800 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1803 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1805 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1810 // no ^xrgb management
1811 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1813 int color, numchars = 0;
1814 char *outputend2c = output2c + maxoutchars - 2;
1815 if (!outcolor || *outcolor == -1)
1816 color = STRING_COLOR_DEFAULT;
1820 maxreadchars = 1<<30;
1821 textend = text + maxreadchars;
1822 while (text != textend && *text)
1824 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1826 if (text[1] == STRING_COLOR_TAG)
1828 else if (text[1] >= '0' && text[1] <= '9')
1830 color = text[1] - '0';
1835 if (output2c >= outputend2c)
1837 *output2c++ = *text++;
1838 *output2c++ = color;
1841 output2c[0] = output2c[1] = 0;
1848 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)
1852 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1853 if(!r_draw2d.integer && !r_draw2d_force)
1856 R_Mesh_ResetTextureState();
1862 height = pic->height;
1863 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1866 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1868 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1869 floats[0] = floats[9] = x;
1870 floats[1] = floats[4] = y;
1871 floats[3] = floats[6] = x + width;
1872 floats[7] = floats[10] = y + height;
1873 floats[12] = s1;floats[13] = t1;
1874 floats[14] = s2;floats[15] = t2;
1875 floats[16] = s4;floats[17] = t4;
1876 floats[18] = s3;floats[19] = t3;
1877 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1878 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1879 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1880 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1882 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1883 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1886 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1890 if(!r_draw2d.integer && !r_draw2d_force)
1892 DrawQ_ProcessDrawFlag(flags, hasalpha);
1894 R_Mesh_ResetTextureState();
1895 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1897 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1898 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1901 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1905 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1906 if(!r_draw2d.integer && !r_draw2d_force)
1910 switch(vid.renderpath)
1912 case RENDERPATH_GL11:
1913 case RENDERPATH_GL13:
1914 case RENDERPATH_GL20:
1915 case RENDERPATH_CGGL:
1917 qglBegin(GL_LINE_LOOP);
1918 for (num = 0;num < mesh->num_vertices;num++)
1920 if (mesh->data_color4f)
1921 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]);
1922 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1927 case RENDERPATH_D3D9:
1928 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1930 case RENDERPATH_D3D10:
1931 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1933 case RENDERPATH_D3D11:
1934 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1939 //[515]: this is old, delete
1940 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1942 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1943 if(!r_draw2d.integer && !r_draw2d_force)
1946 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1948 switch(vid.renderpath)
1950 case RENDERPATH_GL11:
1951 case RENDERPATH_GL13:
1952 case RENDERPATH_GL20:
1953 case RENDERPATH_CGGL:
1956 //qglLineWidth(width);CHECKGLERROR
1958 GL_Color(r,g,b,alpha);
1961 qglVertex2f(x1, y1);
1962 qglVertex2f(x2, y2);
1966 case RENDERPATH_D3D9:
1967 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1969 case RENDERPATH_D3D10:
1970 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1972 case RENDERPATH_D3D11:
1973 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1978 void DrawQ_SetClipArea(float x, float y, float width, float height)
1983 // We have to convert the con coords into real coords
1984 // OGL uses top to bottom
1985 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1986 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1987 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1988 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1989 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1991 GL_ScissorTest(true);
1994 void DrawQ_ResetClipArea(void)
1997 GL_ScissorTest(false);
2000 void DrawQ_Finish(void)
2002 r_refdef.draw2dstage = 0;
2005 void DrawQ_RecalcView(void)
2007 if(r_refdef.draw2dstage)
2008 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2011 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2012 void R_DrawGamma(void)
2015 switch(vid.renderpath)
2017 case RENDERPATH_GL20:
2018 case RENDERPATH_CGGL:
2019 case RENDERPATH_D3D9:
2020 case RENDERPATH_D3D10:
2021 case RENDERPATH_D3D11:
2022 if (vid_usinghwgamma || v_glslgamma.integer)
2025 case RENDERPATH_GL13:
2026 case RENDERPATH_GL11:
2027 if (vid_usinghwgamma)
2031 // all the blends ignore depth
2032 R_Mesh_ResetTextureState();
2033 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2035 GL_DepthRange(0, 1);
2036 GL_PolygonOffset(0, 0);
2037 GL_DepthTest(false);
2038 if (v_color_enable.integer)
2040 c[0] = v_color_white_r.value;
2041 c[1] = v_color_white_g.value;
2042 c[2] = v_color_white_b.value;
2045 c[0] = c[1] = c[2] = v_contrast.value;
2046 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2048 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2049 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2051 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2052 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2053 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2054 VectorScale(c, 0.5, c);
2057 if (v_color_enable.integer)
2059 c[0] = v_color_black_r.value;
2060 c[1] = v_color_black_g.value;
2061 c[2] = v_color_black_b.value;
2064 c[0] = c[1] = c[2] = v_brightness.value;
2065 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2067 GL_BlendFunc(GL_ONE, GL_ONE);
2068 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2069 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2070 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);