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];
322 texflags = TEXF_ALPHA;
323 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
324 texflags |= TEXF_CLAMP;
325 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
326 texflags |= TEXF_COMPRESS;
328 // check whether the picture has already been cached
329 crc = CRC_Block((unsigned char *)path, strlen(path));
330 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
331 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
332 if (!strcmp (path, pic->name))
333 if(pic->texflags == texflags)
336 if (numcachepics == MAX_CACHED_PICS)
338 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
339 // FIXME: support NULL in callers?
340 return cachepics; // return the first one
342 pic = cachepics + (numcachepics++);
343 strlcpy (pic->name, path, sizeof(pic->name));
345 pic->chain = cachepichash[hashkey];
346 cachepichash[hashkey] = pic;
348 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
349 pic->tex = CL_GetDynTexture( path );
350 // if so, set the width/height, too
352 pic->width = R_TextureWidth(pic->tex);
353 pic->height = R_TextureHeight(pic->tex);
354 // we're done now (early-out)
358 pic->texflags = texflags;
359 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
361 // load a high quality image from disk if possible
362 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer != 0, NULL);
363 if (pixels == NULL && !strncmp(path, "gfx/", 4))
364 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer != 0, NULL);
367 pic->width = image_width;
368 pic->height = image_height;
370 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, -1, NULL);
374 pic->autoload = false;
375 // never compress the fallback images
376 pic->texflags &= ~TEXF_COMPRESS;
379 // now read the low quality version (wad or lmp file), and take the pic
380 // size from that even if we don't upload the texture, this way the pics
381 // show up the right size in the menu even if they were replaced with
382 // higher or lower resolution versions
383 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
384 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
386 if (developer_loading.integer)
387 Con_Printf("loading lump \"%s\"\n", path);
391 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
392 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
393 // if no high quality replacement image was found, upload the original low quality texture
395 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
399 else if ((lmpdata = W_GetLumpName (path + 4)))
401 if (developer_loading.integer)
402 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
404 if (!strcmp(path, "gfx/conchars"))
406 // conchars is a raw image and with color 0 as transparent instead of 255
409 // if no high quality replacement image was found, upload the original low quality texture
411 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
415 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
416 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
417 // if no high quality replacement image was found, upload the original low quality texture
419 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
428 else if (pic->tex == NULL)
430 // if it's not found on disk, generate an image
431 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
432 pic->width = R_TextureWidth(pic->tex);
433 pic->height = R_TextureHeight(pic->tex);
439 cachepic_t *Draw_CachePic (const char *path)
441 return Draw_CachePic_Flags (path, 0);
446 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
448 if (pic->autoload && !pic->tex)
450 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer != 0);
451 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
452 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer != 0);
453 if (pic->tex == NULL)
454 pic->tex = draw_generatepic(pic->name, true);
456 pic->lastusedframe = draw_frame;
460 void Draw_Frame(void)
464 static double nextpurgetime;
465 if (nextpurgetime > realtime)
467 nextpurgetime = realtime + 0.05;
468 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
470 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
472 R_FreeTexture(pic->tex);
479 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
484 crc = CRC_Block((unsigned char *)picname, strlen(picname));
485 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
486 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
487 if (!strcmp (picname, pic->name))
492 if (pic->tex && pic->width == width && pic->height == height)
494 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
502 if (numcachepics == MAX_CACHED_PICS)
504 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
505 // FIXME: support NULL in callers?
506 return cachepics; // return the first one
508 pic = cachepics + (numcachepics++);
509 strlcpy (pic->name, picname, sizeof(pic->name));
511 pic->chain = cachepichash[hashkey];
512 cachepichash[hashkey] = pic;
517 pic->height = height;
519 R_FreeTexture(pic->tex);
520 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, -1, NULL);
524 void Draw_FreePic(const char *picname)
529 // this doesn't really free the pic, but does free it's texture
530 crc = CRC_Block((unsigned char *)picname, strlen(picname));
531 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
532 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
534 if (!strcmp (picname, pic->name) && pic->tex)
536 R_FreeTexture(pic->tex);
545 static float snap_to_pixel_x(float x, float roundUpAt);
546 extern int con_linewidth; // to force rewrapping
547 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
551 char widthfile[MAX_QPATH];
553 fs_offset_t widthbufsize;
555 if(override || !fnt->texpath[0])
557 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
558 // load the cvars when the font is FIRST loader
559 fnt->settings.scale = scale;
560 fnt->settings.voffset = voffset;
561 fnt->settings.antialias = r_font_antialias.integer;
562 fnt->settings.hinting = r_font_hinting.integer;
563 fnt->settings.outline = r_font_postprocess_outline.value;
564 fnt->settings.blur = r_font_postprocess_blur.value;
565 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
566 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
567 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
570 if (fnt->settings.scale <= 0)
571 fnt->settings.scale = 1;
573 if(drawtexturepool == NULL)
574 return; // before gl_draw_start, so will be loaded later
578 // clear freetype font
579 Font_UnloadFont(fnt->ft2);
584 if(fnt->req_face != -1)
586 if(!Font_LoadFont(fnt->texpath, fnt))
587 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
590 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
591 if(fnt->tex == r_texture_notexture)
593 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
595 if (!fnt->fallbacks[i][0])
597 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
598 if(fnt->tex != r_texture_notexture)
601 if(fnt->tex == r_texture_notexture)
603 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
604 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
607 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
610 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
612 // unspecified width == 1 (base width)
613 for(i = 1; i < 256; ++i)
614 fnt->width_of[i] = 1;
616 // FIXME load "name.width", if it fails, fill all with 1
617 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
619 float extraspacing = 0;
620 const char *p = widthbuf;
625 if(!COM_ParseToken_Simple(&p, false, false))
643 fnt->width_of[ch] = atof(com_token) + extraspacing;
646 for (i = 0; i < MAX_FONT_SIZES; ++i)
648 //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
649 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
652 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
658 if(!strcmp(com_token, "extraspacing"))
660 if(!COM_ParseToken_Simple(&p, false, false))
662 extraspacing = atof(com_token);
664 else if(!strcmp(com_token, "scale"))
666 if(!COM_ParseToken_Simple(&p, false, false))
668 fnt->settings.scale = atof(com_token);
672 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
673 if(!COM_ParseToken_Simple(&p, false, false))
683 maxwidth = fnt->width_of[1];
684 for(i = 2; i < 256; ++i)
685 maxwidth = max(maxwidth, fnt->width_of[i]);
686 fnt->maxwidth = maxwidth;
688 // fix up maxwidth for overlap
689 fnt->maxwidth *= fnt->settings.scale;
691 if(fnt == FONT_CONSOLE)
692 con_linewidth = -1; // rewrap console in next frame
695 extern cvar_t developer_font;
696 dp_font_t *FindFont(const char *title, qboolean allocate_new)
701 for(i = 0; i < dp_fonts.maxsize; ++i)
702 if(!strcmp(dp_fonts.f[i].title, title))
703 return &dp_fonts.f[i];
704 // if not found - try allocate
707 // find any font with empty title
708 for(i = 0; i < dp_fonts.maxsize; ++i)
710 if(!strcmp(dp_fonts.f[i].title, ""))
712 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
713 return &dp_fonts.f[i];
716 // if no any 'free' fonts - expand buffer
717 i = dp_fonts.maxsize;
718 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
719 if (developer_font.integer)
720 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
721 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
722 // register a font in first expanded slot
723 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
724 return &dp_fonts.f[i];
729 static float snap_to_pixel_x(float x, float roundUpAt)
731 float pixelpos = x * vid.width / vid_conwidth.value;
732 int snap = (int) pixelpos;
733 if (pixelpos - snap >= roundUpAt) ++snap;
734 return ((float)snap * vid_conwidth.value / vid.width);
736 x = (int)(x * vid.width / vid_conwidth.value);
737 x = (x * vid_conwidth.value / vid.width);
742 static float snap_to_pixel_y(float y, float roundUpAt)
744 float pixelpos = y * vid.height / vid_conheight.value;
745 int snap = (int) pixelpos;
746 if (pixelpos - snap > roundUpAt) ++snap;
747 return ((float)snap * vid_conheight.value / vid.height);
749 y = (int)(y * vid.height / vid_conheight.value);
750 y = (y * vid_conheight.value / vid.height);
755 static void LoadFont_f(void)
759 const char *filelist, *c, *cm;
760 float sz, scale, voffset;
761 char mainfont[MAX_QPATH];
765 Con_Printf("Available font commands:\n");
766 for(i = 0; i < dp_fonts.maxsize; ++i)
767 if (dp_fonts.f[i].title[0])
768 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
769 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
770 "can specify multiple fonts and faces\n"
771 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
772 "to load face 2 of the font gfx/vera-sans and use face 1\n"
773 "of gfx/fallback as fallback font.\n"
774 "You can also specify a list of font sizes to load, like this:\n"
775 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
776 "In many cases, 8 12 16 24 32 should be a good choice.\n"
778 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
779 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
783 f = FindFont(Cmd_Argv(1), true);
786 Con_Printf("font function not found\n");
791 filelist = "gfx/conchars";
793 filelist = Cmd_Argv(2);
795 memset(f->fallbacks, 0, sizeof(f->fallbacks));
796 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
798 // first font is handled "normally"
799 c = strchr(filelist, ':');
800 cm = strchr(filelist, ',');
801 if(c && (!cm || c < cm))
802 f->req_face = atoi(c+1);
809 if(!c || (c - filelist) > MAX_QPATH)
810 strlcpy(mainfont, filelist, sizeof(mainfont));
813 memcpy(mainfont, filelist, c - filelist);
814 mainfont[c - filelist] = 0;
817 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
819 c = strchr(filelist, ',');
825 c = strchr(filelist, ':');
826 cm = strchr(filelist, ',');
827 if(c && (!cm || c < cm))
828 f->fallback_faces[i] = atoi(c+1);
831 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
834 if(!c || (c-filelist) > MAX_QPATH)
836 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
840 memcpy(f->fallbacks[i], filelist, c - filelist);
841 f->fallbacks[i][c - filelist] = 0;
845 // for now: by default load only one size: the default size
847 for(i = 1; i < MAX_FONT_SIZES; ++i)
848 f->req_sizes[i] = -1;
854 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
857 if (!strcmp(Cmd_Argv(i), "scale"))
861 scale = atof(Cmd_Argv(i));
864 if (!strcmp(Cmd_Argv(i), "voffset"))
868 voffset = atof(Cmd_Argv(i));
871 // parse one of sizes
872 sz = atof(Cmd_Argv(i));
873 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
875 f->req_sizes[sizes] = sz;
881 LoadFont(true, mainfont, f, scale, voffset);
889 static void gl_draw_start(void)
892 drawtexturepool = R_AllocTexturePool();
895 memset(cachepichash, 0, sizeof(cachepichash));
899 // load default font textures
900 for(i = 0; i < dp_fonts.maxsize; ++i)
901 if (dp_fonts.f[i].title[0])
902 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
904 // draw the loading screen so people have something to see in the newly opened window
905 SCR_UpdateLoadingScreen(true);
908 static void gl_draw_shutdown(void)
912 R_FreeTexturePool(&drawtexturepool);
915 memset(cachepichash, 0, sizeof(cachepichash));
918 static void gl_draw_newmap(void)
923 void GL_Draw_Init (void)
927 Cvar_RegisterVariable(&r_font_postprocess_blur);
928 Cvar_RegisterVariable(&r_font_postprocess_outline);
929 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
930 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
931 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
932 Cvar_RegisterVariable(&r_font_hinting);
933 Cvar_RegisterVariable(&r_font_antialias);
934 Cvar_RegisterVariable(&r_textshadow);
935 Cvar_RegisterVariable(&r_textbrightness);
936 Cvar_RegisterVariable(&r_textcontrast);
938 // allocate fonts storage
939 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
940 dp_fonts.maxsize = MAX_FONTS;
941 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
942 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
944 // assign starting font names
945 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
946 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
947 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
948 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
949 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
950 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
951 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
952 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
953 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
954 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
955 if(!FONT_USER(i)->title[0])
956 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
958 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
959 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
962 void _DrawQ_Setup(void)
964 r_viewport_t viewport;
965 if (r_refdef.draw2dstage)
967 r_refdef.draw2dstage = true;
969 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);
970 R_SetViewport(&viewport);
971 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
972 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
973 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
974 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
975 R_EntityMatrix(&identitymatrix);
979 GL_PolygonOffset(0, 0);
983 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
986 qboolean r_draw2d_force = false;
987 static void _DrawQ_ProcessDrawFlag(int flags)
991 if(!r_draw2d.integer && !r_draw2d_force)
993 if(flags == DRAWFLAG_ADDITIVE)
994 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
995 else if(flags == DRAWFLAG_MODULATE)
996 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
997 else if(flags == DRAWFLAG_2XMODULATE)
998 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
999 else if(flags == DRAWFLAG_SCREEN)
1000 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
1002 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1005 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1009 _DrawQ_ProcessDrawFlag(flags);
1010 if(!r_draw2d.integer && !r_draw2d_force)
1013 R_Mesh_ResetTextureState();
1014 floats[12] = 0.0f;floats[13] = 0.0f;
1015 floats[14] = 1.0f;floats[15] = 0.0f;
1016 floats[16] = 1.0f;floats[17] = 1.0f;
1017 floats[18] = 0.0f;floats[19] = 1.0f;
1018 floats[20] = floats[24] = floats[28] = floats[32] = red;
1019 floats[21] = floats[25] = floats[29] = floats[33] = green;
1020 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1021 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1027 height = pic->height;
1028 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1031 // AK07: lets be texel correct on the corners
1033 float horz_offset = 0.5f / pic->width;
1034 float vert_offset = 0.5f / pic->height;
1036 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1037 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1038 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1039 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1044 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1046 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1047 floats[0] = floats[9] = x;
1048 floats[1] = floats[4] = y;
1049 floats[3] = floats[6] = x + width;
1050 floats[7] = floats[10] = y + height;
1052 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1053 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1056 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)
1059 float af = DEG2RAD(-angle); // forward
1060 float ar = DEG2RAD(-angle + 90); // right
1061 float sinaf = sin(af);
1062 float cosaf = cos(af);
1063 float sinar = sin(ar);
1064 float cosar = cos(ar);
1066 _DrawQ_ProcessDrawFlag(flags);
1067 if(!r_draw2d.integer && !r_draw2d_force)
1070 R_Mesh_ResetTextureState();
1076 height = pic->height;
1077 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1080 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1082 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1085 floats[0] = x - cosaf*org_x - cosar*org_y;
1086 floats[1] = y - sinaf*org_x - sinar*org_y;
1089 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1090 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1093 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1094 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1097 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1098 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1100 floats[12] = 0.0f;floats[13] = 0.0f;
1101 floats[14] = 1.0f;floats[15] = 0.0f;
1102 floats[16] = 1.0f;floats[17] = 1.0f;
1103 floats[18] = 0.0f;floats[19] = 1.0f;
1104 floats[20] = floats[24] = floats[28] = floats[32] = red;
1105 floats[21] = floats[25] = floats[29] = floats[33] = green;
1106 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1107 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1109 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1110 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1113 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1117 _DrawQ_ProcessDrawFlag(flags);
1118 if(!r_draw2d.integer && !r_draw2d_force)
1121 R_Mesh_ResetTextureState();
1122 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1124 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1125 floats[0] = floats[9] = x;
1126 floats[1] = floats[4] = y;
1127 floats[3] = floats[6] = x + width;
1128 floats[7] = floats[10] = y + height;
1129 floats[12] = 0.0f;floats[13] = 0.0f;
1130 floats[14] = 1.0f;floats[15] = 0.0f;
1131 floats[16] = 1.0f;floats[17] = 1.0f;
1132 floats[18] = 0.0f;floats[19] = 1.0f;
1133 floats[20] = floats[24] = floats[28] = floats[32] = red;
1134 floats[21] = floats[25] = floats[29] = floats[33] = green;
1135 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1136 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1138 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1139 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1142 /// color tag printing
1143 static const vec4_t string_colors[] =
1146 // LordHavoc: why on earth is cyan before magenta in Quake3?
1147 // LordHavoc: note: Doom3 uses white for [0] and [7]
1148 {0.0, 0.0, 0.0, 1.0}, // black
1149 {1.0, 0.0, 0.0, 1.0}, // red
1150 {0.0, 1.0, 0.0, 1.0}, // green
1151 {1.0, 1.0, 0.0, 1.0}, // yellow
1152 {0.0, 0.0, 1.0, 1.0}, // blue
1153 {0.0, 1.0, 1.0, 1.0}, // cyan
1154 {1.0, 0.0, 1.0, 1.0}, // magenta
1155 {1.0, 1.0, 1.0, 1.0}, // white
1156 // [515]'s BX_COLOREDTEXT extension
1157 {1.0, 1.0, 1.0, 0.5}, // half transparent
1158 {0.5, 0.5, 0.5, 1.0} // half brightness
1159 // Black's color table
1160 //{1.0, 1.0, 1.0, 1.0},
1161 //{1.0, 0.0, 0.0, 1.0},
1162 //{0.0, 1.0, 0.0, 1.0},
1163 //{0.0, 0.0, 1.0, 1.0},
1164 //{1.0, 1.0, 0.0, 1.0},
1165 //{0.0, 1.0, 1.0, 1.0},
1166 //{1.0, 0.0, 1.0, 1.0},
1167 //{0.1, 0.1, 0.1, 1.0}
1170 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1172 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1174 float C = r_textcontrast.value;
1175 float B = r_textbrightness.value;
1176 if (colorindex & 0x10000) // that bit means RGB color
1178 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1179 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1180 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1181 color[3] = (colorindex & 0xf) / 15.0;
1184 Vector4Copy(string_colors[colorindex], color);
1185 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1188 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1189 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1193 // NOTE: this function always draws exactly one character if maxwidth <= 0
1194 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)
1196 const char *text_start = text;
1197 int colorindex = STRING_COLOR_DEFAULT;
1200 Uchar ch, mapch, nextch;
1201 Uchar prevch = 0; // used for kerning
1206 ft2_font_map_t *fontmap = NULL;
1207 ft2_font_map_t *map = NULL;
1208 //ft2_font_map_t *prevmap = NULL;
1209 ft2_font_t *ft2 = fnt->ft2;
1211 qboolean snap = true;
1212 qboolean least_one = false;
1213 float dw; // display w
1214 //float dh; // display h
1215 const float *width_of;
1222 // do this in the end
1223 w *= fnt->settings.scale;
1224 h *= fnt->settings.scale;
1226 // find the most fitting size:
1230 map_index = Font_IndexForSize(ft2, h, &w, &h);
1232 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1233 fontmap = Font_MapForIndex(ft2, map_index);
1242 if (!outcolor || *outcolor == -1)
1243 colorindex = STRING_COLOR_DEFAULT;
1245 colorindex = *outcolor;
1247 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1248 // ftbase_x = snap_to_pixel_x(0);
1253 maxwidth = -maxwidth;
1257 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1260 width_of = fontmap->width_of;
1262 width_of = fnt->width_of;
1264 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1267 nextch = ch = u8_getnchar(text, &text, bytes_left);
1268 i = text - text_start;
1271 if (ch == ' ' && !fontmap)
1273 if(!least_one || i0) // never skip the first character
1274 if(x + width_of[(int) ' '] * dw > maxwidth)
1277 break; // oops, can't draw this
1279 x += width_of[(int) ' '] * dw;
1282 // i points to the char after ^
1283 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1285 ch = *text; // colors are ascii, so no u8_ needed
1286 if (ch <= '9' && ch >= '0') // ^[0-9] found
1288 colorindex = ch - '0';
1293 // i points to the char after ^...
1294 // i+3 points to 3 in ^x123
1295 // i+3 == *maxlen would mean that char is missing
1296 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1298 // building colorindex...
1299 ch = tolower(text[1]);
1300 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1301 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1302 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1303 else tempcolorindex = 0;
1306 ch = tolower(text[2]);
1307 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1308 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1309 else tempcolorindex = 0;
1312 ch = tolower(text[3]);
1313 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1314 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1315 else tempcolorindex = 0;
1318 colorindex = tempcolorindex | 0xf;
1319 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1327 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1336 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1343 map = ft2_oldstyle_map;
1345 if(!least_one || i0) // never skip the first character
1346 if(x + width_of[ch] * dw > maxwidth)
1349 break; // oops, can't draw this
1351 x += width_of[ch] * dw;
1353 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1355 map = FontMap_FindForChar(fontmap, ch);
1358 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1364 mapch = ch - map->start;
1365 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1367 x += map->glyphs[mapch].advance_x * dw;
1376 *outcolor = colorindex;
1381 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)
1383 int shadow, colorindex = STRING_COLOR_DEFAULT;
1385 float x = startx, y, s, t, u, v, thisw;
1386 float *av, *at, *ac;
1389 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1390 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1391 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1392 Uchar ch, mapch, nextch;
1393 Uchar prevch = 0; // used for kerning
1396 //ft2_font_map_t *prevmap = NULL; // the previous map
1397 ft2_font_map_t *map = NULL; // the currently used map
1398 ft2_font_map_t *fontmap = NULL; // the font map for the size
1400 const char *text_start = text;
1402 ft2_font_t *ft2 = fnt->ft2;
1403 qboolean snap = true;
1407 const float *width_of;
1410 tw = R_TextureWidth(fnt->tex);
1411 th = R_TextureHeight(fnt->tex);
1419 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1420 w *= fnt->settings.scale;
1421 h *= fnt->settings.scale;
1426 map_index = Font_IndexForSize(ft2, h, &w, &h);
1428 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1429 fontmap = Font_MapForIndex(ft2, map_index);
1435 // draw the font at its baseline when using freetype
1437 ftbase_y = dh * (4.5/6.0);
1442 _DrawQ_ProcessDrawFlag(flags);
1443 if(!r_draw2d.integer && !r_draw2d_force)
1444 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1446 R_Mesh_ResetTextureState();
1448 R_Mesh_TexBind(0, fnt->tex);
1449 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1456 //ftbase_x = snap_to_pixel_x(ftbase_x);
1459 startx = snap_to_pixel_x(startx, 0.4);
1460 starty = snap_to_pixel_y(starty, 0.4);
1461 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1464 pix_x = vid.width / vid_conwidth.value;
1465 pix_y = vid.height / vid_conheight.value;
1468 width_of = fontmap->width_of;
1470 width_of = fnt->width_of;
1472 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1477 if (!outcolor || *outcolor == -1)
1478 colorindex = STRING_COLOR_DEFAULT;
1480 colorindex = *outcolor;
1482 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1489 x += r_textshadow.value * vid.width / vid_conwidth.value;
1490 y += r_textshadow.value * vid.height / vid_conheight.value;
1493 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1495 nextch = ch = u8_getnchar(text, &text, bytes_left);
1496 i = text - text_start;
1499 if (ch == ' ' && !fontmap)
1501 x += width_of[(int) ' '] * dw;
1504 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1506 ch = *text; // colors are ascii, so no u8_ needed
1507 if (ch <= '9' && ch >= '0') // ^[0-9] found
1509 colorindex = ch - '0';
1510 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1515 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1517 // building colorindex...
1518 ch = tolower(text[1]);
1519 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1520 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1521 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1522 else tempcolorindex = 0;
1525 ch = tolower(text[2]);
1526 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1527 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1528 else tempcolorindex = 0;
1531 ch = tolower(text[3]);
1532 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1533 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1534 else tempcolorindex = 0;
1537 colorindex = tempcolorindex | 0xf;
1538 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1539 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1540 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1548 else if (ch == STRING_COLOR_TAG)
1557 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1558 // this way we don't need to rebind fnt->tex for every old-style character
1559 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1562 x += 1.0/pix_x * r_textshadow.value;
1563 y += 1.0/pix_y * r_textshadow.value;
1565 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1573 if (map != ft2_oldstyle_map)
1577 // switching from freetype to non-freetype rendering
1578 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1579 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1585 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1586 map = ft2_oldstyle_map;
1590 //num = (unsigned char) text[i];
1591 //thisw = fnt->width_of[num];
1592 thisw = fnt->width_of[ch];
1593 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1594 s = (ch & 15)*0.0625f + (0.5f / tw);
1595 t = (ch >> 4)*0.0625f + (0.5f / th);
1596 u = 0.0625f * thisw - (1.0f / tw);
1597 v = 0.0625f - (1.0f / th);
1598 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1599 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1600 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1601 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1602 at[ 0] = s ; at[ 1] = t ;
1603 at[ 2] = s+u ; at[ 3] = t ;
1604 at[ 4] = s+u ; at[ 5] = t+v ;
1605 at[ 6] = s ; at[ 7] = t+v ;
1606 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1607 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1608 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1609 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1614 if (batchcount >= QUADELEMENTS_MAXQUADS)
1616 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1617 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1623 x += width_of[ch] * dw;
1625 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1627 // new charmap - need to render
1630 // we need a different character map, render what we currently have:
1631 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1632 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1639 map = FontMap_FindForChar(fontmap, ch);
1642 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1649 // this shouldn't happen
1654 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1657 mapch = ch - map->start;
1658 thisw = map->glyphs[mapch].advance_x;
1662 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1669 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1670 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1671 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1672 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1673 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1674 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1675 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1676 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1677 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1678 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1679 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1680 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1689 if (batchcount >= QUADELEMENTS_MAXQUADS)
1691 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1692 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1704 x -= 1.0/pix_x * r_textshadow.value;
1705 y -= 1.0/pix_y * r_textshadow.value;
1711 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1712 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1716 *outcolor = colorindex;
1718 // note: this relies on the proper text (not shadow) being drawn last
1722 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)
1724 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1727 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)
1729 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1732 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1734 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1737 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1739 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1744 // no ^xrgb management
1745 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1747 int color, numchars = 0;
1748 char *outputend2c = output2c + maxoutchars - 2;
1749 if (!outcolor || *outcolor == -1)
1750 color = STRING_COLOR_DEFAULT;
1754 maxreadchars = 1<<30;
1755 textend = text + maxreadchars;
1756 while (text != textend && *text)
1758 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1760 if (text[1] == STRING_COLOR_TAG)
1762 else if (text[1] >= '0' && text[1] <= '9')
1764 color = text[1] - '0';
1769 if (output2c >= outputend2c)
1771 *output2c++ = *text++;
1772 *output2c++ = color;
1775 output2c[0] = output2c[1] = 0;
1782 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)
1786 _DrawQ_ProcessDrawFlag(flags);
1787 if(!r_draw2d.integer && !r_draw2d_force)
1790 R_Mesh_ResetTextureState();
1796 height = pic->height;
1797 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1800 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1802 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1803 floats[0] = floats[9] = x;
1804 floats[1] = floats[4] = y;
1805 floats[3] = floats[6] = x + width;
1806 floats[7] = floats[10] = y + height;
1807 floats[12] = s1;floats[13] = t1;
1808 floats[14] = s2;floats[15] = t2;
1809 floats[16] = s4;floats[17] = t4;
1810 floats[18] = s3;floats[19] = t3;
1811 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1812 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1813 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1814 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1816 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1817 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1820 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1822 _DrawQ_ProcessDrawFlag(flags);
1823 if(!r_draw2d.integer && !r_draw2d_force)
1826 R_Mesh_ResetTextureState();
1827 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1829 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1830 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1833 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1837 _DrawQ_ProcessDrawFlag(flags);
1838 if(!r_draw2d.integer && !r_draw2d_force)
1843 qglBegin(GL_LINE_LOOP);
1844 for (num = 0;num < mesh->num_vertices;num++)
1846 if (mesh->data_color4f)
1847 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]);
1848 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1854 //[515]: this is old, delete
1855 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1857 _DrawQ_ProcessDrawFlag(flags);
1858 if(!r_draw2d.integer && !r_draw2d_force)
1861 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1864 //qglLineWidth(width);CHECKGLERROR
1866 GL_Color(r,g,b,alpha);
1869 qglVertex2f(x1, y1);
1870 qglVertex2f(x2, y2);
1875 void DrawQ_SetClipArea(float x, float y, float width, float height)
1880 // We have to convert the con coords into real coords
1881 // OGL uses top to bottom
1882 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1883 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1884 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1885 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1886 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1888 GL_ScissorTest(true);
1891 void DrawQ_ResetClipArea(void)
1894 GL_ScissorTest(false);
1897 void DrawQ_Finish(void)
1899 r_refdef.draw2dstage = false;
1902 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1903 void R_DrawGamma(void)
1906 switch(vid.renderpath)
1908 case RENDERPATH_GL20:
1909 case RENDERPATH_CGGL:
1910 if (vid_usinghwgamma || v_glslgamma.integer)
1913 case RENDERPATH_GL13:
1914 case RENDERPATH_GL11:
1915 if (vid_usinghwgamma)
1919 // all the blends ignore depth
1920 R_Mesh_ResetTextureState();
1921 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1923 GL_DepthRange(0, 1);
1924 GL_PolygonOffset(0, 0);
1925 GL_DepthTest(false);
1926 if (v_color_enable.integer)
1928 c[0] = v_color_white_r.value;
1929 c[1] = v_color_white_g.value;
1930 c[2] = v_color_white_b.value;
1933 c[0] = c[1] = c[2] = v_contrast.value;
1934 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1936 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1937 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1939 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1940 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1941 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1942 VectorScale(c, 0.5, c);
1945 if (v_color_enable.integer)
1947 c[0] = v_color_black_r.value;
1948 c[1] = v_color_black_g.value;
1949 c[2] = v_color_black_b.value;
1952 c[0] = c[1] = c[2] = v_brightness.value;
1953 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1955 GL_BlendFunc(GL_ONE, GL_ONE);
1956 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1957 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1958 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);