2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include "cl_dyntexture.h"
29 #include "ft2_fontdefs.h"
32 static mempool_t *fonts_mempool = NULL;
34 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
35 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
36 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
46 extern cvar_t v_glslgamma;
48 //=============================================================================
49 /* Support Routines */
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
56 static rtexturepool_t *drawtexturepool;
58 static const unsigned char concharimage[FONT_FILESIZE] =
63 static rtexture_t *draw_generateconchars(void)
70 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
72 for (i = 0;i < 8192;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 83 + (unsigned char)(random * 64);
77 data[i*4+1] = 71 + (unsigned char)(random * 32);
78 data[i*4+0] = 23 + (unsigned char)(random * 16);
81 for (i = 8192;i < 32768;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 95 + (unsigned char)(random * 64);
86 data[i*4+1] = 95 + (unsigned char)(random * 64);
87 data[i*4+0] = 95 + (unsigned char)(random * 64);
90 for (i = 32768;i < 40960;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 83 + (unsigned char)(random * 64);
95 data[i*4+1] = 71 + (unsigned char)(random * 32);
96 data[i*4+0] = 23 + (unsigned char)(random * 16);
99 for (i = 40960;i < 65536;i++)
101 random = lhrandom (0.0,1.0);
102 data[i*4+3] = data[i*4+0];
103 data[i*4+2] = 96 + (unsigned char)(random * 64);
104 data[i*4+1] = 43 + (unsigned char)(random * 32);
105 data[i*4+0] = 27 + (unsigned char)(random * 32);
109 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
112 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
117 static rtexture_t *draw_generateditherpattern(void)
120 unsigned char pixels[8][8];
121 for (y = 0;y < 8;y++)
122 for (x = 0;x < 8;x++)
123 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
127 typedef struct embeddedpic_s
136 static const embeddedpic_t embeddedpics[] =
139 "gfx/prydoncursor001", 16, 16,
158 "ui/mousepointer", 16, 16,
177 "gfx/crosshair1", 16, 16,
196 "gfx/crosshair2", 16, 16,
215 "gfx/crosshair3", 16, 16,
234 "gfx/crosshair4", 16, 16,
253 "gfx/crosshair5", 8, 8,
264 "gfx/crosshair6", 2, 2,
269 "gfx/crosshair7", 16, 16,
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
292 const embeddedpic_t *p;
293 for (p = embeddedpics;p->name;p++)
294 if (!strcmp(name, p->name))
295 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
311 // FIXME: move this to client somehow
312 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
315 unsigned char *pixels;
318 unsigned char *lmpdata;
319 char lmpname[MAX_QPATH];
323 texflags = TEXF_ALPHA;
324 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
325 texflags |= TEXF_CLAMP;
326 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
327 texflags |= TEXF_COMPRESS;
329 // check whether the picture has already been cached
330 crc = CRC_Block((unsigned char *)path, strlen(path));
331 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
332 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
333 if (!strcmp (path, pic->name))
334 if(!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
336 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
339 pic->autoload = false; // persist it
341 goto reload; // load it below, and then persist
346 if (numcachepics == MAX_CACHED_PICS)
348 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
349 // FIXME: support NULL in callers?
350 return cachepics; // return the first one
352 pic = cachepics + (numcachepics++);
353 strlcpy (pic->name, path, sizeof(pic->name));
355 pic->chain = cachepichash[hashkey];
356 cachepichash[hashkey] = pic;
359 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
360 pic->tex = CL_GetDynTexture( path );
361 // if so, set the width/height, too
363 pic->width = R_TextureWidth(pic->tex);
364 pic->height = R_TextureHeight(pic->tex);
365 // we're done now (early-out)
369 pic->hasalpha = true; // assume alpha unless we know it has none
370 pic->texflags = texflags;
371 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
373 // load a high quality image from disk if possible
374 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer, NULL);
375 if (pixels == NULL && !strncmp(path, "gfx/", 4))
376 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer, NULL);
379 pic->hasalpha = false;
380 if (pic->texflags & TEXF_ALPHA)
382 for (j = 3;j < image_width * image_height * 4;j += 4)
386 pic->hasalpha = true;
392 pic->width = image_width;
393 pic->height = image_height;
395 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
399 pic->autoload = false;
400 // never compress the fallback images
401 pic->texflags &= ~TEXF_COMPRESS;
404 // now read the low quality version (wad or lmp file), and take the pic
405 // size from that even if we don't upload the texture, this way the pics
406 // show up the right size in the menu even if they were replaced with
407 // higher or lower resolution versions
408 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
409 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
411 if (developer_loading.integer)
412 Con_Printf("loading lump \"%s\"\n", path);
416 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
417 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
418 // if no high quality replacement image was found, upload the original low quality texture
420 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
424 else if ((lmpdata = W_GetLumpName (path + 4)))
426 if (developer_loading.integer)
427 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
429 if (!strcmp(path, "gfx/conchars"))
431 // conchars is a raw image and with color 0 as transparent instead of 255
434 // if no high quality replacement image was found, upload the original low quality texture
436 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
440 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
441 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
442 // if no high quality replacement image was found, upload the original low quality texture
444 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
453 else if (pic->tex == NULL)
455 // if it's not found on disk, generate an image
456 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
457 pic->width = R_TextureWidth(pic->tex);
458 pic->height = R_TextureHeight(pic->tex);
464 cachepic_t *Draw_CachePic (const char *path)
466 return Draw_CachePic_Flags (path, 0); // default to persistent!
471 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
473 if (pic->autoload && !pic->tex)
475 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
476 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
477 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
478 if (pic->tex == NULL)
479 pic->tex = draw_generatepic(pic->name, true);
481 pic->lastusedframe = draw_frame;
485 void Draw_Frame(void)
489 static double nextpurgetime;
490 if (nextpurgetime > realtime)
492 nextpurgetime = realtime + 0.05;
493 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
495 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
497 R_FreeTexture(pic->tex);
504 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
509 crc = CRC_Block((unsigned char *)picname, strlen(picname));
510 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
511 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
512 if (!strcmp (picname, pic->name))
517 if (pic->tex && pic->width == width && pic->height == height)
519 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
527 if (numcachepics == MAX_CACHED_PICS)
529 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
530 // FIXME: support NULL in callers?
531 return cachepics; // return the first one
533 pic = cachepics + (numcachepics++);
534 strlcpy (pic->name, picname, sizeof(pic->name));
536 pic->chain = cachepichash[hashkey];
537 cachepichash[hashkey] = pic;
542 pic->height = height;
544 R_FreeTexture(pic->tex);
545 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
549 void Draw_FreePic(const char *picname)
554 // this doesn't really free the pic, but does free it's texture
555 crc = CRC_Block((unsigned char *)picname, strlen(picname));
556 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
557 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
559 if (!strcmp (picname, pic->name) && pic->tex)
561 R_FreeTexture(pic->tex);
570 static float snap_to_pixel_x(float x, float roundUpAt);
571 extern int con_linewidth; // to force rewrapping
572 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
576 char widthfile[MAX_QPATH];
578 fs_offset_t widthbufsize;
580 if(override || !fnt->texpath[0])
582 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
583 // load the cvars when the font is FIRST loader
584 fnt->settings.scale = scale;
585 fnt->settings.voffset = voffset;
586 fnt->settings.antialias = r_font_antialias.integer;
587 fnt->settings.hinting = r_font_hinting.integer;
588 fnt->settings.outline = r_font_postprocess_outline.value;
589 fnt->settings.blur = r_font_postprocess_blur.value;
590 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
591 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
592 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
595 if (fnt->settings.scale <= 0)
596 fnt->settings.scale = 1;
598 if(drawtexturepool == NULL)
599 return; // before gl_draw_start, so will be loaded later
603 // clear freetype font
604 Font_UnloadFont(fnt->ft2);
609 if(fnt->req_face != -1)
611 if(!Font_LoadFont(fnt->texpath, fnt))
612 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
615 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
616 if(fnt->tex == r_texture_notexture)
618 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
620 if (!fnt->fallbacks[i][0])
622 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
623 if(fnt->tex != r_texture_notexture)
626 if(fnt->tex == r_texture_notexture)
628 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
629 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
632 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
635 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
637 // unspecified width == 1 (base width)
638 for(ch = 0; ch < 256; ++ch)
639 fnt->width_of[ch] = 1;
641 // FIXME load "name.width", if it fails, fill all with 1
642 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
644 float extraspacing = 0;
645 const char *p = widthbuf;
650 if(!COM_ParseToken_Simple(&p, false, false))
668 fnt->width_of[ch] = atof(com_token) + extraspacing;
672 if(!strcmp(com_token, "extraspacing"))
674 if(!COM_ParseToken_Simple(&p, false, false))
676 extraspacing = atof(com_token);
678 else if(!strcmp(com_token, "scale"))
680 if(!COM_ParseToken_Simple(&p, false, false))
682 fnt->settings.scale = atof(com_token);
686 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
687 if(!COM_ParseToken_Simple(&p, false, false))
699 for (i = 0; i < MAX_FONT_SIZES; ++i)
701 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
704 for(ch = 0; ch < 256; ++ch)
705 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
709 maxwidth = fnt->width_of[0];
710 for(i = 1; i < 256; ++i)
711 maxwidth = max(maxwidth, fnt->width_of[i]);
712 fnt->maxwidth = maxwidth;
714 // fix up maxwidth for overlap
715 fnt->maxwidth *= fnt->settings.scale;
717 if(fnt == FONT_CONSOLE)
718 con_linewidth = -1; // rewrap console in next frame
721 extern cvar_t developer_font;
722 dp_font_t *FindFont(const char *title, qboolean allocate_new)
727 for(i = 0; i < dp_fonts.maxsize; ++i)
728 if(!strcmp(dp_fonts.f[i].title, title))
729 return &dp_fonts.f[i];
730 // if not found - try allocate
733 // find any font with empty title
734 for(i = 0; i < dp_fonts.maxsize; ++i)
736 if(!strcmp(dp_fonts.f[i].title, ""))
738 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
739 return &dp_fonts.f[i];
742 // if no any 'free' fonts - expand buffer
743 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 = 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 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1021 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
1022 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
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)
1082 GL_Color(red, green, blue, alpha);
1084 R_Mesh_VertexPointer(floats, 0, 0);
1085 R_Mesh_ColorPointer(NULL, 0, 0);
1086 R_Mesh_ResetTextureState();
1092 height = pic->height;
1093 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1094 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1097 floats[12] = 0.0f;floats[13] = 0.0f;
1098 floats[14] = 1.0f;floats[15] = 0.0f;
1099 floats[16] = 1.0f;floats[17] = 1.0f;
1100 floats[18] = 0.0f;floats[19] = 1.0f;
1102 // AK07: lets be texel correct on the corners
1104 float horz_offset = 0.5f / pic->width;
1105 float vert_offset = 0.5f / pic->height;
1107 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1108 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1109 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1110 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1115 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1117 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1118 floats[0] = floats[9] = x;
1119 floats[1] = floats[4] = y;
1120 floats[3] = floats[6] = x + width;
1121 floats[7] = floats[10] = y + height;
1123 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 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)
1139 GL_Color(red, green, blue, alpha);
1141 R_Mesh_VertexPointer(floats, 0, 0);
1142 R_Mesh_ColorPointer(NULL, 0, 0);
1143 R_Mesh_ResetTextureState();
1149 height = pic->height;
1150 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1151 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1153 floats[12] = 0.0f;floats[13] = 0.0f;
1154 floats[14] = 1.0f;floats[15] = 0.0f;
1155 floats[16] = 1.0f;floats[17] = 1.0f;
1156 floats[18] = 0.0f;floats[19] = 1.0f;
1159 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1161 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1164 floats[0] = x - cosaf*org_x - cosar*org_y;
1165 floats[1] = y - sinaf*org_x - sinar*org_y;
1168 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1169 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1172 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1173 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1176 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1177 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1179 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 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)
1189 GL_Color(red, green, blue, alpha);
1191 R_Mesh_VertexPointer(floats, 0, 0);
1192 R_Mesh_ColorPointer(NULL, 0, 0);
1193 R_Mesh_ResetTextureState();
1194 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1196 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1197 floats[0] = floats[9] = x;
1198 floats[1] = floats[4] = y;
1199 floats[3] = floats[6] = x + width;
1200 floats[7] = floats[10] = y + height;
1202 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1205 /// color tag printing
1206 static const vec4_t string_colors[] =
1209 // LordHavoc: why on earth is cyan before magenta in Quake3?
1210 // LordHavoc: note: Doom3 uses white for [0] and [7]
1211 {0.0, 0.0, 0.0, 1.0}, // black
1212 {1.0, 0.0, 0.0, 1.0}, // red
1213 {0.0, 1.0, 0.0, 1.0}, // green
1214 {1.0, 1.0, 0.0, 1.0}, // yellow
1215 {0.0, 0.0, 1.0, 1.0}, // blue
1216 {0.0, 1.0, 1.0, 1.0}, // cyan
1217 {1.0, 0.0, 1.0, 1.0}, // magenta
1218 {1.0, 1.0, 1.0, 1.0}, // white
1219 // [515]'s BX_COLOREDTEXT extension
1220 {1.0, 1.0, 1.0, 0.5}, // half transparent
1221 {0.5, 0.5, 0.5, 1.0} // half brightness
1222 // Black's color table
1223 //{1.0, 1.0, 1.0, 1.0},
1224 //{1.0, 0.0, 0.0, 1.0},
1225 //{0.0, 1.0, 0.0, 1.0},
1226 //{0.0, 0.0, 1.0, 1.0},
1227 //{1.0, 1.0, 0.0, 1.0},
1228 //{0.0, 1.0, 1.0, 1.0},
1229 //{1.0, 0.0, 1.0, 1.0},
1230 //{0.1, 0.1, 0.1, 1.0}
1233 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1235 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1237 float C = r_textcontrast.value;
1238 float B = r_textbrightness.value;
1239 if (colorindex & 0x10000) // that bit means RGB color
1241 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1242 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1243 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1244 color[3] = (colorindex & 0xf) / 15.0;
1247 Vector4Copy(string_colors[colorindex], color);
1248 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1251 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1252 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1256 // NOTE: this function always draws exactly one character if maxwidth <= 0
1257 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)
1259 const char *text_start = text;
1260 int colorindex = STRING_COLOR_DEFAULT;
1263 Uchar ch, mapch, nextch;
1264 Uchar prevch = 0; // used for kerning
1269 ft2_font_map_t *fontmap = NULL;
1270 ft2_font_map_t *map = NULL;
1271 //ft2_font_map_t *prevmap = NULL;
1272 ft2_font_t *ft2 = fnt->ft2;
1274 qboolean snap = true;
1275 qboolean least_one = false;
1276 float dw; // display w
1277 //float dh; // display h
1278 const float *width_of;
1285 // do this in the end
1286 w *= fnt->settings.scale;
1287 h *= fnt->settings.scale;
1289 // find the most fitting size:
1293 map_index = Font_IndexForSize(ft2, h, &w, &h);
1295 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1296 fontmap = Font_MapForIndex(ft2, map_index);
1305 if (!outcolor || *outcolor == -1)
1306 colorindex = STRING_COLOR_DEFAULT;
1308 colorindex = *outcolor;
1310 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1311 // ftbase_x = snap_to_pixel_x(0);
1316 maxwidth = -maxwidth;
1320 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1323 width_of = fontmap->width_of;
1325 width_of = fnt->width_of;
1327 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1330 nextch = ch = u8_getnchar(text, &text, bytes_left);
1331 i = text - text_start;
1334 if (ch == ' ' && !fontmap)
1336 if(!least_one || i0) // never skip the first character
1337 if(x + width_of[(int) ' '] * dw > maxwidth)
1340 break; // oops, can't draw this
1342 x += width_of[(int) ' '] * dw;
1345 // i points to the char after ^
1346 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1348 ch = *text; // colors are ascii, so no u8_ needed
1349 if (ch <= '9' && ch >= '0') // ^[0-9] found
1351 colorindex = ch - '0';
1356 // i points to the char after ^...
1357 // i+3 points to 3 in ^x123
1358 // i+3 == *maxlen would mean that char is missing
1359 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1361 // building colorindex...
1362 ch = tolower(text[1]);
1363 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1364 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1365 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1366 else tempcolorindex = 0;
1369 ch = tolower(text[2]);
1370 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1371 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1372 else tempcolorindex = 0;
1375 ch = tolower(text[3]);
1376 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1377 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1378 else tempcolorindex = 0;
1381 colorindex = tempcolorindex | 0xf;
1382 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1390 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1399 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1406 map = ft2_oldstyle_map;
1408 if(!least_one || i0) // never skip the first character
1409 if(x + width_of[ch] * dw > maxwidth)
1412 break; // oops, can't draw this
1414 x += width_of[ch] * dw;
1416 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1418 map = FontMap_FindForChar(fontmap, ch);
1421 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1427 mapch = ch - map->start;
1428 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1430 x += map->glyphs[mapch].advance_x * dw;
1439 *outcolor = colorindex;
1444 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)
1446 int shadow, colorindex = STRING_COLOR_DEFAULT;
1448 float x = startx, y, s, t, u, v, thisw;
1449 float *av, *at, *ac;
1452 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1453 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1454 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1455 Uchar ch, mapch, nextch;
1456 Uchar prevch = 0; // used for kerning
1459 //ft2_font_map_t *prevmap = NULL; // the previous map
1460 ft2_font_map_t *map = NULL; // the currently used map
1461 ft2_font_map_t *fontmap = NULL; // the font map for the size
1463 const char *text_start = text;
1465 ft2_font_t *ft2 = fnt->ft2;
1466 qboolean snap = true;
1470 const float *width_of;
1473 tw = R_TextureWidth(fnt->tex);
1474 th = R_TextureHeight(fnt->tex);
1482 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1483 w *= fnt->settings.scale;
1484 h *= fnt->settings.scale;
1489 map_index = Font_IndexForSize(ft2, h, &w, &h);
1491 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1492 fontmap = Font_MapForIndex(ft2, map_index);
1498 // draw the font at its baseline when using freetype
1500 ftbase_y = dh * (4.5/6.0);
1505 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1506 if(!r_draw2d.integer && !r_draw2d_force)
1507 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1509 R_Mesh_ColorPointer(color4f, 0, 0);
1510 R_Mesh_ResetTextureState();
1512 R_Mesh_TexBind(0, fnt->tex);
1513 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1514 R_Mesh_VertexPointer(vertex3f, 0, 0);
1515 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1522 //ftbase_x = snap_to_pixel_x(ftbase_x);
1525 startx = snap_to_pixel_x(startx, 0.4);
1526 starty = snap_to_pixel_y(starty, 0.4);
1527 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1530 pix_x = vid.width / vid_conwidth.value;
1531 pix_y = vid.height / vid_conheight.value;
1534 width_of = fontmap->width_of;
1536 width_of = fnt->width_of;
1538 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1543 if (!outcolor || *outcolor == -1)
1544 colorindex = STRING_COLOR_DEFAULT;
1546 colorindex = *outcolor;
1548 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1555 x += r_textshadow.value * vid.width / vid_conwidth.value;
1556 y += r_textshadow.value * vid.height / vid_conheight.value;
1559 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1561 nextch = ch = u8_getnchar(text, &text, bytes_left);
1562 i = text - text_start;
1565 if (ch == ' ' && !fontmap)
1567 x += width_of[(int) ' '] * dw;
1570 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1572 ch = *text; // colors are ascii, so no u8_ needed
1573 if (ch <= '9' && ch >= '0') // ^[0-9] found
1575 colorindex = ch - '0';
1576 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1581 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1583 // building colorindex...
1584 ch = tolower(text[1]);
1585 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1586 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1587 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1588 else tempcolorindex = 0;
1591 ch = tolower(text[2]);
1592 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1593 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1594 else tempcolorindex = 0;
1597 ch = tolower(text[3]);
1598 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1599 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1600 else tempcolorindex = 0;
1603 colorindex = tempcolorindex | 0xf;
1604 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1605 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1606 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1614 else if (ch == STRING_COLOR_TAG)
1623 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1624 // this way we don't need to rebind fnt->tex for every old-style character
1625 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1628 x += 1.0/pix_x * r_textshadow.value;
1629 y += 1.0/pix_y * r_textshadow.value;
1631 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1639 if (map != ft2_oldstyle_map)
1643 // switching from freetype to non-freetype rendering
1644 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1650 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1651 map = ft2_oldstyle_map;
1655 //num = (unsigned char) text[i];
1656 //thisw = fnt->width_of[num];
1657 thisw = fnt->width_of[ch];
1658 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1659 s = (ch & 15)*0.0625f + (0.5f / tw);
1660 t = (ch >> 4)*0.0625f + (0.5f / th);
1661 u = 0.0625f * thisw - (1.0f / tw);
1662 v = 0.0625f - (1.0f / th);
1663 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1664 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1665 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1666 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1667 at[ 0] = s ; at[ 1] = t ;
1668 at[ 2] = s+u ; at[ 3] = t ;
1669 at[ 4] = s+u ; at[ 5] = t+v ;
1670 at[ 6] = s ; at[ 7] = t+v ;
1671 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1672 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1673 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1674 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1679 if (batchcount >= QUADELEMENTS_MAXQUADS)
1681 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1687 x += width_of[ch] * dw;
1689 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1691 // new charmap - need to render
1694 // we need a different character map, render what we currently have:
1695 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1702 map = FontMap_FindForChar(fontmap, ch);
1705 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1712 // this shouldn't happen
1717 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1720 mapch = ch - map->start;
1721 thisw = map->glyphs[mapch].advance_x;
1725 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1732 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1733 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1734 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1735 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1736 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1737 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1738 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1739 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1740 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1741 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1742 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1743 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1752 if (batchcount >= QUADELEMENTS_MAXQUADS)
1754 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1767 x -= 1.0/pix_x * r_textshadow.value;
1768 y -= 1.0/pix_y * r_textshadow.value;
1773 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1776 *outcolor = colorindex;
1778 // note: this relies on the proper text (not shadow) being drawn last
1782 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)
1784 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1787 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)
1789 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1792 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1794 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1797 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1799 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1804 // no ^xrgb management
1805 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1807 int color, numchars = 0;
1808 char *outputend2c = output2c + maxoutchars - 2;
1809 if (!outcolor || *outcolor == -1)
1810 color = STRING_COLOR_DEFAULT;
1814 maxreadchars = 1<<30;
1815 textend = text + maxreadchars;
1816 while (text != textend && *text)
1818 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1820 if (text[1] == STRING_COLOR_TAG)
1822 else if (text[1] >= '0' && text[1] <= '9')
1824 color = text[1] - '0';
1829 if (output2c >= outputend2c)
1831 *output2c++ = *text++;
1832 *output2c++ = color;
1835 output2c[0] = output2c[1] = 0;
1842 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)
1846 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1847 if(!r_draw2d.integer && !r_draw2d_force)
1850 R_Mesh_VertexPointer(floats, 0, 0);
1851 R_Mesh_ColorPointer(floats + 20, 0, 0);
1852 R_Mesh_ResetTextureState();
1858 height = pic->height;
1859 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1860 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1861 floats[12] = s1;floats[13] = t1;
1862 floats[14] = s2;floats[15] = t2;
1863 floats[16] = s4;floats[17] = t4;
1864 floats[18] = s3;floats[19] = t3;
1867 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1869 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1870 floats[0] = floats[9] = x;
1871 floats[1] = floats[4] = y;
1872 floats[3] = floats[6] = x + width;
1873 floats[7] = floats[10] = y + height;
1874 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1875 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1876 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1877 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1879 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1882 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1886 if(!r_draw2d.integer && !r_draw2d_force)
1888 DrawQ_ProcessDrawFlag(flags, hasalpha);
1890 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1891 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1892 R_Mesh_ResetTextureState();
1893 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1894 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1896 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1899 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1903 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1904 if(!r_draw2d.integer && !r_draw2d_force)
1909 qglBegin(GL_LINE_LOOP);
1910 for (num = 0;num < mesh->num_vertices;num++)
1912 if (mesh->data_color4f)
1913 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]);
1914 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1920 //[515]: this is old, delete
1921 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1923 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1924 if(!r_draw2d.integer && !r_draw2d_force)
1927 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1930 //qglLineWidth(width);CHECKGLERROR
1932 GL_Color(r,g,b,alpha);
1935 qglVertex2f(x1, y1);
1936 qglVertex2f(x2, y2);
1941 void DrawQ_SetClipArea(float x, float y, float width, float height)
1946 // We have to convert the con coords into real coords
1947 // OGL uses top to bottom
1948 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1949 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1950 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1951 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1952 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1954 GL_ScissorTest(true);
1957 void DrawQ_ResetClipArea(void)
1960 GL_ScissorTest(false);
1963 void DrawQ_Finish(void)
1965 r_refdef.draw2dstage = 0;
1968 void DrawQ_RecalcView(void)
1970 if(r_refdef.draw2dstage)
1971 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
1974 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1975 void R_DrawGamma(void)
1978 switch(vid.renderpath)
1980 case RENDERPATH_GL20:
1981 case RENDERPATH_CGGL:
1982 if (vid_usinghwgamma || v_glslgamma.integer)
1985 case RENDERPATH_GL13:
1986 case RENDERPATH_GL11:
1987 if (vid_usinghwgamma)
1991 // all the blends ignore depth
1992 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1993 R_Mesh_ColorPointer(NULL, 0, 0);
1994 R_Mesh_ResetTextureState();
1995 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1997 GL_DepthRange(0, 1);
1998 GL_PolygonOffset(0, 0);
1999 GL_DepthTest(false);
2000 if (v_color_enable.integer)
2002 c[0] = v_color_white_r.value;
2003 c[1] = v_color_white_g.value;
2004 c[2] = v_color_white_b.value;
2007 c[0] = c[1] = c[2] = v_contrast.value;
2008 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2010 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2011 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
2013 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
2014 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
2015 VectorScale(c, 0.5, c);
2018 if (v_color_enable.integer)
2020 c[0] = v_color_black_r.value;
2021 c[1] = v_color_black_g.value;
2022 c[2] = v_color_black_b.value;
2025 c[0] = c[1] = c[2] = v_brightness.value;
2026 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2028 GL_BlendFunc(GL_ONE, GL_ONE);
2029 GL_Color(c[0], c[1], c[2], 1);
2030 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);