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 oldsize = 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", oldsize, 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 // relink ft2 structures
749 for(i = 0; i < oldsize; ++i)
750 if (dp_fonts.f[i].ft2)
751 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
752 // register a font in first expanded slot
753 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
754 return &dp_fonts.f[oldsize];
759 static float snap_to_pixel_x(float x, float roundUpAt)
761 float pixelpos = x * vid.width / vid_conwidth.value;
762 int snap = (int) pixelpos;
763 if (pixelpos - snap >= roundUpAt) ++snap;
764 return ((float)snap * vid_conwidth.value / vid.width);
766 x = (int)(x * vid.width / vid_conwidth.value);
767 x = (x * vid_conwidth.value / vid.width);
772 static float snap_to_pixel_y(float y, float roundUpAt)
774 float pixelpos = y * vid.height / vid_conheight.value;
775 int snap = (int) pixelpos;
776 if (pixelpos - snap > roundUpAt) ++snap;
777 return ((float)snap * vid_conheight.value / vid.height);
779 y = (int)(y * vid.height / vid_conheight.value);
780 y = (y * vid_conheight.value / vid.height);
785 static void LoadFont_f(void)
789 const char *filelist, *c, *cm;
790 float sz, scale, voffset;
791 char mainfont[MAX_QPATH];
795 Con_Printf("Available font commands:\n");
796 for(i = 0; i < dp_fonts.maxsize; ++i)
797 if (dp_fonts.f[i].title[0])
798 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
799 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
800 "can specify multiple fonts and faces\n"
801 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
802 "to load face 2 of the font gfx/vera-sans and use face 1\n"
803 "of gfx/fallback as fallback font.\n"
804 "You can also specify a list of font sizes to load, like this:\n"
805 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
806 "In many cases, 8 12 16 24 32 should be a good choice.\n"
808 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
809 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
813 f = FindFont(Cmd_Argv(1), true);
816 Con_Printf("font function not found\n");
821 filelist = "gfx/conchars";
823 filelist = Cmd_Argv(2);
825 memset(f->fallbacks, 0, sizeof(f->fallbacks));
826 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
828 // first font is handled "normally"
829 c = strchr(filelist, ':');
830 cm = strchr(filelist, ',');
831 if(c && (!cm || c < cm))
832 f->req_face = atoi(c+1);
839 if(!c || (c - filelist) > MAX_QPATH)
840 strlcpy(mainfont, filelist, sizeof(mainfont));
843 memcpy(mainfont, filelist, c - filelist);
844 mainfont[c - filelist] = 0;
847 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
849 c = strchr(filelist, ',');
855 c = strchr(filelist, ':');
856 cm = strchr(filelist, ',');
857 if(c && (!cm || c < cm))
858 f->fallback_faces[i] = atoi(c+1);
861 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
864 if(!c || (c-filelist) > MAX_QPATH)
866 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
870 memcpy(f->fallbacks[i], filelist, c - filelist);
871 f->fallbacks[i][c - filelist] = 0;
875 // for now: by default load only one size: the default size
877 for(i = 1; i < MAX_FONT_SIZES; ++i)
878 f->req_sizes[i] = -1;
884 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
887 if (!strcmp(Cmd_Argv(i), "scale"))
891 scale = atof(Cmd_Argv(i));
894 if (!strcmp(Cmd_Argv(i), "voffset"))
898 voffset = atof(Cmd_Argv(i));
903 continue; // no slot for other sizes
905 // parse one of sizes
906 sz = atof(Cmd_Argv(i));
907 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
909 // search for duplicated sizes
911 for (j=0; j<sizes; j++)
912 if (f->req_sizes[j] == sz)
915 continue; // sz already in req_sizes, don't add it again
917 if (sizes == MAX_FONT_SIZES)
919 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
923 f->req_sizes[sizes] = sz;
929 LoadFont(true, mainfont, f, scale, voffset);
937 static void gl_draw_start(void)
940 drawtexturepool = R_AllocTexturePool();
943 memset(cachepichash, 0, sizeof(cachepichash));
947 // load default font textures
948 for(i = 0; i < dp_fonts.maxsize; ++i)
949 if (dp_fonts.f[i].title[0])
950 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
952 // draw the loading screen so people have something to see in the newly opened window
953 SCR_UpdateLoadingScreen(true);
956 static void gl_draw_shutdown(void)
960 R_FreeTexturePool(&drawtexturepool);
963 memset(cachepichash, 0, sizeof(cachepichash));
966 static void gl_draw_newmap(void)
971 void GL_Draw_Init (void)
975 Cvar_RegisterVariable(&r_font_postprocess_blur);
976 Cvar_RegisterVariable(&r_font_postprocess_outline);
977 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
978 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
979 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
980 Cvar_RegisterVariable(&r_font_hinting);
981 Cvar_RegisterVariable(&r_font_antialias);
982 Cvar_RegisterVariable(&r_textshadow);
983 Cvar_RegisterVariable(&r_textbrightness);
984 Cvar_RegisterVariable(&r_textcontrast);
986 // allocate fonts storage
987 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
988 dp_fonts.maxsize = MAX_FONTS;
989 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
990 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
992 // assign starting font names
993 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
994 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
995 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
996 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
997 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
998 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
999 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1000 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1001 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1002 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1003 if(!FONT_USER(i)->title[0])
1004 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1006 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1007 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1010 static void _DrawQ_Setup(void)
1012 r_viewport_t viewport;
1013 if (r_refdef.draw2dstage == 1)
1015 r_refdef.draw2dstage = 1;
1017 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);
1018 R_SetViewport(&viewport);
1019 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1020 GL_DepthFunc(GL_LEQUAL);
1021 GL_PolygonOffset(0,0);
1022 GL_CullFace(GL_NONE);
1023 R_EntityMatrix(&identitymatrix);
1025 GL_DepthRange(0, 1);
1026 GL_PolygonOffset(0, 0);
1027 GL_DepthTest(false);
1029 GL_AlphaTest(false);
1032 qboolean r_draw2d_force = false;
1033 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1037 if(!r_draw2d.integer && !r_draw2d_force)
1039 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1041 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1043 if(flags == DRAWFLAG_ADDITIVE)
1045 GL_DepthMask(false);
1046 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1048 else if(flags == DRAWFLAG_MODULATE)
1050 GL_DepthMask(false);
1051 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1053 else if(flags == DRAWFLAG_2XMODULATE)
1055 GL_DepthMask(false);
1056 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1058 else if(flags == DRAWFLAG_SCREEN)
1060 GL_DepthMask(false);
1061 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1065 GL_DepthMask(false);
1066 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1071 GL_BlendFunc(GL_ONE, GL_ZERO);
1075 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1079 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1080 if(!r_draw2d.integer && !r_draw2d_force)
1083 R_Mesh_ResetTextureState();
1084 floats[12] = 0.0f;floats[13] = 0.0f;
1085 floats[14] = 1.0f;floats[15] = 0.0f;
1086 floats[16] = 1.0f;floats[17] = 1.0f;
1087 floats[18] = 0.0f;floats[19] = 1.0f;
1088 floats[20] = floats[24] = floats[28] = floats[32] = red;
1089 floats[21] = floats[25] = floats[29] = floats[33] = green;
1090 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1091 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1097 height = pic->height;
1098 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1101 // AK07: lets be texel correct on the corners
1103 float horz_offset = 0.5f / pic->width;
1104 float vert_offset = 0.5f / pic->height;
1106 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1107 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1108 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1109 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1114 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1116 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1117 floats[0] = floats[9] = x;
1118 floats[1] = floats[4] = y;
1119 floats[3] = floats[6] = x + width;
1120 floats[7] = floats[10] = y + height;
1122 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1123 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1126 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)
1129 float af = DEG2RAD(-angle); // forward
1130 float ar = DEG2RAD(-angle + 90); // right
1131 float sinaf = sin(af);
1132 float cosaf = cos(af);
1133 float sinar = sin(ar);
1134 float cosar = cos(ar);
1136 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1137 if(!r_draw2d.integer && !r_draw2d_force)
1140 R_Mesh_ResetTextureState();
1146 height = pic->height;
1147 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1150 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1152 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1155 floats[0] = x - cosaf*org_x - cosar*org_y;
1156 floats[1] = y - sinaf*org_x - sinar*org_y;
1159 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1160 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1163 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1164 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1167 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1168 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1170 floats[12] = 0.0f;floats[13] = 0.0f;
1171 floats[14] = 1.0f;floats[15] = 0.0f;
1172 floats[16] = 1.0f;floats[17] = 1.0f;
1173 floats[18] = 0.0f;floats[19] = 1.0f;
1174 floats[20] = floats[24] = floats[28] = floats[32] = red;
1175 floats[21] = floats[25] = floats[29] = floats[33] = green;
1176 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1177 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1179 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1180 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1183 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1187 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1188 if(!r_draw2d.integer && !r_draw2d_force)
1191 R_Mesh_ResetTextureState();
1192 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1194 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1195 floats[0] = floats[9] = x;
1196 floats[1] = floats[4] = y;
1197 floats[3] = floats[6] = x + width;
1198 floats[7] = floats[10] = y + height;
1199 floats[12] = 0.0f;floats[13] = 0.0f;
1200 floats[14] = 1.0f;floats[15] = 0.0f;
1201 floats[16] = 1.0f;floats[17] = 1.0f;
1202 floats[18] = 0.0f;floats[19] = 1.0f;
1203 floats[20] = floats[24] = floats[28] = floats[32] = red;
1204 floats[21] = floats[25] = floats[29] = floats[33] = green;
1205 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1206 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1208 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1209 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1212 /// color tag printing
1213 static const vec4_t string_colors[] =
1216 // LordHavoc: why on earth is cyan before magenta in Quake3?
1217 // LordHavoc: note: Doom3 uses white for [0] and [7]
1218 {0.0, 0.0, 0.0, 1.0}, // black
1219 {1.0, 0.0, 0.0, 1.0}, // red
1220 {0.0, 1.0, 0.0, 1.0}, // green
1221 {1.0, 1.0, 0.0, 1.0}, // yellow
1222 {0.0, 0.0, 1.0, 1.0}, // blue
1223 {0.0, 1.0, 1.0, 1.0}, // cyan
1224 {1.0, 0.0, 1.0, 1.0}, // magenta
1225 {1.0, 1.0, 1.0, 1.0}, // white
1226 // [515]'s BX_COLOREDTEXT extension
1227 {1.0, 1.0, 1.0, 0.5}, // half transparent
1228 {0.5, 0.5, 0.5, 1.0} // half brightness
1229 // Black's color table
1230 //{1.0, 1.0, 1.0, 1.0},
1231 //{1.0, 0.0, 0.0, 1.0},
1232 //{0.0, 1.0, 0.0, 1.0},
1233 //{0.0, 0.0, 1.0, 1.0},
1234 //{1.0, 1.0, 0.0, 1.0},
1235 //{0.0, 1.0, 1.0, 1.0},
1236 //{1.0, 0.0, 1.0, 1.0},
1237 //{0.1, 0.1, 0.1, 1.0}
1240 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1242 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1244 float C = r_textcontrast.value;
1245 float B = r_textbrightness.value;
1246 if (colorindex & 0x10000) // that bit means RGB color
1248 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1249 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1250 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1251 color[3] = (colorindex & 0xf) / 15.0;
1254 Vector4Copy(string_colors[colorindex], color);
1255 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1258 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1259 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1263 // NOTE: this function always draws exactly one character if maxwidth <= 0
1264 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)
1266 const char *text_start = text;
1267 int colorindex = STRING_COLOR_DEFAULT;
1270 Uchar ch, mapch, nextch;
1271 Uchar prevch = 0; // used for kerning
1276 ft2_font_map_t *fontmap = NULL;
1277 ft2_font_map_t *map = NULL;
1278 //ft2_font_map_t *prevmap = NULL;
1279 ft2_font_t *ft2 = fnt->ft2;
1281 qboolean snap = true;
1282 qboolean least_one = false;
1283 float dw; // display w
1284 //float dh; // display h
1285 const float *width_of;
1292 // do this in the end
1293 w *= fnt->settings.scale;
1294 h *= fnt->settings.scale;
1296 // find the most fitting size:
1300 map_index = Font_IndexForSize(ft2, h, &w, &h);
1302 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1303 fontmap = Font_MapForIndex(ft2, map_index);
1312 if (!outcolor || *outcolor == -1)
1313 colorindex = STRING_COLOR_DEFAULT;
1315 colorindex = *outcolor;
1317 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1318 // ftbase_x = snap_to_pixel_x(0);
1323 maxwidth = -maxwidth;
1327 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1330 width_of = fontmap->width_of;
1332 width_of = fnt->width_of;
1334 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1337 nextch = ch = u8_getnchar(text, &text, bytes_left);
1338 i = text - text_start;
1341 if (ch == ' ' && !fontmap)
1343 if(!least_one || i0) // never skip the first character
1344 if(x + width_of[(int) ' '] * dw > maxwidth)
1347 break; // oops, can't draw this
1349 x += width_of[(int) ' '] * dw;
1352 // i points to the char after ^
1353 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1355 ch = *text; // colors are ascii, so no u8_ needed
1356 if (ch <= '9' && ch >= '0') // ^[0-9] found
1358 colorindex = ch - '0';
1363 // i points to the char after ^...
1364 // i+3 points to 3 in ^x123
1365 // i+3 == *maxlen would mean that char is missing
1366 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1368 // building colorindex...
1369 ch = tolower(text[1]);
1370 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1371 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1372 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1373 else tempcolorindex = 0;
1376 ch = tolower(text[2]);
1377 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1378 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1379 else tempcolorindex = 0;
1382 ch = tolower(text[3]);
1383 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1384 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1385 else tempcolorindex = 0;
1388 colorindex = tempcolorindex | 0xf;
1389 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1397 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1406 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1413 map = ft2_oldstyle_map;
1415 if(!least_one || i0) // never skip the first character
1416 if(x + width_of[ch] * dw > maxwidth)
1419 break; // oops, can't draw this
1421 x += width_of[ch] * dw;
1423 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1425 map = FontMap_FindForChar(fontmap, ch);
1428 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1434 mapch = ch - map->start;
1435 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1437 x += map->glyphs[mapch].advance_x * dw;
1446 *outcolor = colorindex;
1451 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)
1453 int shadow, colorindex = STRING_COLOR_DEFAULT;
1455 float x = startx, y, s, t, u, v, thisw;
1456 float *av, *at, *ac;
1459 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1460 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1461 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1462 Uchar ch, mapch, nextch;
1463 Uchar prevch = 0; // used for kerning
1466 //ft2_font_map_t *prevmap = NULL; // the previous map
1467 ft2_font_map_t *map = NULL; // the currently used map
1468 ft2_font_map_t *fontmap = NULL; // the font map for the size
1470 const char *text_start = text;
1472 ft2_font_t *ft2 = fnt->ft2;
1473 qboolean snap = true;
1477 const float *width_of;
1480 tw = R_TextureWidth(fnt->tex);
1481 th = R_TextureHeight(fnt->tex);
1489 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1490 w *= fnt->settings.scale;
1491 h *= fnt->settings.scale;
1496 map_index = Font_IndexForSize(ft2, h, &w, &h);
1498 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1499 fontmap = Font_MapForIndex(ft2, map_index);
1505 // draw the font at its baseline when using freetype
1507 ftbase_y = dh * (4.5/6.0);
1512 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1513 if(!r_draw2d.integer && !r_draw2d_force)
1514 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1516 R_Mesh_ResetTextureState();
1518 R_Mesh_TexBind(0, fnt->tex);
1519 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1526 //ftbase_x = snap_to_pixel_x(ftbase_x);
1529 startx = snap_to_pixel_x(startx, 0.4);
1530 starty = snap_to_pixel_y(starty, 0.4);
1531 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1534 pix_x = vid.width / vid_conwidth.value;
1535 pix_y = vid.height / vid_conheight.value;
1538 width_of = fontmap->width_of;
1540 width_of = fnt->width_of;
1542 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1547 if (!outcolor || *outcolor == -1)
1548 colorindex = STRING_COLOR_DEFAULT;
1550 colorindex = *outcolor;
1552 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1559 x += r_textshadow.value * vid.width / vid_conwidth.value;
1560 y += r_textshadow.value * vid.height / vid_conheight.value;
1563 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1565 nextch = ch = u8_getnchar(text, &text, bytes_left);
1566 i = text - text_start;
1569 if (ch == ' ' && !fontmap)
1571 x += width_of[(int) ' '] * dw;
1574 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1576 ch = *text; // colors are ascii, so no u8_ needed
1577 if (ch <= '9' && ch >= '0') // ^[0-9] found
1579 colorindex = ch - '0';
1580 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1585 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1587 // building colorindex...
1588 ch = tolower(text[1]);
1589 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1590 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1591 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1592 else tempcolorindex = 0;
1595 ch = tolower(text[2]);
1596 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1597 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1598 else tempcolorindex = 0;
1601 ch = tolower(text[3]);
1602 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1603 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1604 else tempcolorindex = 0;
1607 colorindex = tempcolorindex | 0xf;
1608 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1609 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1610 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1618 else if (ch == STRING_COLOR_TAG)
1627 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1628 // this way we don't need to rebind fnt->tex for every old-style character
1629 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1632 x += 1.0/pix_x * r_textshadow.value;
1633 y += 1.0/pix_y * r_textshadow.value;
1635 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1643 if (map != ft2_oldstyle_map)
1647 // switching from freetype to non-freetype rendering
1648 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1649 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1655 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1656 map = ft2_oldstyle_map;
1660 //num = (unsigned char) text[i];
1661 //thisw = fnt->width_of[num];
1662 thisw = fnt->width_of[ch];
1663 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1664 s = (ch & 15)*0.0625f + (0.5f / tw);
1665 t = (ch >> 4)*0.0625f + (0.5f / th);
1666 u = 0.0625f * thisw - (1.0f / tw);
1667 v = 0.0625f - (1.0f / th);
1668 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1669 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1670 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1671 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1672 at[ 0] = s ; at[ 1] = t ;
1673 at[ 2] = s+u ; at[ 3] = t ;
1674 at[ 4] = s+u ; at[ 5] = t+v ;
1675 at[ 6] = s ; at[ 7] = t+v ;
1676 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1677 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1678 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1679 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1684 if (batchcount >= QUADELEMENTS_MAXQUADS)
1686 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1687 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1693 x += width_of[ch] * dw;
1695 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1697 // new charmap - need to render
1700 // we need a different character map, render what we currently have:
1701 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1702 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1709 map = FontMap_FindForChar(fontmap, ch);
1712 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1719 // this shouldn't happen
1724 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1727 mapch = ch - map->start;
1728 thisw = map->glyphs[mapch].advance_x;
1732 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1739 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1740 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1741 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1742 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1743 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1744 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1745 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1746 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1747 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1748 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1749 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1750 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1759 if (batchcount >= QUADELEMENTS_MAXQUADS)
1761 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1762 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1774 x -= 1.0/pix_x * r_textshadow.value;
1775 y -= 1.0/pix_y * r_textshadow.value;
1781 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1782 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1786 *outcolor = colorindex;
1788 // note: this relies on the proper text (not shadow) being drawn last
1792 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)
1794 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1797 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)
1799 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1802 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1804 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1807 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1809 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1814 // no ^xrgb management
1815 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1817 int color, numchars = 0;
1818 char *outputend2c = output2c + maxoutchars - 2;
1819 if (!outcolor || *outcolor == -1)
1820 color = STRING_COLOR_DEFAULT;
1824 maxreadchars = 1<<30;
1825 textend = text + maxreadchars;
1826 while (text != textend && *text)
1828 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1830 if (text[1] == STRING_COLOR_TAG)
1832 else if (text[1] >= '0' && text[1] <= '9')
1834 color = text[1] - '0';
1839 if (output2c >= outputend2c)
1841 *output2c++ = *text++;
1842 *output2c++ = color;
1845 output2c[0] = output2c[1] = 0;
1852 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)
1856 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1857 if(!r_draw2d.integer && !r_draw2d_force)
1860 R_Mesh_ResetTextureState();
1866 height = pic->height;
1867 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1870 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1872 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1873 floats[0] = floats[9] = x;
1874 floats[1] = floats[4] = y;
1875 floats[3] = floats[6] = x + width;
1876 floats[7] = floats[10] = y + height;
1877 floats[12] = s1;floats[13] = t1;
1878 floats[14] = s2;floats[15] = t2;
1879 floats[16] = s4;floats[17] = t4;
1880 floats[18] = s3;floats[19] = t3;
1881 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1882 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1883 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1884 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1886 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1887 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1890 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1894 if(!r_draw2d.integer && !r_draw2d_force)
1896 DrawQ_ProcessDrawFlag(flags, hasalpha);
1898 R_Mesh_ResetTextureState();
1899 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1901 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1902 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1905 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1909 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1910 if(!r_draw2d.integer && !r_draw2d_force)
1914 switch(vid.renderpath)
1916 case RENDERPATH_GL11:
1917 case RENDERPATH_GL13:
1918 case RENDERPATH_GL20:
1919 case RENDERPATH_CGGL:
1921 qglBegin(GL_LINE_LOOP);
1922 for (num = 0;num < mesh->num_vertices;num++)
1924 if (mesh->data_color4f)
1925 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]);
1926 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1931 case RENDERPATH_D3D9:
1932 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1934 case RENDERPATH_D3D10:
1935 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1937 case RENDERPATH_D3D11:
1938 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1943 //[515]: this is old, delete
1944 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1946 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1947 if(!r_draw2d.integer && !r_draw2d_force)
1950 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1952 switch(vid.renderpath)
1954 case RENDERPATH_GL11:
1955 case RENDERPATH_GL13:
1956 case RENDERPATH_GL20:
1957 case RENDERPATH_CGGL:
1960 //qglLineWidth(width);CHECKGLERROR
1962 GL_Color(r,g,b,alpha);
1965 qglVertex2f(x1, y1);
1966 qglVertex2f(x2, y2);
1970 case RENDERPATH_D3D9:
1971 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1973 case RENDERPATH_D3D10:
1974 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1976 case RENDERPATH_D3D11:
1977 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1982 void DrawQ_SetClipArea(float x, float y, float width, float height)
1987 // We have to convert the con coords into real coords
1988 // OGL uses top to bottom
1989 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1990 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1991 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1992 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1993 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1995 GL_ScissorTest(true);
1998 void DrawQ_ResetClipArea(void)
2001 GL_ScissorTest(false);
2004 void DrawQ_Finish(void)
2006 r_refdef.draw2dstage = 0;
2009 void DrawQ_RecalcView(void)
2011 if(r_refdef.draw2dstage)
2012 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2015 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2016 void R_DrawGamma(void)
2019 switch(vid.renderpath)
2021 case RENDERPATH_GL20:
2022 case RENDERPATH_CGGL:
2023 case RENDERPATH_D3D9:
2024 case RENDERPATH_D3D10:
2025 case RENDERPATH_D3D11:
2026 if (vid_usinghwgamma || v_glslgamma.integer)
2029 case RENDERPATH_GL13:
2030 case RENDERPATH_GL11:
2031 if (vid_usinghwgamma)
2035 // all the blends ignore depth
2036 R_Mesh_ResetTextureState();
2037 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2039 GL_DepthRange(0, 1);
2040 GL_PolygonOffset(0, 0);
2041 GL_DepthTest(false);
2042 if (v_color_enable.integer)
2044 c[0] = v_color_white_r.value;
2045 c[1] = v_color_white_g.value;
2046 c[2] = v_color_white_b.value;
2049 c[0] = c[1] = c[2] = v_contrast.value;
2050 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2052 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2053 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2055 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2056 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2057 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2058 VectorScale(c, 0.5, c);
2061 if (v_color_enable.integer)
2063 c[0] = v_color_black_r.value;
2064 c[1] = v_color_black_g.value;
2065 c[2] = v_color_black_b.value;
2068 c[0] = c[1] = c[2] = v_brightness.value;
2069 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2071 GL_BlendFunc(GL_ONE, GL_ONE);
2072 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2073 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2074 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);