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, 0, width, height, 1);
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);
1031 qboolean r_draw2d_force = false;
1032 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1036 if(!r_draw2d.integer && !r_draw2d_force)
1038 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1040 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1042 if(flags == DRAWFLAG_ADDITIVE)
1044 GL_DepthMask(false);
1045 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1047 else if(flags == DRAWFLAG_MODULATE)
1049 GL_DepthMask(false);
1050 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1052 else if(flags == DRAWFLAG_2XMODULATE)
1054 GL_DepthMask(false);
1055 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1057 else if(flags == DRAWFLAG_SCREEN)
1059 GL_DepthMask(false);
1060 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1064 GL_DepthMask(false);
1065 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1070 GL_BlendFunc(GL_ONE, GL_ZERO);
1074 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1078 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1079 if(!r_draw2d.integer && !r_draw2d_force)
1082 // R_Mesh_ResetTextureState();
1083 floats[12] = 0.0f;floats[13] = 0.0f;
1084 floats[14] = 1.0f;floats[15] = 0.0f;
1085 floats[16] = 1.0f;floats[17] = 1.0f;
1086 floats[18] = 0.0f;floats[19] = 1.0f;
1087 floats[20] = floats[24] = floats[28] = floats[32] = red;
1088 floats[21] = floats[25] = floats[29] = floats[33] = green;
1089 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1090 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1096 height = pic->height;
1097 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1100 // AK07: lets be texel correct on the corners
1102 float horz_offset = 0.5f / pic->width;
1103 float vert_offset = 0.5f / pic->height;
1105 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1106 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1107 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1108 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1113 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1115 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1116 floats[0] = floats[9] = x;
1117 floats[1] = floats[4] = y;
1118 floats[3] = floats[6] = x + width;
1119 floats[7] = floats[10] = y + height;
1121 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1122 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1125 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)
1128 float af = DEG2RAD(-angle); // forward
1129 float ar = DEG2RAD(-angle + 90); // right
1130 float sinaf = sin(af);
1131 float cosaf = cos(af);
1132 float sinar = sin(ar);
1133 float cosar = cos(ar);
1135 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1136 if(!r_draw2d.integer && !r_draw2d_force)
1139 // R_Mesh_ResetTextureState();
1145 height = pic->height;
1146 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1149 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1151 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1154 floats[0] = x - cosaf*org_x - cosar*org_y;
1155 floats[1] = y - sinaf*org_x - sinar*org_y;
1158 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1159 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1162 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1163 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1166 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1167 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1169 floats[12] = 0.0f;floats[13] = 0.0f;
1170 floats[14] = 1.0f;floats[15] = 0.0f;
1171 floats[16] = 1.0f;floats[17] = 1.0f;
1172 floats[18] = 0.0f;floats[19] = 1.0f;
1173 floats[20] = floats[24] = floats[28] = floats[32] = red;
1174 floats[21] = floats[25] = floats[29] = floats[33] = green;
1175 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1176 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1178 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1179 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1182 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1186 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1187 if(!r_draw2d.integer && !r_draw2d_force)
1190 // R_Mesh_ResetTextureState();
1191 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1193 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1194 floats[0] = floats[9] = x;
1195 floats[1] = floats[4] = y;
1196 floats[3] = floats[6] = x + width;
1197 floats[7] = floats[10] = y + height;
1198 floats[12] = 0.0f;floats[13] = 0.0f;
1199 floats[14] = 1.0f;floats[15] = 0.0f;
1200 floats[16] = 1.0f;floats[17] = 1.0f;
1201 floats[18] = 0.0f;floats[19] = 1.0f;
1202 floats[20] = floats[24] = floats[28] = floats[32] = red;
1203 floats[21] = floats[25] = floats[29] = floats[33] = green;
1204 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1205 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1207 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1208 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1211 /// color tag printing
1212 static const vec4_t string_colors[] =
1215 // LordHavoc: why on earth is cyan before magenta in Quake3?
1216 // LordHavoc: note: Doom3 uses white for [0] and [7]
1217 {0.0, 0.0, 0.0, 1.0}, // black
1218 {1.0, 0.0, 0.0, 1.0}, // red
1219 {0.0, 1.0, 0.0, 1.0}, // green
1220 {1.0, 1.0, 0.0, 1.0}, // yellow
1221 {0.0, 0.0, 1.0, 1.0}, // blue
1222 {0.0, 1.0, 1.0, 1.0}, // cyan
1223 {1.0, 0.0, 1.0, 1.0}, // magenta
1224 {1.0, 1.0, 1.0, 1.0}, // white
1225 // [515]'s BX_COLOREDTEXT extension
1226 {1.0, 1.0, 1.0, 0.5}, // half transparent
1227 {0.5, 0.5, 0.5, 1.0} // half brightness
1228 // Black's color table
1229 //{1.0, 1.0, 1.0, 1.0},
1230 //{1.0, 0.0, 0.0, 1.0},
1231 //{0.0, 1.0, 0.0, 1.0},
1232 //{0.0, 0.0, 1.0, 1.0},
1233 //{1.0, 1.0, 0.0, 1.0},
1234 //{0.0, 1.0, 1.0, 1.0},
1235 //{1.0, 0.0, 1.0, 1.0},
1236 //{0.1, 0.1, 0.1, 1.0}
1239 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1241 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1243 float C = r_textcontrast.value;
1244 float B = r_textbrightness.value;
1245 if (colorindex & 0x10000) // that bit means RGB color
1247 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1248 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1249 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1250 color[3] = (colorindex & 0xf) / 15.0;
1253 Vector4Copy(string_colors[colorindex], color);
1254 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1257 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1258 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1262 // NOTE: this function always draws exactly one character if maxwidth <= 0
1263 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)
1265 const char *text_start = text;
1266 int colorindex = STRING_COLOR_DEFAULT;
1269 Uchar ch, mapch, nextch;
1270 Uchar prevch = 0; // used for kerning
1275 ft2_font_map_t *fontmap = NULL;
1276 ft2_font_map_t *map = NULL;
1277 //ft2_font_map_t *prevmap = NULL;
1278 ft2_font_t *ft2 = fnt->ft2;
1280 qboolean snap = true;
1281 qboolean least_one = false;
1282 float dw; // display w
1283 //float dh; // display h
1284 const float *width_of;
1291 // do this in the end
1292 w *= fnt->settings.scale;
1293 h *= fnt->settings.scale;
1295 // find the most fitting size:
1299 map_index = Font_IndexForSize(ft2, h, &w, &h);
1301 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1302 fontmap = Font_MapForIndex(ft2, map_index);
1311 if (!outcolor || *outcolor == -1)
1312 colorindex = STRING_COLOR_DEFAULT;
1314 colorindex = *outcolor;
1316 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1317 // ftbase_x = snap_to_pixel_x(0);
1322 maxwidth = -maxwidth;
1326 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1329 width_of = fontmap->width_of;
1331 width_of = fnt->width_of;
1333 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1336 nextch = ch = u8_getnchar(text, &text, bytes_left);
1337 i = text - text_start;
1340 if (ch == ' ' && !fontmap)
1342 if(!least_one || i0) // never skip the first character
1343 if(x + width_of[(int) ' '] * dw > maxwidth)
1346 break; // oops, can't draw this
1348 x += width_of[(int) ' '] * dw;
1351 // i points to the char after ^
1352 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1354 ch = *text; // colors are ascii, so no u8_ needed
1355 if (ch <= '9' && ch >= '0') // ^[0-9] found
1357 colorindex = ch - '0';
1362 // i points to the char after ^...
1363 // i+3 points to 3 in ^x123
1364 // i+3 == *maxlen would mean that char is missing
1365 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1367 // building colorindex...
1368 ch = tolower(text[1]);
1369 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1370 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1371 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1372 else tempcolorindex = 0;
1375 ch = tolower(text[2]);
1376 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1377 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1378 else tempcolorindex = 0;
1381 ch = tolower(text[3]);
1382 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1383 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1384 else tempcolorindex = 0;
1387 colorindex = tempcolorindex | 0xf;
1388 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1396 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1405 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1412 map = ft2_oldstyle_map;
1414 if(!least_one || i0) // never skip the first character
1415 if(x + width_of[ch] * dw > maxwidth)
1418 break; // oops, can't draw this
1420 x += width_of[ch] * dw;
1422 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1424 map = FontMap_FindForChar(fontmap, ch);
1427 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1433 mapch = ch - map->start;
1434 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1436 x += map->glyphs[mapch].advance_x * dw;
1445 *outcolor = colorindex;
1450 float DrawQ_Color[4];
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;
1458 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1459 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1460 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1461 Uchar ch, mapch, nextch;
1462 Uchar prevch = 0; // used for kerning
1465 //ft2_font_map_t *prevmap = NULL; // the previous map
1466 ft2_font_map_t *map = NULL; // the currently used map
1467 ft2_font_map_t *fontmap = NULL; // the font map for the size
1469 const char *text_start = text;
1471 ft2_font_t *ft2 = fnt->ft2;
1472 qboolean snap = true;
1476 const float *width_of;
1479 tw = R_TextureWidth(fnt->tex);
1480 th = R_TextureHeight(fnt->tex);
1488 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1489 w *= fnt->settings.scale;
1490 h *= fnt->settings.scale;
1495 map_index = Font_IndexForSize(ft2, h, &w, &h);
1497 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1498 fontmap = Font_MapForIndex(ft2, map_index);
1504 // draw the font at its baseline when using freetype
1506 ftbase_y = dh * (4.5/6.0);
1511 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1512 if(!r_draw2d.integer && !r_draw2d_force)
1513 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1515 // R_Mesh_ResetTextureState();
1517 R_Mesh_TexBind(0, fnt->tex);
1518 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1525 //ftbase_x = snap_to_pixel_x(ftbase_x);
1528 startx = snap_to_pixel_x(startx, 0.4);
1529 starty = snap_to_pixel_y(starty, 0.4);
1530 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1533 pix_x = vid.width / vid_conwidth.value;
1534 pix_y = vid.height / vid_conheight.value;
1537 width_of = fontmap->width_of;
1539 width_of = fnt->width_of;
1541 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1546 if (!outcolor || *outcolor == -1)
1547 colorindex = STRING_COLOR_DEFAULT;
1549 colorindex = *outcolor;
1551 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1558 x += r_textshadow.value * vid.width / vid_conwidth.value;
1559 y += r_textshadow.value * vid.height / vid_conheight.value;
1562 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1564 nextch = ch = u8_getnchar(text, &text, bytes_left);
1565 i = text - text_start;
1568 if (ch == ' ' && !fontmap)
1570 x += width_of[(int) ' '] * dw;
1573 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1575 ch = *text; // colors are ascii, so no u8_ needed
1576 if (ch <= '9' && ch >= '0') // ^[0-9] found
1578 colorindex = ch - '0';
1579 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1584 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1586 // building colorindex...
1587 ch = tolower(text[1]);
1588 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1589 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1590 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1591 else tempcolorindex = 0;
1594 ch = tolower(text[2]);
1595 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1596 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1597 else tempcolorindex = 0;
1600 ch = tolower(text[3]);
1601 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1602 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1603 else tempcolorindex = 0;
1606 colorindex = tempcolorindex | 0xf;
1607 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1608 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1609 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1617 else if (ch == STRING_COLOR_TAG)
1626 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1627 // this way we don't need to rebind fnt->tex for every old-style character
1628 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1631 x += 1.0/pix_x * r_textshadow.value;
1632 y += 1.0/pix_y * r_textshadow.value;
1634 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1642 if (map != ft2_oldstyle_map)
1646 // switching from freetype to non-freetype rendering
1647 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1648 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1654 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1655 map = ft2_oldstyle_map;
1659 //num = (unsigned char) text[i];
1660 //thisw = fnt->width_of[num];
1661 thisw = fnt->width_of[ch];
1662 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1663 s = (ch & 15)*0.0625f + (0.5f / tw);
1664 t = (ch >> 4)*0.0625f + (0.5f / th);
1665 u = 0.0625f * thisw - (1.0f / tw);
1666 v = 0.0625f - (1.0f / th);
1667 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1668 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1669 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1670 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1671 at[ 0] = s ; at[ 1] = t ;
1672 at[ 2] = s+u ; at[ 3] = t ;
1673 at[ 4] = s+u ; at[ 5] = t+v ;
1674 at[ 6] = s ; at[ 7] = t+v ;
1675 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1676 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1677 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1678 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1683 if (batchcount >= QUADELEMENTS_MAXQUADS)
1685 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1686 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1692 x += width_of[ch] * dw;
1694 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1696 // new charmap - need to render
1699 // we need a different character map, render what we currently have:
1700 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1701 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1708 map = FontMap_FindForChar(fontmap, ch);
1711 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1718 // this shouldn't happen
1723 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1726 mapch = ch - map->start;
1727 thisw = map->glyphs[mapch].advance_x;
1731 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1738 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1739 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1740 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1741 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1742 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1743 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1744 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1745 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1746 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1747 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1748 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1749 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1758 if (batchcount >= QUADELEMENTS_MAXQUADS)
1760 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1761 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:
1920 qglBegin(GL_LINE_LOOP);
1921 for (num = 0;num < mesh->num_vertices;num++)
1923 if (mesh->data_color4f)
1924 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]);
1925 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1930 case RENDERPATH_D3D9:
1931 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1933 case RENDERPATH_D3D10:
1934 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1936 case RENDERPATH_D3D11:
1937 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1939 case RENDERPATH_SOFT:
1940 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1942 case RENDERPATH_GLES2:
1943 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1948 //[515]: this is old, delete
1949 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1951 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1952 if(!r_draw2d.integer && !r_draw2d_force)
1955 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1957 switch(vid.renderpath)
1959 case RENDERPATH_GL11:
1960 case RENDERPATH_GL13:
1961 case RENDERPATH_GL20:
1964 //qglLineWidth(width);CHECKGLERROR
1966 GL_Color(r,g,b,alpha);
1969 qglVertex2f(x1, y1);
1970 qglVertex2f(x2, y2);
1974 case RENDERPATH_D3D9:
1975 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1977 case RENDERPATH_D3D10:
1978 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1980 case RENDERPATH_D3D11:
1981 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1983 case RENDERPATH_SOFT:
1984 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1986 case RENDERPATH_GLES2:
1987 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1992 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
1995 qboolean hasalpha = false;
1996 for (i = 0;i < numlines*2;i++)
1997 if (color4f[i*4+3] < 1.0f)
2000 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2002 if(!r_draw2d.integer && !r_draw2d_force)
2005 switch(vid.renderpath)
2007 case RENDERPATH_GL11:
2008 case RENDERPATH_GL13:
2009 case RENDERPATH_GL20:
2012 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2014 //qglLineWidth(width);CHECKGLERROR
2017 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2018 qglDrawArrays(GL_LINES, 0, numlines*2);
2021 case RENDERPATH_D3D9:
2022 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2024 case RENDERPATH_D3D10:
2025 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2027 case RENDERPATH_D3D11:
2028 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2030 case RENDERPATH_SOFT:
2031 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2033 case RENDERPATH_GLES2:
2034 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2039 void DrawQ_SetClipArea(float x, float y, float width, float height)
2044 // We have to convert the con coords into real coords
2045 // OGL uses top to bottom
2046 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2047 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2048 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2049 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2050 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2052 GL_ScissorTest(true);
2055 void DrawQ_ResetClipArea(void)
2058 GL_ScissorTest(false);
2061 void DrawQ_Finish(void)
2063 r_refdef.draw2dstage = 0;
2066 void DrawQ_RecalcView(void)
2068 if(r_refdef.draw2dstage)
2069 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2072 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2073 void R_DrawGamma(void)
2076 switch(vid.renderpath)
2078 case RENDERPATH_GL20:
2079 case RENDERPATH_D3D9:
2080 case RENDERPATH_D3D10:
2081 case RENDERPATH_D3D11:
2082 case RENDERPATH_GLES2:
2083 if (vid_usinghwgamma || v_glslgamma.integer)
2086 case RENDERPATH_GL13:
2087 case RENDERPATH_GL11:
2088 if (vid_usinghwgamma)
2091 case RENDERPATH_SOFT:
2094 // all the blends ignore depth
2095 // R_Mesh_ResetTextureState();
2096 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
2098 GL_DepthRange(0, 1);
2099 GL_PolygonOffset(0, 0);
2100 GL_DepthTest(false);
2101 if (v_color_enable.integer)
2103 c[0] = v_color_white_r.value;
2104 c[1] = v_color_white_g.value;
2105 c[2] = v_color_white_b.value;
2108 c[0] = c[1] = c[2] = v_contrast.value;
2109 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2111 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2112 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2114 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2115 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2116 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2117 VectorScale(c, 0.5, c);
2120 if (v_color_enable.integer)
2122 c[0] = v_color_black_r.value;
2123 c[1] = v_color_black_g.value;
2124 c[2] = v_color_black_b.value;
2127 c[0] = c[1] = c[2] = v_brightness.value;
2128 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2130 GL_BlendFunc(GL_ONE, GL_ONE);
2131 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2132 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2133 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);