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"
31 dp_font_t dp_fonts[MAX_FONTS] = {{0}};
33 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)"};
34 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)"};
35 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)"};
37 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
38 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
39 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
40 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
42 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
43 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
45 extern cvar_t v_glslgamma;
47 //=============================================================================
48 /* Support Routines */
50 #define FONT_FILESIZE 13468
51 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
52 static cachepic_t cachepics[MAX_CACHED_PICS];
53 static int numcachepics;
55 static rtexturepool_t *drawtexturepool;
57 static const unsigned char concharimage[FONT_FILESIZE] =
62 static rtexture_t *draw_generateconchars(void)
69 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
71 for (i = 0;i < 8192;i++)
73 random = lhrandom (0.0,1.0);
74 data[i*4+3] = data[i*4+0];
75 data[i*4+2] = 83 + (unsigned char)(random * 64);
76 data[i*4+1] = 71 + (unsigned char)(random * 32);
77 data[i*4+0] = 23 + (unsigned char)(random * 16);
80 for (i = 8192;i < 32768;i++)
82 random = lhrandom (0.0,1.0);
83 data[i*4+3] = data[i*4+0];
84 data[i*4+2] = 95 + (unsigned char)(random * 64);
85 data[i*4+1] = 95 + (unsigned char)(random * 64);
86 data[i*4+0] = 95 + (unsigned char)(random * 64);
89 for (i = 32768;i < 40960;i++)
91 random = lhrandom (0.0,1.0);
92 data[i*4+3] = data[i*4+0];
93 data[i*4+2] = 83 + (unsigned char)(random * 64);
94 data[i*4+1] = 71 + (unsigned char)(random * 32);
95 data[i*4+0] = 23 + (unsigned char)(random * 16);
98 for (i = 40960;i < 65536;i++)
100 random = lhrandom (0.0,1.0);
101 data[i*4+3] = data[i*4+0];
102 data[i*4+2] = 96 + (unsigned char)(random * 64);
103 data[i*4+1] = 43 + (unsigned char)(random * 32);
104 data[i*4+0] = 27 + (unsigned char)(random * 32);
108 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
111 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, NULL);
116 static rtexture_t *draw_generateditherpattern(void)
119 unsigned char pixels[8][8];
120 for (y = 0;y < 8;y++)
121 for (x = 0;x < 8;x++)
122 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
123 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, palette_bgra_transparent);
126 typedef struct embeddedpic_s
135 static const embeddedpic_t embeddedpics[] =
138 "gfx/prydoncursor001", 16, 16,
157 "ui/mousepointer", 16, 16,
176 "gfx/crosshair1", 16, 16,
195 "gfx/crosshair2", 16, 16,
214 "gfx/crosshair3", 16, 16,
233 "gfx/crosshair4", 16, 16,
252 "gfx/crosshair5", 8, 8,
263 "gfx/crosshair6", 2, 2,
268 "gfx/crosshair7", 16, 16,
289 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
291 const embeddedpic_t *p;
292 for (p = embeddedpics;p->name;p++)
293 if (!strcmp(name, p->name))
294 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, palette_bgra_embeddedpic);
295 if (!strcmp(name, "gfx/conchars"))
296 return draw_generateconchars();
297 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
298 return draw_generateditherpattern();
300 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
301 return r_texture_notexture;
310 // FIXME: move this to client somehow
311 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
314 unsigned char *pixels;
317 unsigned char *lmpdata;
318 char lmpname[MAX_QPATH];
320 // check whether the picture has already been cached
321 crc = CRC_Block((unsigned char *)path, strlen(path));
322 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
323 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
324 if (!strcmp (path, pic->name))
327 if (numcachepics == MAX_CACHED_PICS)
329 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
330 // FIXME: support NULL in callers?
331 return cachepics; // return the first one
333 pic = cachepics + (numcachepics++);
334 strlcpy (pic->name, path, sizeof(pic->name));
336 pic->chain = cachepichash[hashkey];
337 cachepichash[hashkey] = pic;
339 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
340 pic->tex = CL_GetDynTexture( path );
341 // if so, set the width/height, too
343 pic->width = R_TextureWidth(pic->tex);
344 pic->height = R_TextureHeight(pic->tex);
345 // we're done now (early-out)
349 pic->texflags = TEXF_ALPHA;
350 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
351 pic->texflags |= TEXF_CLAMP;
352 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
353 pic->texflags |= TEXF_COMPRESS;
355 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
357 // load a high quality image from disk if possible
358 pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer);
359 if (pixels == NULL && !strncmp(path, "gfx/", 4))
360 pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer);
363 pic->width = image_width;
364 pic->height = image_height;
366 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, NULL);
370 pic->autoload = false;
371 // never compress the fallback images
372 pic->texflags &= ~TEXF_COMPRESS;
375 // now read the low quality version (wad or lmp file), and take the pic
376 // size from that even if we don't upload the texture, this way the pics
377 // show up the right size in the menu even if they were replaced with
378 // higher or lower resolution versions
379 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
380 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
382 if (developer_loading.integer)
383 Con_Printf("loading lump \"%s\"\n", path);
387 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
388 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
389 // if no high quality replacement image was found, upload the original low quality texture
391 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
395 else if ((lmpdata = W_GetLumpName (path + 4)))
397 if (developer_loading.integer)
398 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
400 if (!strcmp(path, "gfx/conchars"))
402 // conchars is a raw image and with color 0 as transparent instead of 255
405 // if no high quality replacement image was found, upload the original low quality texture
407 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, palette_bgra_font);
411 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
412 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
413 // if no high quality replacement image was found, upload the original low quality texture
415 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
424 else if (pic->tex == NULL)
426 // if it's not found on disk, generate an image
427 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
428 pic->width = R_TextureWidth(pic->tex);
429 pic->height = R_TextureHeight(pic->tex);
435 cachepic_t *Draw_CachePic (const char *path)
437 return Draw_CachePic_Flags (path, 0);
442 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
444 if (pic->autoload && !pic->tex)
446 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
447 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
448 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer);
449 if (pic->tex == NULL)
450 pic->tex = draw_generatepic(pic->name, true);
452 pic->lastusedframe = draw_frame;
456 void Draw_Frame(void)
460 static double nextpurgetime;
461 if (nextpurgetime > realtime)
463 nextpurgetime = realtime + 0.05;
464 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
466 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
468 R_FreeTexture(pic->tex);
475 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
480 crc = CRC_Block((unsigned char *)picname, strlen(picname));
481 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
482 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
483 if (!strcmp (picname, pic->name))
488 if (pic->tex && pic->width == width && pic->height == height)
490 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
498 if (numcachepics == MAX_CACHED_PICS)
500 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
501 // FIXME: support NULL in callers?
502 return cachepics; // return the first one
504 pic = cachepics + (numcachepics++);
505 strlcpy (pic->name, picname, sizeof(pic->name));
507 pic->chain = cachepichash[hashkey];
508 cachepichash[hashkey] = pic;
513 pic->height = height;
515 R_FreeTexture(pic->tex);
516 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL);
520 void Draw_FreePic(const char *picname)
525 // this doesn't really free the pic, but does free it's texture
526 crc = CRC_Block((unsigned char *)picname, strlen(picname));
527 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
528 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
530 if (!strcmp (picname, pic->name) && pic->tex)
532 R_FreeTexture(pic->tex);
541 static float snap_to_pixel_x(float x, float roundUpAt);
542 extern int con_linewidth; // to force rewrapping
543 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
546 float maxwidth, scale;
547 char widthfile[MAX_QPATH];
549 fs_offset_t widthbufsize;
551 if(override || !fnt->texpath[0])
553 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
555 // load the cvars when the font is FIRST loaded
556 fnt->settings.antialias = r_font_antialias.integer;
557 fnt->settings.hinting = r_font_hinting.integer;
558 fnt->settings.outline = r_font_postprocess_outline.value;
559 fnt->settings.blur = r_font_postprocess_blur.value;
560 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
561 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
562 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
565 if(drawtexturepool == NULL)
566 return; // before gl_draw_start, so will be loaded later
570 // clear freetype font
571 Font_UnloadFont(fnt->ft2);
576 if(fnt->req_face != -1)
578 if(!Font_LoadFont(fnt->texpath, fnt))
579 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
582 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
583 if(fnt->tex == r_texture_notexture)
585 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
587 if (!fnt->fallbacks[i][0])
589 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
590 if(fnt->tex != r_texture_notexture)
593 if(fnt->tex == r_texture_notexture)
595 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
596 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
599 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
602 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
604 // unspecified width == 1 (base width)
605 for(i = 1; i < 256; ++i)
606 fnt->width_of[i] = 1;
609 // FIXME load "name.width", if it fails, fill all with 1
610 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
612 float extraspacing = 0;
613 const char *p = widthbuf;
618 if(!COM_ParseToken_Simple(&p, false, false))
636 fnt->width_of[ch] = atof(com_token) + extraspacing;
639 for (i = 0; i < MAX_FONT_SIZES; ++i)
641 //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
642 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
645 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
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 scale = atof(com_token);
665 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
666 if(!COM_ParseToken_Simple(&p, false, false))
676 maxwidth = fnt->width_of[1];
677 for(i = 2; i < 256; ++i)
678 maxwidth = max(maxwidth, fnt->width_of[i]);
679 fnt->maxwidth = maxwidth;
681 // fix up maxwidth for overlap
682 fnt->maxwidth *= scale;
685 if(fnt == FONT_CONSOLE)
686 con_linewidth = -1; // rewrap console in next frame
689 static dp_font_t *FindFont(const char *title)
692 for(i = 0; i < MAX_FONTS; ++i)
693 if(!strcmp(dp_fonts[i].title, title))
698 static float snap_to_pixel_x(float x, float roundUpAt)
700 float pixelpos = x * vid.width / vid_conwidth.value;
701 int snap = (int) pixelpos;
702 if (pixelpos - snap >= roundUpAt) ++snap;
703 return ((float)snap * vid_conwidth.value / vid.width);
705 x = (int)(x * vid.width / vid_conwidth.value);
706 x = (x * vid_conwidth.value / vid.width);
711 static float snap_to_pixel_y(float y, float roundUpAt)
713 float pixelpos = y * vid.height / vid_conheight.value;
714 int snap = (int) pixelpos;
715 if (pixelpos - snap > roundUpAt) ++snap;
716 return ((float)snap * vid_conheight.value / vid.height);
718 y = (int)(y * vid.height / vid_conheight.value);
719 y = (y * vid_conheight.value / vid.height);
724 static void LoadFont_f(void)
728 const char *filelist, *c, *cm;
730 char mainfont[MAX_QPATH];
734 Con_Printf("Available font commands:\n");
735 for(i = 0; i < MAX_FONTS; ++i)
736 Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts[i].title);
737 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
738 "can specify multiple fonts and faces\n"
739 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
740 "to load face 2 of the font gfx/vera-sans and use face 1\n"
741 "of gfx/fallback as fallback font.\n"
742 "You can also specify a list of font sizes to load, like this:\n"
743 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
744 "In many cases, 8 12 16 24 32 should be a good choice.\n"
748 f = FindFont(Cmd_Argv(1));
751 Con_Printf("font function not found\n");
756 filelist = "gfx/conchars";
758 filelist = Cmd_Argv(2);
760 memset(f->fallbacks, 0, sizeof(f->fallbacks));
761 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
763 // first font is handled "normally"
764 c = strchr(filelist, ':');
765 cm = strchr(filelist, ',');
766 if(c && (!cm || c < cm))
767 f->req_face = atoi(c+1);
774 if(!c || (c - filelist) > MAX_QPATH)
775 strlcpy(mainfont, filelist, sizeof(mainfont));
778 memcpy(mainfont, filelist, c - filelist);
779 mainfont[c - filelist] = 0;
782 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
784 c = strchr(filelist, ',');
790 c = strchr(filelist, ':');
791 cm = strchr(filelist, ',');
792 if(c && (!cm || c < cm))
793 f->fallback_faces[i] = atoi(c+1);
796 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
799 if(!c || (c-filelist) > MAX_QPATH)
801 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
805 memcpy(f->fallbacks[i], filelist, c - filelist);
806 f->fallbacks[i][c - filelist] = 0;
810 // for now: by default load only one size: the default size
812 for(i = 1; i < MAX_FONT_SIZES; ++i)
813 f->req_sizes[i] = -1;
817 for(i = 0; i < Cmd_Argc()-3; ++i)
819 sz = atof(Cmd_Argv(i+3));
820 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
821 f->req_sizes[i] = sz;
825 LoadFont(true, mainfont, f);
833 static void gl_draw_start(void)
836 drawtexturepool = R_AllocTexturePool();
839 memset(cachepichash, 0, sizeof(cachepichash));
843 for(i = 0; i < MAX_FONTS; ++i)
844 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
846 // draw the loading screen so people have something to see in the newly opened window
847 SCR_UpdateLoadingScreen(true);
850 static void gl_draw_shutdown(void)
854 R_FreeTexturePool(&drawtexturepool);
857 memset(cachepichash, 0, sizeof(cachepichash));
860 static void gl_draw_newmap(void)
865 void GL_Draw_Init (void)
868 Cvar_RegisterVariable(&r_font_postprocess_blur);
869 Cvar_RegisterVariable(&r_font_postprocess_outline);
870 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
871 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
872 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
873 Cvar_RegisterVariable(&r_font_hinting);
874 Cvar_RegisterVariable(&r_font_antialias);
875 Cvar_RegisterVariable(&r_textshadow);
876 Cvar_RegisterVariable(&r_textbrightness);
877 Cvar_RegisterVariable(&r_textcontrast);
878 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
879 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
881 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
882 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
883 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
884 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
885 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
886 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
887 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
888 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
889 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
890 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
891 if(!FONT_USER[i].title[0])
892 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
895 void _DrawQ_Setup(void)
897 r_viewport_t viewport;
898 if (r_refdef.draw2dstage)
900 r_refdef.draw2dstage = true;
902 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);
903 R_SetViewport(&viewport);
904 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
905 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
906 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
907 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
908 R_EntityMatrix(&identitymatrix);
912 GL_PolygonOffset(0, 0);
916 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
919 static void _DrawQ_ProcessDrawFlag(int flags)
923 if(flags == DRAWFLAG_ADDITIVE)
924 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
925 else if(flags == DRAWFLAG_MODULATE)
926 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
927 else if(flags == DRAWFLAG_2XMODULATE)
928 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
929 else if(flags == DRAWFLAG_SCREEN)
930 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
932 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
935 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
939 _DrawQ_ProcessDrawFlag(flags);
940 GL_Color(red, green, blue, alpha);
942 R_Mesh_VertexPointer(floats, 0, 0);
943 R_Mesh_ColorPointer(NULL, 0, 0);
944 R_Mesh_ResetTextureState();
950 height = pic->height;
951 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
952 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
955 floats[12] = 0.0f;floats[13] = 0.0f;
956 floats[14] = 1.0f;floats[15] = 0.0f;
957 floats[16] = 1.0f;floats[17] = 1.0f;
958 floats[18] = 0.0f;floats[19] = 1.0f;
960 // AK07: lets be texel correct on the corners
962 float horz_offset = 0.5f / pic->width;
963 float vert_offset = 0.5f / pic->height;
965 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
966 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
967 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
968 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
973 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
975 floats[2] = floats[5] = floats[8] = floats[11] = 0;
976 floats[0] = floats[9] = x;
977 floats[1] = floats[4] = y;
978 floats[3] = floats[6] = x + width;
979 floats[7] = floats[10] = y + height;
981 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
984 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)
987 float af = DEG2RAD(-angle); // forward
988 float ar = DEG2RAD(-angle + 90); // right
989 float sinaf = sin(af);
990 float cosaf = cos(af);
991 float sinar = sin(ar);
992 float cosar = cos(ar);
994 _DrawQ_ProcessDrawFlag(flags);
995 GL_Color(red, green, blue, alpha);
997 R_Mesh_VertexPointer(floats, 0, 0);
998 R_Mesh_ColorPointer(NULL, 0, 0);
999 R_Mesh_ResetTextureState();
1005 height = pic->height;
1006 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1007 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1009 floats[12] = 0.0f;floats[13] = 0.0f;
1010 floats[14] = 1.0f;floats[15] = 0.0f;
1011 floats[16] = 1.0f;floats[17] = 1.0f;
1012 floats[18] = 0.0f;floats[19] = 1.0f;
1015 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1017 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1020 floats[0] = x - cosaf*org_x - cosar*org_y;
1021 floats[1] = y - sinaf*org_x - sinar*org_y;
1024 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1025 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1028 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1029 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1032 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1033 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1035 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1038 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1042 _DrawQ_ProcessDrawFlag(flags);
1043 GL_Color(red, green, blue, alpha);
1045 R_Mesh_VertexPointer(floats, 0, 0);
1046 R_Mesh_ColorPointer(NULL, 0, 0);
1047 R_Mesh_ResetTextureState();
1048 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1050 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1051 floats[0] = floats[9] = x;
1052 floats[1] = floats[4] = y;
1053 floats[3] = floats[6] = x + width;
1054 floats[7] = floats[10] = y + height;
1056 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1059 /// color tag printing
1060 static const vec4_t string_colors[] =
1063 // LordHavoc: why on earth is cyan before magenta in Quake3?
1064 // LordHavoc: note: Doom3 uses white for [0] and [7]
1065 {0.0, 0.0, 0.0, 1.0}, // black
1066 {1.0, 0.0, 0.0, 1.0}, // red
1067 {0.0, 1.0, 0.0, 1.0}, // green
1068 {1.0, 1.0, 0.0, 1.0}, // yellow
1069 {0.0, 0.0, 1.0, 1.0}, // blue
1070 {0.0, 1.0, 1.0, 1.0}, // cyan
1071 {1.0, 0.0, 1.0, 1.0}, // magenta
1072 {1.0, 1.0, 1.0, 1.0}, // white
1073 // [515]'s BX_COLOREDTEXT extension
1074 {1.0, 1.0, 1.0, 0.5}, // half transparent
1075 {0.5, 0.5, 0.5, 1.0} // half brightness
1076 // Black's color table
1077 //{1.0, 1.0, 1.0, 1.0},
1078 //{1.0, 0.0, 0.0, 1.0},
1079 //{0.0, 1.0, 0.0, 1.0},
1080 //{0.0, 0.0, 1.0, 1.0},
1081 //{1.0, 1.0, 0.0, 1.0},
1082 //{0.0, 1.0, 1.0, 1.0},
1083 //{1.0, 0.0, 1.0, 1.0},
1084 //{0.1, 0.1, 0.1, 1.0}
1087 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1089 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1091 float C = r_textcontrast.value;
1092 float B = r_textbrightness.value;
1093 if (colorindex & 0x10000) // that bit means RGB color
1095 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1096 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1097 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1098 color[3] = (colorindex & 0xf) / 15.0;
1101 Vector4Copy(string_colors[colorindex], color);
1102 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1105 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1106 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1110 // NOTE: this function always draws exactly one character if maxwidth <= 0
1111 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)
1113 const char *text_start = text;
1114 int colorindex = STRING_COLOR_DEFAULT;
1117 Uchar ch, mapch, nextch;
1118 Uchar prevch = 0; // used for kerning
1123 ft2_font_map_t *fontmap = NULL;
1124 ft2_font_map_t *map = NULL;
1125 //ft2_font_map_t *prevmap = NULL;
1126 ft2_font_t *ft2 = fnt->ft2;
1128 qboolean snap = true;
1129 qboolean least_one = false;
1130 float dw; // display w
1131 //float dh; // display h
1132 const float *width_of;
1139 // do this in the end
1143 // find the most fitting size:
1147 map_index = Font_IndexForSize(ft2, h, &w, &h);
1149 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1150 fontmap = Font_MapForIndex(ft2, map_index);
1159 if (!outcolor || *outcolor == -1)
1160 colorindex = STRING_COLOR_DEFAULT;
1162 colorindex = *outcolor;
1164 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1165 // ftbase_x = snap_to_pixel_x(0);
1170 maxwidth = -maxwidth;
1174 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1177 width_of = fontmap->width_of;
1179 width_of = fnt->width_of;
1181 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1184 nextch = ch = u8_getnchar(text, &text, bytes_left);
1185 i = text - text_start;
1188 if (ch == ' ' && !fontmap)
1190 if(!least_one || i0) // never skip the first character
1191 if(x + width_of[(int) ' '] * dw > maxwidth)
1194 break; // oops, can't draw this
1196 x += width_of[(int) ' '] * dw;
1199 // i points to the char after ^
1200 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1202 ch = *text; // colors are ascii, so no u8_ needed
1203 if (ch <= '9' && ch >= '0') // ^[0-9] found
1205 colorindex = ch - '0';
1210 // i points to the char after ^...
1211 // i+3 points to 3 in ^x123
1212 // i+3 == *maxlen would mean that char is missing
1213 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1215 // building colorindex...
1216 ch = tolower(text[1]);
1217 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1218 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1219 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1220 else tempcolorindex = 0;
1223 ch = tolower(text[2]);
1224 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1225 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1226 else tempcolorindex = 0;
1229 ch = tolower(text[3]);
1230 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1231 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1232 else tempcolorindex = 0;
1235 colorindex = tempcolorindex | 0xf;
1236 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1244 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1253 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1260 map = ft2_oldstyle_map;
1262 if(!least_one || i0) // never skip the first character
1263 if(x + width_of[ch] * dw > maxwidth)
1266 break; // oops, can't draw this
1268 x += width_of[ch] * dw;
1270 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1272 map = FontMap_FindForChar(fontmap, ch);
1275 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1281 mapch = ch - map->start;
1282 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1284 x += map->glyphs[mapch].advance_x * dw;
1293 *outcolor = colorindex;
1298 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)
1300 int shadow, colorindex = STRING_COLOR_DEFAULT;
1302 float x = startx, y, s, t, u, v, thisw;
1303 float *av, *at, *ac;
1306 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1307 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1308 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1309 Uchar ch, mapch, nextch;
1310 Uchar prevch = 0; // used for kerning
1313 //ft2_font_map_t *prevmap = NULL; // the previous map
1314 ft2_font_map_t *map = NULL; // the currently used map
1315 ft2_font_map_t *fontmap = NULL; // the font map for the size
1317 const char *text_start = text;
1319 ft2_font_t *ft2 = fnt->ft2;
1320 qboolean snap = true;
1324 const float *width_of;
1327 tw = R_TextureWidth(fnt->tex);
1328 th = R_TextureHeight(fnt->tex);
1336 starty -= (fnt->scale - 1) * h * 0.5; // center
1343 map_index = Font_IndexForSize(ft2, h, &w, &h);
1345 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1346 fontmap = Font_MapForIndex(ft2, map_index);
1352 // draw the font at its baseline when using freetype
1354 ftbase_y = dh * (4.5/6.0);
1359 _DrawQ_ProcessDrawFlag(flags);
1361 R_Mesh_ColorPointer(color4f, 0, 0);
1362 R_Mesh_ResetTextureState();
1364 R_Mesh_TexBind(0, fnt->tex);
1365 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1366 R_Mesh_VertexPointer(vertex3f, 0, 0);
1367 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1374 //ftbase_x = snap_to_pixel_x(ftbase_x);
1377 startx = snap_to_pixel_x(startx, 0.4);
1378 starty = snap_to_pixel_y(starty, 0.4);
1379 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1382 pix_x = vid.width / vid_conwidth.value;
1383 pix_y = vid.height / vid_conheight.value;
1386 width_of = fontmap->width_of;
1388 width_of = fnt->width_of;
1390 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1395 if (!outcolor || *outcolor == -1)
1396 colorindex = STRING_COLOR_DEFAULT;
1398 colorindex = *outcolor;
1400 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1407 x += r_textshadow.value * vid.width / vid_conwidth.value;
1408 y += r_textshadow.value * vid.height / vid_conheight.value;
1411 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1413 nextch = ch = u8_getnchar(text, &text, bytes_left);
1414 i = text - text_start;
1417 if (ch == ' ' && !fontmap)
1419 x += width_of[(int) ' '] * dw;
1422 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1424 ch = *text; // colors are ascii, so no u8_ needed
1425 if (ch <= '9' && ch >= '0') // ^[0-9] found
1427 colorindex = ch - '0';
1428 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1433 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1435 // building colorindex...
1436 ch = tolower(text[1]);
1437 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1438 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1439 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1440 else tempcolorindex = 0;
1443 ch = tolower(text[2]);
1444 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1445 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1446 else tempcolorindex = 0;
1449 ch = tolower(text[3]);
1450 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1451 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1452 else tempcolorindex = 0;
1455 colorindex = tempcolorindex | 0xf;
1456 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1457 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1458 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1466 else if (ch == STRING_COLOR_TAG)
1475 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1476 // this way we don't need to rebind fnt->tex for every old-style character
1477 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1480 x += 1.0/pix_x * r_textshadow.value;
1481 y += 1.0/pix_y * r_textshadow.value;
1483 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1491 if (map != ft2_oldstyle_map)
1495 // switching from freetype to non-freetype rendering
1496 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1502 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1503 map = ft2_oldstyle_map;
1507 //num = (unsigned char) text[i];
1508 //thisw = fnt->width_of[num];
1509 thisw = fnt->width_of[ch];
1510 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1511 s = (ch & 15)*0.0625f + (0.5f / tw);
1512 t = (ch >> 4)*0.0625f + (0.5f / th);
1513 u = 0.0625f * thisw - (1.0f / tw);
1514 v = 0.0625f - (1.0f / th);
1515 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1516 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1517 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1518 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1519 at[ 0] = s ; at[ 1] = t ;
1520 at[ 2] = s+u ; at[ 3] = t ;
1521 at[ 4] = s+u ; at[ 5] = t+v ;
1522 at[ 6] = s ; at[ 7] = t+v ;
1523 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1524 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1525 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1526 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1531 if (batchcount >= QUADELEMENTS_MAXQUADS)
1533 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1539 x += width_of[ch] * dw;
1541 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1543 // new charmap - need to render
1546 // we need a different character map, render what we currently have:
1547 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1554 map = FontMap_FindForChar(fontmap, ch);
1557 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1564 // this shouldn't happen
1569 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1572 mapch = ch - map->start;
1573 thisw = map->glyphs[mapch].advance_x;
1577 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1584 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1585 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1586 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1587 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1588 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1589 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1590 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1591 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1592 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1593 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1594 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1595 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1604 if (batchcount >= QUADELEMENTS_MAXQUADS)
1606 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1618 x -= 1.0/pix_x * r_textshadow.value;
1619 y -= 1.0/pix_y * r_textshadow.value;
1624 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1627 *outcolor = colorindex;
1629 // note: this relies on the proper text (not shadow) being drawn last
1633 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)
1635 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1638 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)
1640 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1643 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1645 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1648 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1650 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1655 // no ^xrgb management
1656 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1658 int color, numchars = 0;
1659 char *outputend2c = output2c + maxoutchars - 2;
1660 if (!outcolor || *outcolor == -1)
1661 color = STRING_COLOR_DEFAULT;
1665 maxreadchars = 1<<30;
1666 textend = text + maxreadchars;
1667 while (text != textend && *text)
1669 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1671 if (text[1] == STRING_COLOR_TAG)
1673 else if (text[1] >= '0' && text[1] <= '9')
1675 color = text[1] - '0';
1680 if (output2c >= outputend2c)
1682 *output2c++ = *text++;
1683 *output2c++ = color;
1686 output2c[0] = output2c[1] = 0;
1693 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)
1697 _DrawQ_ProcessDrawFlag(flags);
1699 R_Mesh_VertexPointer(floats, 0, 0);
1700 R_Mesh_ColorPointer(floats + 20, 0, 0);
1701 R_Mesh_ResetTextureState();
1707 height = pic->height;
1708 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1709 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1710 floats[12] = s1;floats[13] = t1;
1711 floats[14] = s2;floats[15] = t2;
1712 floats[16] = s4;floats[17] = t4;
1713 floats[18] = s3;floats[19] = t3;
1716 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1718 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1719 floats[0] = floats[9] = x;
1720 floats[1] = floats[4] = y;
1721 floats[3] = floats[6] = x + width;
1722 floats[7] = floats[10] = y + height;
1723 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1724 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1725 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1726 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1728 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1731 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1733 _DrawQ_ProcessDrawFlag(flags);
1735 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1736 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1737 R_Mesh_ResetTextureState();
1738 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1739 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1741 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1744 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1748 _DrawQ_ProcessDrawFlag(flags);
1752 qglBegin(GL_LINE_LOOP);
1753 for (num = 0;num < mesh->num_vertices;num++)
1755 if (mesh->data_color4f)
1756 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]);
1757 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1763 //[515]: this is old, delete
1764 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1766 _DrawQ_ProcessDrawFlag(flags);
1768 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1771 //qglLineWidth(width);CHECKGLERROR
1773 GL_Color(r,g,b,alpha);
1776 qglVertex2f(x1, y1);
1777 qglVertex2f(x2, y2);
1782 void DrawQ_SetClipArea(float x, float y, float width, float height)
1787 // We have to convert the con coords into real coords
1788 // OGL uses top to bottom
1789 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1790 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1791 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1792 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1793 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1795 GL_ScissorTest(true);
1798 void DrawQ_ResetClipArea(void)
1801 GL_ScissorTest(false);
1804 void DrawQ_Finish(void)
1806 r_refdef.draw2dstage = false;
1809 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1810 void R_DrawGamma(void)
1813 switch(vid.renderpath)
1815 case RENDERPATH_GL20:
1816 case RENDERPATH_CGGL:
1817 if (vid_usinghwgamma || v_glslgamma.integer)
1820 case RENDERPATH_GL13:
1821 case RENDERPATH_GL11:
1822 if (vid_usinghwgamma)
1826 // all the blends ignore depth
1827 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1828 R_Mesh_ColorPointer(NULL, 0, 0);
1829 R_Mesh_ResetTextureState();
1830 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1832 GL_DepthRange(0, 1);
1833 GL_PolygonOffset(0, 0);
1834 GL_DepthTest(false);
1835 if (v_color_enable.integer)
1837 c[0] = v_color_white_r.value;
1838 c[1] = v_color_white_g.value;
1839 c[2] = v_color_white_b.value;
1842 c[0] = c[1] = c[2] = v_contrast.value;
1843 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1845 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1846 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1848 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1849 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1850 VectorScale(c, 0.5, c);
1853 if (v_color_enable.integer)
1855 c[0] = v_color_black_r.value;
1856 c[1] = v_color_black_g.value;
1857 c[2] = v_color_black_b.value;
1860 c[0] = c[1] = c[2] = v_brightness.value;
1861 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1863 GL_BlendFunc(GL_ONE, GL_ONE);
1864 GL_Color(c[0], c[1], c[2], 1);
1865 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);