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)
335 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
336 pic->autoload = false; // persist it
340 if (numcachepics == MAX_CACHED_PICS)
342 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
343 // FIXME: support NULL in callers?
344 return cachepics; // return the first one
346 pic = cachepics + (numcachepics++);
347 strlcpy (pic->name, path, sizeof(pic->name));
349 pic->chain = cachepichash[hashkey];
350 cachepichash[hashkey] = pic;
352 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
353 pic->tex = CL_GetDynTexture( path );
354 // if so, set the width/height, too
356 pic->width = R_TextureWidth(pic->tex);
357 pic->height = R_TextureHeight(pic->tex);
358 // we're done now (early-out)
362 pic->texflags = texflags;
363 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
365 // load a high quality image from disk if possible
366 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer != 0, NULL);
367 if (pixels == NULL && !strncmp(path, "gfx/", 4))
368 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer != 0, NULL);
371 pic->width = image_width;
372 pic->height = image_height;
374 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, -1, NULL);
378 pic->autoload = false;
379 // never compress the fallback images
380 pic->texflags &= ~TEXF_COMPRESS;
383 // now read the low quality version (wad or lmp file), and take the pic
384 // size from that even if we don't upload the texture, this way the pics
385 // show up the right size in the menu even if they were replaced with
386 // higher or lower resolution versions
387 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
388 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
390 if (developer_loading.integer)
391 Con_Printf("loading lump \"%s\"\n", path);
395 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
396 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
397 // if no high quality replacement image was found, upload the original low quality texture
399 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
403 else if ((lmpdata = W_GetLumpName (path + 4)))
405 if (developer_loading.integer)
406 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
408 if (!strcmp(path, "gfx/conchars"))
410 // conchars is a raw image and with color 0 as transparent instead of 255
413 // if no high quality replacement image was found, upload the original low quality texture
415 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
419 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
420 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
421 // if no high quality replacement image was found, upload the original low quality texture
423 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
432 else if (pic->tex == NULL)
434 // if it's not found on disk, generate an image
435 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
436 pic->width = R_TextureWidth(pic->tex);
437 pic->height = R_TextureHeight(pic->tex);
443 cachepic_t *Draw_CachePic (const char *path)
445 return Draw_CachePic_Flags (path, 0); // default to persistent!
450 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
452 if (pic->autoload && !pic->tex)
454 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer != 0);
455 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
456 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer != 0);
457 if (pic->tex == NULL)
458 pic->tex = draw_generatepic(pic->name, true);
460 pic->lastusedframe = draw_frame;
464 void Draw_Frame(void)
468 static double nextpurgetime;
469 if (nextpurgetime > realtime)
471 nextpurgetime = realtime + 0.05;
472 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
474 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
476 R_FreeTexture(pic->tex);
483 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
488 crc = CRC_Block((unsigned char *)picname, strlen(picname));
489 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
490 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
491 if (!strcmp (picname, pic->name))
496 if (pic->tex && pic->width == width && pic->height == height)
498 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
506 if (numcachepics == MAX_CACHED_PICS)
508 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
509 // FIXME: support NULL in callers?
510 return cachepics; // return the first one
512 pic = cachepics + (numcachepics++);
513 strlcpy (pic->name, picname, sizeof(pic->name));
515 pic->chain = cachepichash[hashkey];
516 cachepichash[hashkey] = pic;
521 pic->height = height;
523 R_FreeTexture(pic->tex);
524 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, -1, NULL);
528 void Draw_FreePic(const char *picname)
533 // this doesn't really free the pic, but does free it's texture
534 crc = CRC_Block((unsigned char *)picname, strlen(picname));
535 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
536 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
538 if (!strcmp (picname, pic->name) && pic->tex)
540 R_FreeTexture(pic->tex);
549 static float snap_to_pixel_x(float x, float roundUpAt);
550 extern int con_linewidth; // to force rewrapping
551 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
555 char widthfile[MAX_QPATH];
557 fs_offset_t widthbufsize;
559 if(override || !fnt->texpath[0])
561 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
562 // load the cvars when the font is FIRST loader
563 fnt->settings.scale = scale;
564 fnt->settings.voffset = voffset;
565 fnt->settings.antialias = r_font_antialias.integer;
566 fnt->settings.hinting = r_font_hinting.integer;
567 fnt->settings.outline = r_font_postprocess_outline.value;
568 fnt->settings.blur = r_font_postprocess_blur.value;
569 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
570 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
571 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
574 if (fnt->settings.scale <= 0)
575 fnt->settings.scale = 1;
577 if(drawtexturepool == NULL)
578 return; // before gl_draw_start, so will be loaded later
582 // clear freetype font
583 Font_UnloadFont(fnt->ft2);
588 if(fnt->req_face != -1)
590 if(!Font_LoadFont(fnt->texpath, fnt))
591 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
594 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
595 if(fnt->tex == r_texture_notexture)
597 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
599 if (!fnt->fallbacks[i][0])
601 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
602 if(fnt->tex != r_texture_notexture)
605 if(fnt->tex == r_texture_notexture)
607 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
608 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
611 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
614 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
616 // unspecified width == 1 (base width)
617 for(ch = 0; ch < 256; ++ch)
618 fnt->width_of[ch] = 1;
620 // FIXME load "name.width", if it fails, fill all with 1
621 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
623 float extraspacing = 0;
624 const char *p = widthbuf;
629 if(!COM_ParseToken_Simple(&p, false, false))
647 fnt->width_of[ch] = atof(com_token) + extraspacing;
651 if(!strcmp(com_token, "extraspacing"))
653 if(!COM_ParseToken_Simple(&p, false, false))
655 extraspacing = atof(com_token);
657 else if(!strcmp(com_token, "scale"))
659 if(!COM_ParseToken_Simple(&p, false, false))
661 fnt->settings.scale = atof(com_token);
665 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
666 if(!COM_ParseToken_Simple(&p, false, false))
678 for (i = 0; i < MAX_FONT_SIZES; ++i)
680 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
683 for(ch = 0; ch < 256; ++ch)
684 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
688 maxwidth = fnt->width_of[0];
689 for(i = 1; i < 256; ++i)
690 maxwidth = max(maxwidth, fnt->width_of[i]);
691 fnt->maxwidth = maxwidth;
693 // fix up maxwidth for overlap
694 fnt->maxwidth *= fnt->settings.scale;
696 if(fnt == FONT_CONSOLE)
697 con_linewidth = -1; // rewrap console in next frame
700 extern cvar_t developer_font;
701 dp_font_t *FindFont(const char *title, qboolean allocate_new)
706 for(i = 0; i < dp_fonts.maxsize; ++i)
707 if(!strcmp(dp_fonts.f[i].title, title))
708 return &dp_fonts.f[i];
709 // if not found - try allocate
712 // find any font with empty title
713 for(i = 0; i < dp_fonts.maxsize; ++i)
715 if(!strcmp(dp_fonts.f[i].title, ""))
717 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
718 return &dp_fonts.f[i];
721 // if no any 'free' fonts - expand buffer
722 i = dp_fonts.maxsize;
723 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
724 if (developer_font.integer)
725 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", i, dp_fonts.maxsize);
726 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
727 // register a font in first expanded slot
728 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
729 return &dp_fonts.f[i];
734 static float snap_to_pixel_x(float x, float roundUpAt)
736 float pixelpos = x * vid.width / vid_conwidth.value;
737 int snap = (int) pixelpos;
738 if (pixelpos - snap >= roundUpAt) ++snap;
739 return ((float)snap * vid_conwidth.value / vid.width);
741 x = (int)(x * vid.width / vid_conwidth.value);
742 x = (x * vid_conwidth.value / vid.width);
747 static float snap_to_pixel_y(float y, float roundUpAt)
749 float pixelpos = y * vid.height / vid_conheight.value;
750 int snap = (int) pixelpos;
751 if (pixelpos - snap > roundUpAt) ++snap;
752 return ((float)snap * vid_conheight.value / vid.height);
754 y = (int)(y * vid.height / vid_conheight.value);
755 y = (y * vid_conheight.value / vid.height);
760 static void LoadFont_f(void)
764 const char *filelist, *c, *cm;
765 float sz, scale, voffset;
766 char mainfont[MAX_QPATH];
770 Con_Printf("Available font commands:\n");
771 for(i = 0; i < dp_fonts.maxsize; ++i)
772 if (dp_fonts.f[i].title[0])
773 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
774 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
775 "can specify multiple fonts and faces\n"
776 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
777 "to load face 2 of the font gfx/vera-sans and use face 1\n"
778 "of gfx/fallback as fallback font.\n"
779 "You can also specify a list of font sizes to load, like this:\n"
780 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
781 "In many cases, 8 12 16 24 32 should be a good choice.\n"
783 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
784 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
788 f = FindFont(Cmd_Argv(1), true);
791 Con_Printf("font function not found\n");
796 filelist = "gfx/conchars";
798 filelist = Cmd_Argv(2);
800 memset(f->fallbacks, 0, sizeof(f->fallbacks));
801 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
803 // first font is handled "normally"
804 c = strchr(filelist, ':');
805 cm = strchr(filelist, ',');
806 if(c && (!cm || c < cm))
807 f->req_face = atoi(c+1);
814 if(!c || (c - filelist) > MAX_QPATH)
815 strlcpy(mainfont, filelist, sizeof(mainfont));
818 memcpy(mainfont, filelist, c - filelist);
819 mainfont[c - filelist] = 0;
822 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
824 c = strchr(filelist, ',');
830 c = strchr(filelist, ':');
831 cm = strchr(filelist, ',');
832 if(c && (!cm || c < cm))
833 f->fallback_faces[i] = atoi(c+1);
836 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
839 if(!c || (c-filelist) > MAX_QPATH)
841 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
845 memcpy(f->fallbacks[i], filelist, c - filelist);
846 f->fallbacks[i][c - filelist] = 0;
850 // for now: by default load only one size: the default size
852 for(i = 1; i < MAX_FONT_SIZES; ++i)
853 f->req_sizes[i] = -1;
859 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
862 if (!strcmp(Cmd_Argv(i), "scale"))
866 scale = atof(Cmd_Argv(i));
869 if (!strcmp(Cmd_Argv(i), "voffset"))
873 voffset = atof(Cmd_Argv(i));
876 // parse one of sizes
877 sz = atof(Cmd_Argv(i));
878 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
880 f->req_sizes[sizes] = sz;
886 LoadFont(true, mainfont, f, scale, voffset);
894 static void gl_draw_start(void)
897 drawtexturepool = R_AllocTexturePool();
900 memset(cachepichash, 0, sizeof(cachepichash));
904 // load default font textures
905 for(i = 0; i < dp_fonts.maxsize; ++i)
906 if (dp_fonts.f[i].title[0])
907 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
909 // draw the loading screen so people have something to see in the newly opened window
910 SCR_UpdateLoadingScreen(true);
913 static void gl_draw_shutdown(void)
917 R_FreeTexturePool(&drawtexturepool);
920 memset(cachepichash, 0, sizeof(cachepichash));
923 static void gl_draw_newmap(void)
928 void GL_Draw_Init (void)
932 Cvar_RegisterVariable(&r_font_postprocess_blur);
933 Cvar_RegisterVariable(&r_font_postprocess_outline);
934 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
935 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
936 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
937 Cvar_RegisterVariable(&r_font_hinting);
938 Cvar_RegisterVariable(&r_font_antialias);
939 Cvar_RegisterVariable(&r_textshadow);
940 Cvar_RegisterVariable(&r_textbrightness);
941 Cvar_RegisterVariable(&r_textcontrast);
943 // allocate fonts storage
944 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
945 dp_fonts.maxsize = MAX_FONTS;
946 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
947 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
949 // assign starting font names
950 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
951 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
952 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
953 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
954 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
955 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
956 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
957 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
958 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
959 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
960 if(!FONT_USER(i)->title[0])
961 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
963 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
964 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
967 void _DrawQ_Setup(void)
969 r_viewport_t viewport;
970 if (r_refdef.draw2dstage)
972 r_refdef.draw2dstage = true;
974 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);
975 R_SetViewport(&viewport);
976 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
977 GL_DepthFunc(GL_LEQUAL);
978 GL_PolygonOffset(0,0);
979 GL_CullFace(GL_NONE);
980 R_EntityMatrix(&identitymatrix);
984 GL_PolygonOffset(0, 0);
988 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
991 qboolean r_draw2d_force = false;
992 static void _DrawQ_ProcessDrawFlag(int flags)
996 if(!r_draw2d.integer && !r_draw2d_force)
998 if(flags == DRAWFLAG_ADDITIVE)
999 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1000 else if(flags == DRAWFLAG_MODULATE)
1001 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1002 else if(flags == DRAWFLAG_2XMODULATE)
1003 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
1004 else if(flags == DRAWFLAG_SCREEN)
1005 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
1007 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1010 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1014 _DrawQ_ProcessDrawFlag(flags);
1015 if(!r_draw2d.integer && !r_draw2d_force)
1018 R_Mesh_ResetTextureState();
1019 floats[12] = 0.0f;floats[13] = 0.0f;
1020 floats[14] = 1.0f;floats[15] = 0.0f;
1021 floats[16] = 1.0f;floats[17] = 1.0f;
1022 floats[18] = 0.0f;floats[19] = 1.0f;
1023 floats[20] = floats[24] = floats[28] = floats[32] = red;
1024 floats[21] = floats[25] = floats[29] = floats[33] = green;
1025 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1026 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1032 height = pic->height;
1033 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1036 // AK07: lets be texel correct on the corners
1038 float horz_offset = 0.5f / pic->width;
1039 float vert_offset = 0.5f / pic->height;
1041 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1042 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1043 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1044 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1049 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1051 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1052 floats[0] = floats[9] = x;
1053 floats[1] = floats[4] = y;
1054 floats[3] = floats[6] = x + width;
1055 floats[7] = floats[10] = y + height;
1057 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1058 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1061 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)
1064 float af = DEG2RAD(-angle); // forward
1065 float ar = DEG2RAD(-angle + 90); // right
1066 float sinaf = sin(af);
1067 float cosaf = cos(af);
1068 float sinar = sin(ar);
1069 float cosar = cos(ar);
1071 _DrawQ_ProcessDrawFlag(flags);
1072 if(!r_draw2d.integer && !r_draw2d_force)
1075 R_Mesh_ResetTextureState();
1081 height = pic->height;
1082 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1085 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1087 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1090 floats[0] = x - cosaf*org_x - cosar*org_y;
1091 floats[1] = y - sinaf*org_x - sinar*org_y;
1094 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1095 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1098 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1099 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1102 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1103 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1105 floats[12] = 0.0f;floats[13] = 0.0f;
1106 floats[14] = 1.0f;floats[15] = 0.0f;
1107 floats[16] = 1.0f;floats[17] = 1.0f;
1108 floats[18] = 0.0f;floats[19] = 1.0f;
1109 floats[20] = floats[24] = floats[28] = floats[32] = red;
1110 floats[21] = floats[25] = floats[29] = floats[33] = green;
1111 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1112 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1114 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1115 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1118 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1122 _DrawQ_ProcessDrawFlag(flags);
1123 if(!r_draw2d.integer && !r_draw2d_force)
1126 R_Mesh_ResetTextureState();
1127 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1129 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1130 floats[0] = floats[9] = x;
1131 floats[1] = floats[4] = y;
1132 floats[3] = floats[6] = x + width;
1133 floats[7] = floats[10] = y + height;
1134 floats[12] = 0.0f;floats[13] = 0.0f;
1135 floats[14] = 1.0f;floats[15] = 0.0f;
1136 floats[16] = 1.0f;floats[17] = 1.0f;
1137 floats[18] = 0.0f;floats[19] = 1.0f;
1138 floats[20] = floats[24] = floats[28] = floats[32] = red;
1139 floats[21] = floats[25] = floats[29] = floats[33] = green;
1140 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1141 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1143 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1144 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1147 /// color tag printing
1148 static const vec4_t string_colors[] =
1151 // LordHavoc: why on earth is cyan before magenta in Quake3?
1152 // LordHavoc: note: Doom3 uses white for [0] and [7]
1153 {0.0, 0.0, 0.0, 1.0}, // black
1154 {1.0, 0.0, 0.0, 1.0}, // red
1155 {0.0, 1.0, 0.0, 1.0}, // green
1156 {1.0, 1.0, 0.0, 1.0}, // yellow
1157 {0.0, 0.0, 1.0, 1.0}, // blue
1158 {0.0, 1.0, 1.0, 1.0}, // cyan
1159 {1.0, 0.0, 1.0, 1.0}, // magenta
1160 {1.0, 1.0, 1.0, 1.0}, // white
1161 // [515]'s BX_COLOREDTEXT extension
1162 {1.0, 1.0, 1.0, 0.5}, // half transparent
1163 {0.5, 0.5, 0.5, 1.0} // half brightness
1164 // Black's color table
1165 //{1.0, 1.0, 1.0, 1.0},
1166 //{1.0, 0.0, 0.0, 1.0},
1167 //{0.0, 1.0, 0.0, 1.0},
1168 //{0.0, 0.0, 1.0, 1.0},
1169 //{1.0, 1.0, 0.0, 1.0},
1170 //{0.0, 1.0, 1.0, 1.0},
1171 //{1.0, 0.0, 1.0, 1.0},
1172 //{0.1, 0.1, 0.1, 1.0}
1175 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1177 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1179 float C = r_textcontrast.value;
1180 float B = r_textbrightness.value;
1181 if (colorindex & 0x10000) // that bit means RGB color
1183 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1184 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1185 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1186 color[3] = (colorindex & 0xf) / 15.0;
1189 Vector4Copy(string_colors[colorindex], color);
1190 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1193 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1194 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1198 // NOTE: this function always draws exactly one character if maxwidth <= 0
1199 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)
1201 const char *text_start = text;
1202 int colorindex = STRING_COLOR_DEFAULT;
1205 Uchar ch, mapch, nextch;
1206 Uchar prevch = 0; // used for kerning
1211 ft2_font_map_t *fontmap = NULL;
1212 ft2_font_map_t *map = NULL;
1213 //ft2_font_map_t *prevmap = NULL;
1214 ft2_font_t *ft2 = fnt->ft2;
1216 qboolean snap = true;
1217 qboolean least_one = false;
1218 float dw; // display w
1219 //float dh; // display h
1220 const float *width_of;
1227 // do this in the end
1228 w *= fnt->settings.scale;
1229 h *= fnt->settings.scale;
1231 // find the most fitting size:
1235 map_index = Font_IndexForSize(ft2, h, &w, &h);
1237 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1238 fontmap = Font_MapForIndex(ft2, map_index);
1247 if (!outcolor || *outcolor == -1)
1248 colorindex = STRING_COLOR_DEFAULT;
1250 colorindex = *outcolor;
1252 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1253 // ftbase_x = snap_to_pixel_x(0);
1258 maxwidth = -maxwidth;
1262 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1265 width_of = fontmap->width_of;
1267 width_of = fnt->width_of;
1269 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1272 nextch = ch = u8_getnchar(text, &text, bytes_left);
1273 i = text - text_start;
1276 if (ch == ' ' && !fontmap)
1278 if(!least_one || i0) // never skip the first character
1279 if(x + width_of[(int) ' '] * dw > maxwidth)
1282 break; // oops, can't draw this
1284 x += width_of[(int) ' '] * dw;
1287 // i points to the char after ^
1288 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1290 ch = *text; // colors are ascii, so no u8_ needed
1291 if (ch <= '9' && ch >= '0') // ^[0-9] found
1293 colorindex = ch - '0';
1298 // i points to the char after ^...
1299 // i+3 points to 3 in ^x123
1300 // i+3 == *maxlen would mean that char is missing
1301 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1303 // building colorindex...
1304 ch = tolower(text[1]);
1305 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1306 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1307 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1308 else tempcolorindex = 0;
1311 ch = tolower(text[2]);
1312 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1313 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1314 else tempcolorindex = 0;
1317 ch = tolower(text[3]);
1318 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1319 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1320 else tempcolorindex = 0;
1323 colorindex = tempcolorindex | 0xf;
1324 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1332 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1341 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1348 map = ft2_oldstyle_map;
1350 if(!least_one || i0) // never skip the first character
1351 if(x + width_of[ch] * dw > maxwidth)
1354 break; // oops, can't draw this
1356 x += width_of[ch] * dw;
1358 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1360 map = FontMap_FindForChar(fontmap, ch);
1363 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1369 mapch = ch - map->start;
1370 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1372 x += map->glyphs[mapch].advance_x * dw;
1381 *outcolor = colorindex;
1386 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)
1388 int shadow, colorindex = STRING_COLOR_DEFAULT;
1390 float x = startx, y, s, t, u, v, thisw;
1391 float *av, *at, *ac;
1394 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1395 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1396 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1397 Uchar ch, mapch, nextch;
1398 Uchar prevch = 0; // used for kerning
1401 //ft2_font_map_t *prevmap = NULL; // the previous map
1402 ft2_font_map_t *map = NULL; // the currently used map
1403 ft2_font_map_t *fontmap = NULL; // the font map for the size
1405 const char *text_start = text;
1407 ft2_font_t *ft2 = fnt->ft2;
1408 qboolean snap = true;
1412 const float *width_of;
1415 tw = R_TextureWidth(fnt->tex);
1416 th = R_TextureHeight(fnt->tex);
1424 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1425 w *= fnt->settings.scale;
1426 h *= fnt->settings.scale;
1431 map_index = Font_IndexForSize(ft2, h, &w, &h);
1433 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1434 fontmap = Font_MapForIndex(ft2, map_index);
1440 // draw the font at its baseline when using freetype
1442 ftbase_y = dh * (4.5/6.0);
1447 _DrawQ_ProcessDrawFlag(flags);
1448 if(!r_draw2d.integer && !r_draw2d_force)
1449 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1451 R_Mesh_ResetTextureState();
1453 R_Mesh_TexBind(0, fnt->tex);
1454 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1461 //ftbase_x = snap_to_pixel_x(ftbase_x);
1464 startx = snap_to_pixel_x(startx, 0.4);
1465 starty = snap_to_pixel_y(starty, 0.4);
1466 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1469 pix_x = vid.width / vid_conwidth.value;
1470 pix_y = vid.height / vid_conheight.value;
1473 width_of = fontmap->width_of;
1475 width_of = fnt->width_of;
1477 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1482 if (!outcolor || *outcolor == -1)
1483 colorindex = STRING_COLOR_DEFAULT;
1485 colorindex = *outcolor;
1487 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1494 x += r_textshadow.value * vid.width / vid_conwidth.value;
1495 y += r_textshadow.value * vid.height / vid_conheight.value;
1498 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1500 nextch = ch = u8_getnchar(text, &text, bytes_left);
1501 i = text - text_start;
1504 if (ch == ' ' && !fontmap)
1506 x += width_of[(int) ' '] * dw;
1509 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1511 ch = *text; // colors are ascii, so no u8_ needed
1512 if (ch <= '9' && ch >= '0') // ^[0-9] found
1514 colorindex = ch - '0';
1515 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1520 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1522 // building colorindex...
1523 ch = tolower(text[1]);
1524 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1525 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1526 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1527 else tempcolorindex = 0;
1530 ch = tolower(text[2]);
1531 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1532 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1533 else tempcolorindex = 0;
1536 ch = tolower(text[3]);
1537 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1538 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1539 else tempcolorindex = 0;
1542 colorindex = tempcolorindex | 0xf;
1543 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1544 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1545 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1553 else if (ch == STRING_COLOR_TAG)
1562 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1563 // this way we don't need to rebind fnt->tex for every old-style character
1564 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1567 x += 1.0/pix_x * r_textshadow.value;
1568 y += 1.0/pix_y * r_textshadow.value;
1570 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1578 if (map != ft2_oldstyle_map)
1582 // switching from freetype to non-freetype rendering
1583 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1584 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1590 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1591 map = ft2_oldstyle_map;
1595 //num = (unsigned char) text[i];
1596 //thisw = fnt->width_of[num];
1597 thisw = fnt->width_of[ch];
1598 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1599 s = (ch & 15)*0.0625f + (0.5f / tw);
1600 t = (ch >> 4)*0.0625f + (0.5f / th);
1601 u = 0.0625f * thisw - (1.0f / tw);
1602 v = 0.0625f - (1.0f / th);
1603 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1604 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1605 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1606 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1607 at[ 0] = s ; at[ 1] = t ;
1608 at[ 2] = s+u ; at[ 3] = t ;
1609 at[ 4] = s+u ; at[ 5] = t+v ;
1610 at[ 6] = s ; at[ 7] = t+v ;
1611 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1612 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1613 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1614 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1619 if (batchcount >= QUADELEMENTS_MAXQUADS)
1621 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1622 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1628 x += width_of[ch] * dw;
1630 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1632 // new charmap - need to render
1635 // we need a different character map, render what we currently have:
1636 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1637 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1644 map = FontMap_FindForChar(fontmap, ch);
1647 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1654 // this shouldn't happen
1659 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1662 mapch = ch - map->start;
1663 thisw = map->glyphs[mapch].advance_x;
1667 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1674 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1675 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1676 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1677 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1678 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1679 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1680 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1681 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1682 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1683 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1684 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1685 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1694 if (batchcount >= QUADELEMENTS_MAXQUADS)
1696 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1697 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1709 x -= 1.0/pix_x * r_textshadow.value;
1710 y -= 1.0/pix_y * r_textshadow.value;
1716 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1717 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1721 *outcolor = colorindex;
1723 // note: this relies on the proper text (not shadow) being drawn last
1727 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)
1729 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1732 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)
1734 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1737 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1739 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1742 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1744 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1749 // no ^xrgb management
1750 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1752 int color, numchars = 0;
1753 char *outputend2c = output2c + maxoutchars - 2;
1754 if (!outcolor || *outcolor == -1)
1755 color = STRING_COLOR_DEFAULT;
1759 maxreadchars = 1<<30;
1760 textend = text + maxreadchars;
1761 while (text != textend && *text)
1763 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1765 if (text[1] == STRING_COLOR_TAG)
1767 else if (text[1] >= '0' && text[1] <= '9')
1769 color = text[1] - '0';
1774 if (output2c >= outputend2c)
1776 *output2c++ = *text++;
1777 *output2c++ = color;
1780 output2c[0] = output2c[1] = 0;
1787 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)
1791 _DrawQ_ProcessDrawFlag(flags);
1792 if(!r_draw2d.integer && !r_draw2d_force)
1795 R_Mesh_ResetTextureState();
1801 height = pic->height;
1802 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1805 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1807 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1808 floats[0] = floats[9] = x;
1809 floats[1] = floats[4] = y;
1810 floats[3] = floats[6] = x + width;
1811 floats[7] = floats[10] = y + height;
1812 floats[12] = s1;floats[13] = t1;
1813 floats[14] = s2;floats[15] = t2;
1814 floats[16] = s4;floats[17] = t4;
1815 floats[18] = s3;floats[19] = t3;
1816 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1817 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1818 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1819 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1821 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1822 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1825 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1827 _DrawQ_ProcessDrawFlag(flags);
1828 if(!r_draw2d.integer && !r_draw2d_force)
1831 R_Mesh_ResetTextureState();
1832 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1834 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1835 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1838 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1842 _DrawQ_ProcessDrawFlag(flags);
1843 if(!r_draw2d.integer && !r_draw2d_force)
1847 switch(vid.renderpath)
1849 case RENDERPATH_GL11:
1850 case RENDERPATH_GL13:
1851 case RENDERPATH_GL20:
1852 case RENDERPATH_CGGL:
1854 qglBegin(GL_LINE_LOOP);
1855 for (num = 0;num < mesh->num_vertices;num++)
1857 if (mesh->data_color4f)
1858 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]);
1859 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1864 case RENDERPATH_D3D9:
1865 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1867 case RENDERPATH_D3D10:
1868 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1870 case RENDERPATH_D3D11:
1871 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1876 //[515]: this is old, delete
1877 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1879 _DrawQ_ProcessDrawFlag(flags);
1880 if(!r_draw2d.integer && !r_draw2d_force)
1883 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1885 switch(vid.renderpath)
1887 case RENDERPATH_GL11:
1888 case RENDERPATH_GL13:
1889 case RENDERPATH_GL20:
1890 case RENDERPATH_CGGL:
1893 //qglLineWidth(width);CHECKGLERROR
1895 GL_Color(r,g,b,alpha);
1898 qglVertex2f(x1, y1);
1899 qglVertex2f(x2, y2);
1903 case RENDERPATH_D3D9:
1904 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1906 case RENDERPATH_D3D10:
1907 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1909 case RENDERPATH_D3D11:
1910 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
1915 void DrawQ_SetClipArea(float x, float y, float width, float height)
1920 // We have to convert the con coords into real coords
1921 // OGL uses top to bottom
1922 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1923 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1924 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
1925 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
1926 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1928 GL_ScissorTest(true);
1931 void DrawQ_ResetClipArea(void)
1934 GL_ScissorTest(false);
1937 void DrawQ_Finish(void)
1939 r_refdef.draw2dstage = false;
1942 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1943 void R_DrawGamma(void)
1946 switch(vid.renderpath)
1948 case RENDERPATH_GL20:
1949 case RENDERPATH_CGGL:
1950 case RENDERPATH_D3D9:
1951 case RENDERPATH_D3D10:
1952 case RENDERPATH_D3D11:
1953 if (vid_usinghwgamma || v_glslgamma.integer)
1956 case RENDERPATH_GL13:
1957 case RENDERPATH_GL11:
1958 if (vid_usinghwgamma)
1962 // all the blends ignore depth
1963 R_Mesh_ResetTextureState();
1964 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1966 GL_DepthRange(0, 1);
1967 GL_PolygonOffset(0, 0);
1968 GL_DepthTest(false);
1969 if (v_color_enable.integer)
1971 c[0] = v_color_white_r.value;
1972 c[1] = v_color_white_g.value;
1973 c[2] = v_color_white_b.value;
1976 c[0] = c[1] = c[2] = v_contrast.value;
1977 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1979 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1980 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1982 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1983 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1984 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1985 VectorScale(c, 0.5, c);
1988 if (v_color_enable.integer)
1990 c[0] = v_color_black_r.value;
1991 c[1] = v_color_black_g.value;
1992 c[2] = v_color_black_b.value;
1995 c[0] = c[1] = c[2] = v_brightness.value;
1996 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1998 GL_BlendFunc(GL_ONE, GL_ONE);
1999 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
2000 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2001 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);