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);
359 if (pixels == NULL && !strncmp(path, "gfx/", 4))
360 pixels = loadimagepixelsbgra(path+4, false, true);
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);
447 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
448 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true);
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;
462 if (nextpurgetime > realtime)
464 nextpurgetime = realtime + 0.05;
465 purgeframe = draw_frame - 1;
466 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
468 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
470 R_FreeTexture(pic->tex);
477 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
482 crc = CRC_Block((unsigned char *)picname, strlen(picname));
483 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
484 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
485 if (!strcmp (picname, pic->name))
490 if (pic->tex && pic->width == width && pic->height == height)
492 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
500 if (numcachepics == MAX_CACHED_PICS)
502 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
503 // FIXME: support NULL in callers?
504 return cachepics; // return the first one
506 pic = cachepics + (numcachepics++);
507 strlcpy (pic->name, picname, sizeof(pic->name));
509 pic->chain = cachepichash[hashkey];
510 cachepichash[hashkey] = pic;
515 pic->height = height;
517 R_FreeTexture(pic->tex);
518 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL);
522 void Draw_FreePic(const char *picname)
527 // this doesn't really free the pic, but does free it's texture
528 crc = CRC_Block((unsigned char *)picname, strlen(picname));
529 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
530 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
532 if (!strcmp (picname, pic->name) && pic->tex)
534 R_FreeTexture(pic->tex);
543 static float snap_to_pixel_x(float x, float roundUpAt);
544 extern int con_linewidth; // to force rewrapping
545 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
548 float maxwidth, scale;
549 char widthfile[MAX_QPATH];
551 fs_offset_t widthbufsize;
553 if(override || !fnt->texpath[0])
555 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
557 // load the cvars when the font is FIRST loaded
558 fnt->settings.antialias = r_font_antialias.integer;
559 fnt->settings.hinting = r_font_hinting.integer;
560 fnt->settings.outline = r_font_postprocess_outline.value;
561 fnt->settings.blur = r_font_postprocess_blur.value;
562 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
563 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
564 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
567 if(drawtexturepool == NULL)
568 return; // before gl_draw_start, so will be loaded later
572 // clear freetype font
573 Font_UnloadFont(fnt->ft2);
578 if(fnt->req_face != -1)
580 if(!Font_LoadFont(fnt->texpath, fnt))
581 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
584 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
585 if(fnt->tex == r_texture_notexture)
587 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
589 if (!fnt->fallbacks[i][0])
591 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
592 if(fnt->tex != r_texture_notexture)
595 if(fnt->tex == r_texture_notexture)
597 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
598 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
601 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
604 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
606 // unspecified width == 1 (base width)
607 for(i = 1; i < 256; ++i)
608 fnt->width_of[i] = 1;
611 // FIXME load "name.width", if it fails, fill all with 1
612 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
614 float extraspacing = 0;
615 const char *p = widthbuf;
620 if(!COM_ParseToken_Simple(&p, false, false))
638 fnt->width_of[ch] = atof(com_token) + extraspacing;
641 for (i = 0; i < MAX_FONT_SIZES; ++i)
643 //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
644 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
647 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
653 if(!strcmp(com_token, "extraspacing"))
655 if(!COM_ParseToken_Simple(&p, false, false))
657 extraspacing = atof(com_token);
659 else if(!strcmp(com_token, "scale"))
661 if(!COM_ParseToken_Simple(&p, false, false))
663 scale = atof(com_token);
667 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
668 if(!COM_ParseToken_Simple(&p, false, false))
678 maxwidth = fnt->width_of[1];
679 for(i = 2; i < 256; ++i)
680 maxwidth = max(maxwidth, fnt->width_of[i]);
681 fnt->maxwidth = maxwidth;
683 // fix up maxwidth for overlap
684 fnt->maxwidth *= scale;
687 if(fnt == FONT_CONSOLE)
688 con_linewidth = -1; // rewrap console in next frame
691 static dp_font_t *FindFont(const char *title)
694 for(i = 0; i < MAX_FONTS; ++i)
695 if(!strcmp(dp_fonts[i].title, title))
700 static float snap_to_pixel_x(float x, float roundUpAt)
702 float pixelpos = x * vid.width / vid_conwidth.value;
703 int snap = (int) pixelpos;
704 if (pixelpos - snap >= roundUpAt) ++snap;
705 return ((float)snap * vid_conwidth.value / vid.width);
707 x = (int)(x * vid.width / vid_conwidth.value);
708 x = (x * vid_conwidth.value / vid.width);
713 static float snap_to_pixel_y(float y, float roundUpAt)
715 float pixelpos = y * vid.height / vid_conheight.value;
716 int snap = (int) pixelpos;
717 if (pixelpos - snap > roundUpAt) ++snap;
718 return ((float)snap * vid_conheight.value / vid.height);
720 y = (int)(y * vid.height / vid_conheight.value);
721 y = (y * vid_conheight.value / vid.height);
726 static void LoadFont_f(void)
730 const char *filelist, *c, *cm;
732 char mainfont[MAX_QPATH];
736 Con_Printf("Available font commands:\n");
737 for(i = 0; i < MAX_FONTS; ++i)
738 Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts[i].title);
739 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
740 "can specify multiple fonts and faces\n"
741 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
742 "to load face 2 of the font gfx/vera-sans and use face 1\n"
743 "of gfx/fallback as fallback font.\n"
744 "You can also specify a list of font sizes to load, like this:\n"
745 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
746 "In many cases, 8 12 16 24 32 should be a good choice.\n"
750 f = FindFont(Cmd_Argv(1));
753 Con_Printf("font function not found\n");
758 filelist = "gfx/conchars";
760 filelist = Cmd_Argv(2);
762 memset(f->fallbacks, 0, sizeof(f->fallbacks));
763 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
765 // first font is handled "normally"
766 c = strchr(filelist, ':');
767 cm = strchr(filelist, ',');
768 if(c && (!cm || c < cm))
769 f->req_face = atoi(c+1);
776 if(!c || (c - filelist) > MAX_QPATH)
777 strlcpy(mainfont, filelist, sizeof(mainfont));
780 memcpy(mainfont, filelist, c - filelist);
781 mainfont[c - filelist] = 0;
784 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
786 c = strchr(filelist, ',');
792 c = strchr(filelist, ':');
793 cm = strchr(filelist, ',');
794 if(c && (!cm || c < cm))
795 f->fallback_faces[i] = atoi(c+1);
798 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
801 if(!c || (c-filelist) > MAX_QPATH)
803 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
807 memcpy(f->fallbacks[i], filelist, c - filelist);
808 f->fallbacks[i][c - filelist] = 0;
812 // for now: by default load only one size: the default size
814 for(i = 1; i < MAX_FONT_SIZES; ++i)
815 f->req_sizes[i] = -1;
819 for(i = 0; i < Cmd_Argc()-3; ++i)
821 sz = atof(Cmd_Argv(i+3));
822 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
823 f->req_sizes[i] = sz;
827 LoadFont(true, mainfont, f);
835 static void gl_draw_start(void)
838 drawtexturepool = R_AllocTexturePool();
841 memset(cachepichash, 0, sizeof(cachepichash));
845 for(i = 0; i < MAX_FONTS; ++i)
846 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
848 // draw the loading screen so people have something to see in the newly opened window
849 SCR_UpdateLoadingScreen(true);
852 static void gl_draw_shutdown(void)
856 R_FreeTexturePool(&drawtexturepool);
859 memset(cachepichash, 0, sizeof(cachepichash));
862 static void gl_draw_newmap(void)
867 void GL_Draw_Init (void)
870 Cvar_RegisterVariable(&r_font_postprocess_blur);
871 Cvar_RegisterVariable(&r_font_postprocess_outline);
872 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
873 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
874 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
875 Cvar_RegisterVariable(&r_font_hinting);
876 Cvar_RegisterVariable(&r_font_antialias);
877 Cvar_RegisterVariable(&r_textshadow);
878 Cvar_RegisterVariable(&r_textbrightness);
879 Cvar_RegisterVariable(&r_textcontrast);
880 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
881 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
883 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
884 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
885 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
886 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
887 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
888 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
889 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
890 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
891 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
892 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
893 if(!FONT_USER[i].title[0])
894 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
897 void _DrawQ_Setup(void)
899 r_viewport_t viewport;
900 if (r_refdef.draw2dstage)
902 r_refdef.draw2dstage = true;
904 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);
905 R_SetViewport(&viewport);
906 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
907 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
908 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
909 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
910 R_EntityMatrix(&identitymatrix);
914 GL_PolygonOffset(0, 0);
918 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
921 static void _DrawQ_ProcessDrawFlag(int flags)
925 if(flags == DRAWFLAG_ADDITIVE)
926 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
927 else if(flags == DRAWFLAG_MODULATE)
928 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
929 else if(flags == DRAWFLAG_2XMODULATE)
930 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
931 else if(flags == DRAWFLAG_SCREEN)
932 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
934 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
937 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
941 _DrawQ_ProcessDrawFlag(flags);
942 GL_Color(red, green, blue, alpha);
944 R_Mesh_VertexPointer(floats, 0, 0);
945 R_Mesh_ColorPointer(NULL, 0, 0);
946 R_Mesh_ResetTextureState();
952 height = pic->height;
953 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
954 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
957 floats[12] = 0.0f;floats[13] = 0.0f;
958 floats[14] = 1.0f;floats[15] = 0.0f;
959 floats[16] = 1.0f;floats[17] = 1.0f;
960 floats[18] = 0.0f;floats[19] = 1.0f;
962 // AK07: lets be texel correct on the corners
964 float horz_offset = 0.5f / pic->width;
965 float vert_offset = 0.5f / pic->height;
967 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
968 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
969 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
970 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
975 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
977 floats[2] = floats[5] = floats[8] = floats[11] = 0;
978 floats[0] = floats[9] = x;
979 floats[1] = floats[4] = y;
980 floats[3] = floats[6] = x + width;
981 floats[7] = floats[10] = y + height;
983 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
986 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)
989 float af = DEG2RAD(-angle); // forward
990 float ar = DEG2RAD(-angle + 90); // right
991 float sinaf = sin(af);
992 float cosaf = cos(af);
993 float sinar = sin(ar);
994 float cosar = cos(ar);
996 _DrawQ_ProcessDrawFlag(flags);
997 GL_Color(red, green, blue, alpha);
999 R_Mesh_VertexPointer(floats, 0, 0);
1000 R_Mesh_ColorPointer(NULL, 0, 0);
1001 R_Mesh_ResetTextureState();
1007 height = pic->height;
1008 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1009 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1011 floats[12] = 0.0f;floats[13] = 0.0f;
1012 floats[14] = 1.0f;floats[15] = 0.0f;
1013 floats[16] = 1.0f;floats[17] = 1.0f;
1014 floats[18] = 0.0f;floats[19] = 1.0f;
1017 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1019 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1022 floats[0] = x - cosaf*org_x - cosar*org_y;
1023 floats[1] = y - sinaf*org_x - sinar*org_y;
1026 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1027 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1030 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1031 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1034 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1035 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1037 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1040 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1044 _DrawQ_ProcessDrawFlag(flags);
1045 GL_Color(red, green, blue, alpha);
1047 R_Mesh_VertexPointer(floats, 0, 0);
1048 R_Mesh_ColorPointer(NULL, 0, 0);
1049 R_Mesh_ResetTextureState();
1050 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1052 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1053 floats[0] = floats[9] = x;
1054 floats[1] = floats[4] = y;
1055 floats[3] = floats[6] = x + width;
1056 floats[7] = floats[10] = y + height;
1058 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1061 /// color tag printing
1062 static const vec4_t string_colors[] =
1065 // LordHavoc: why on earth is cyan before magenta in Quake3?
1066 // LordHavoc: note: Doom3 uses white for [0] and [7]
1067 {0.0, 0.0, 0.0, 1.0}, // black
1068 {1.0, 0.0, 0.0, 1.0}, // red
1069 {0.0, 1.0, 0.0, 1.0}, // green
1070 {1.0, 1.0, 0.0, 1.0}, // yellow
1071 {0.0, 0.0, 1.0, 1.0}, // blue
1072 {0.0, 1.0, 1.0, 1.0}, // cyan
1073 {1.0, 0.0, 1.0, 1.0}, // magenta
1074 {1.0, 1.0, 1.0, 1.0}, // white
1075 // [515]'s BX_COLOREDTEXT extension
1076 {1.0, 1.0, 1.0, 0.5}, // half transparent
1077 {0.5, 0.5, 0.5, 1.0} // half brightness
1078 // Black's color table
1079 //{1.0, 1.0, 1.0, 1.0},
1080 //{1.0, 0.0, 0.0, 1.0},
1081 //{0.0, 1.0, 0.0, 1.0},
1082 //{0.0, 0.0, 1.0, 1.0},
1083 //{1.0, 1.0, 0.0, 1.0},
1084 //{0.0, 1.0, 1.0, 1.0},
1085 //{1.0, 0.0, 1.0, 1.0},
1086 //{0.1, 0.1, 0.1, 1.0}
1089 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1091 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1093 float C = r_textcontrast.value;
1094 float B = r_textbrightness.value;
1095 if (colorindex & 0x10000) // that bit means RGB color
1097 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1098 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1099 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1100 color[3] = (colorindex & 0xf) / 15.0;
1103 Vector4Copy(string_colors[colorindex], color);
1104 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1107 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1108 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1112 // NOTE: this function always draws exactly one character if maxwidth <= 0
1113 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)
1115 const char *text_start = text;
1116 int colorindex = STRING_COLOR_DEFAULT;
1119 Uchar ch, mapch, nextch;
1120 Uchar prevch = 0; // used for kerning
1125 ft2_font_map_t *fontmap = NULL;
1126 ft2_font_map_t *map = NULL;
1127 ft2_font_map_t *prevmap = NULL;
1128 ft2_font_t *ft2 = fnt->ft2;
1130 qboolean snap = true;
1131 qboolean least_one = false;
1132 float dw, dh; // display w/h
1133 const float *width_of;
1140 // do this in the end
1144 // find the most fitting size:
1148 map_index = Font_IndexForSize(ft2, h, &w, &h);
1150 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1151 fontmap = Font_MapForIndex(ft2, map_index);
1160 if (!outcolor || *outcolor == -1)
1161 colorindex = STRING_COLOR_DEFAULT;
1163 colorindex = *outcolor;
1165 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1166 // ftbase_x = snap_to_pixel_x(0);
1171 maxwidth = -maxwidth;
1175 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1178 width_of = fontmap->width_of;
1180 width_of = fnt->width_of;
1182 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1185 nextch = ch = u8_getnchar(text, &text, bytes_left);
1186 i = text - text_start;
1189 if (ch == ' ' && !fontmap)
1191 if(!least_one || i0) // never skip the first character
1192 if(x + width_of[(int) ' '] * dw > maxwidth)
1195 break; // oops, can't draw this
1197 x += width_of[(int) ' '] * dw;
1200 // i points to the char after ^
1201 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1203 ch = *text; // colors are ascii, so no u8_ needed
1204 if (ch <= '9' && ch >= '0') // ^[0-9] found
1206 colorindex = ch - '0';
1211 // i points to the char after ^...
1212 // i+3 points to 3 in ^x123
1213 // i+3 == *maxlen would mean that char is missing
1214 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1216 // building colorindex...
1217 ch = tolower(text[1]);
1218 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1219 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1220 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1221 else tempcolorindex = 0;
1224 ch = tolower(text[2]);
1225 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1226 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1227 else tempcolorindex = 0;
1230 ch = tolower(text[3]);
1231 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1232 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1233 else tempcolorindex = 0;
1236 colorindex = tempcolorindex | 0xf;
1237 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1245 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1254 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1261 map = ft2_oldstyle_map;
1263 if(!least_one || i0) // never skip the first character
1264 if(x + width_of[ch] * dw > maxwidth)
1267 break; // oops, can't draw this
1269 x += width_of[ch] * dw;
1271 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1273 map = FontMap_FindForChar(fontmap, ch);
1276 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1282 mapch = ch - map->start;
1283 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1285 x += map->glyphs[mapch].advance_x * dw;
1294 *outcolor = colorindex;
1299 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)
1301 int shadow, colorindex = STRING_COLOR_DEFAULT;
1303 float x = startx, y, s, t, u, v, thisw;
1304 float *av, *at, *ac;
1307 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1308 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1309 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1310 Uchar ch, mapch, nextch;
1311 Uchar prevch = 0; // used for kerning
1314 ft2_font_map_t *prevmap = NULL; // the previous map
1315 ft2_font_map_t *map = NULL; // the currently used map
1316 ft2_font_map_t *fontmap = NULL; // the font map for the size
1318 const char *text_start = text;
1320 ft2_font_t *ft2 = fnt->ft2;
1321 qboolean snap = true;
1325 const float *width_of;
1328 tw = R_TextureWidth(fnt->tex);
1329 th = R_TextureHeight(fnt->tex);
1337 starty -= (fnt->scale - 1) * h * 0.5; // center
1344 map_index = Font_IndexForSize(ft2, h, &w, &h);
1346 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1347 fontmap = Font_MapForIndex(ft2, map_index);
1353 // draw the font at its baseline when using freetype
1355 ftbase_y = dh * (4.5/6.0);
1360 _DrawQ_ProcessDrawFlag(flags);
1362 R_Mesh_ColorPointer(color4f, 0, 0);
1363 R_Mesh_ResetTextureState();
1365 R_Mesh_TexBind(0, fnt->tex);
1366 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1367 R_Mesh_VertexPointer(vertex3f, 0, 0);
1368 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1375 //ftbase_x = snap_to_pixel_x(ftbase_x);
1378 startx = snap_to_pixel_x(startx, 0.4);
1379 starty = snap_to_pixel_y(starty, 0.4);
1380 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1383 pix_x = vid.width / vid_conwidth.value;
1384 pix_y = vid.height / vid_conheight.value;
1387 width_of = fontmap->width_of;
1389 width_of = fnt->width_of;
1391 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1396 if (!outcolor || *outcolor == -1)
1397 colorindex = STRING_COLOR_DEFAULT;
1399 colorindex = *outcolor;
1401 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1408 x += r_textshadow.value * vid.width / vid_conwidth.value;
1409 y += r_textshadow.value * vid.height / vid_conheight.value;
1412 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1414 nextch = ch = u8_getnchar(text, &text, bytes_left);
1415 i = text - text_start;
1418 if (ch == ' ' && !fontmap)
1420 x += width_of[(int) ' '] * dw;
1423 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1425 ch = *text; // colors are ascii, so no u8_ needed
1426 if (ch <= '9' && ch >= '0') // ^[0-9] found
1428 colorindex = ch - '0';
1429 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1434 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1436 // building colorindex...
1437 ch = tolower(text[1]);
1438 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1439 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1440 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1441 else tempcolorindex = 0;
1444 ch = tolower(text[2]);
1445 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1446 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1447 else tempcolorindex = 0;
1450 ch = tolower(text[3]);
1451 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1452 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1453 else tempcolorindex = 0;
1456 colorindex = tempcolorindex | 0xf;
1457 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1458 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1459 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1467 else if (ch == STRING_COLOR_TAG)
1476 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1477 // this way we don't need to rebind fnt->tex for every old-style character
1478 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1481 x += 1.0/pix_x * r_textshadow.value;
1482 y += 1.0/pix_y * r_textshadow.value;
1484 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1492 if (map != ft2_oldstyle_map)
1496 // switching from freetype to non-freetype rendering
1497 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1503 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1504 map = ft2_oldstyle_map;
1508 //num = (unsigned char) text[i];
1509 //thisw = fnt->width_of[num];
1510 thisw = fnt->width_of[ch];
1511 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1512 s = (ch & 15)*0.0625f + (0.5f / tw);
1513 t = (ch >> 4)*0.0625f + (0.5f / th);
1514 u = 0.0625f * thisw - (1.0f / tw);
1515 v = 0.0625f - (1.0f / th);
1516 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1517 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1518 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1519 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1520 at[ 0] = s ; at[ 1] = t ;
1521 at[ 2] = s+u ; at[ 3] = t ;
1522 at[ 4] = s+u ; at[ 5] = t+v ;
1523 at[ 6] = s ; at[ 7] = t+v ;
1524 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1525 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1526 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1527 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1532 if (batchcount >= QUADELEMENTS_MAXQUADS)
1534 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1540 x += width_of[ch] * dw;
1542 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1544 // new charmap - need to render
1547 // we need a different character map, render what we currently have:
1548 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1555 map = FontMap_FindForChar(fontmap, ch);
1558 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1565 // this shouldn't happen
1570 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1573 mapch = ch - map->start;
1574 thisw = map->glyphs[mapch].advance_x;
1578 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1585 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1586 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1587 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1588 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1589 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1590 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1591 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1592 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1593 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1594 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1595 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1596 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1605 if (batchcount >= QUADELEMENTS_MAXQUADS)
1607 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1619 x -= 1.0/pix_x * r_textshadow.value;
1620 y -= 1.0/pix_y * r_textshadow.value;
1625 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1628 *outcolor = colorindex;
1630 // note: this relies on the proper text (not shadow) being drawn last
1634 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)
1636 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1639 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)
1641 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1644 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1646 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1649 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1651 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1656 // no ^xrgb management
1657 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1659 int color, numchars = 0;
1660 char *outputend2c = output2c + maxoutchars - 2;
1661 if (!outcolor || *outcolor == -1)
1662 color = STRING_COLOR_DEFAULT;
1666 maxreadchars = 1<<30;
1667 textend = text + maxreadchars;
1668 while (text != textend && *text)
1670 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1672 if (text[1] == STRING_COLOR_TAG)
1674 else if (text[1] >= '0' && text[1] <= '9')
1676 color = text[1] - '0';
1681 if (output2c >= outputend2c)
1683 *output2c++ = *text++;
1684 *output2c++ = color;
1687 output2c[0] = output2c[1] = 0;
1694 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)
1698 _DrawQ_ProcessDrawFlag(flags);
1700 R_Mesh_VertexPointer(floats, 0, 0);
1701 R_Mesh_ColorPointer(floats + 20, 0, 0);
1702 R_Mesh_ResetTextureState();
1708 height = pic->height;
1709 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1710 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1711 floats[12] = s1;floats[13] = t1;
1712 floats[14] = s2;floats[15] = t2;
1713 floats[16] = s4;floats[17] = t4;
1714 floats[18] = s3;floats[19] = t3;
1717 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1719 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1720 floats[0] = floats[9] = x;
1721 floats[1] = floats[4] = y;
1722 floats[3] = floats[6] = x + width;
1723 floats[7] = floats[10] = y + height;
1724 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1725 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1726 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1727 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1729 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1732 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1734 _DrawQ_ProcessDrawFlag(flags);
1736 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1737 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1738 R_Mesh_ResetTextureState();
1739 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1740 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1742 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1745 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1749 _DrawQ_ProcessDrawFlag(flags);
1753 qglBegin(GL_LINE_LOOP);
1754 for (num = 0;num < mesh->num_vertices;num++)
1756 if (mesh->data_color4f)
1757 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]);
1758 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1764 //[515]: this is old, delete
1765 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1767 _DrawQ_ProcessDrawFlag(flags);
1769 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1772 //qglLineWidth(width);CHECKGLERROR
1774 GL_Color(r,g,b,alpha);
1777 qglVertex2f(x1, y1);
1778 qglVertex2f(x2, y2);
1783 void DrawQ_SetClipArea(float x, float y, float width, float height)
1788 // We have to convert the con coords into real coords
1789 // OGL uses top to bottom
1790 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1791 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1792 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1793 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1794 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1796 GL_ScissorTest(true);
1799 void DrawQ_ResetClipArea(void)
1802 GL_ScissorTest(false);
1805 void DrawQ_Finish(void)
1807 r_refdef.draw2dstage = false;
1810 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1811 void R_DrawGamma(void)
1814 switch(vid.renderpath)
1816 case RENDERPATH_GL20:
1817 case RENDERPATH_CGGL:
1818 if (vid_usinghwgamma || v_glslgamma.integer)
1821 case RENDERPATH_GL13:
1822 case RENDERPATH_GL11:
1823 if (vid_usinghwgamma)
1827 // all the blends ignore depth
1828 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1829 R_Mesh_ColorPointer(NULL, 0, 0);
1830 R_Mesh_ResetTextureState();
1831 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1833 GL_DepthRange(0, 1);
1834 GL_PolygonOffset(0, 0);
1835 GL_DepthTest(false);
1836 if (v_color_enable.integer)
1838 c[0] = v_color_white_r.value;
1839 c[1] = v_color_white_g.value;
1840 c[2] = v_color_white_b.value;
1843 c[0] = c[1] = c[2] = v_contrast.value;
1844 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1846 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1847 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1849 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1850 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1851 VectorScale(c, 0.5, c);
1854 if (v_color_enable.integer)
1856 c[0] = v_color_black_r.value;
1857 c[1] = v_color_black_g.value;
1858 c[2] = v_color_black_b.value;
1861 c[0] = c[1] = c[2] = v_brightness.value;
1862 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1864 GL_BlendFunc(GL_ONE, GL_ONE);
1865 GL_Color(c[0], c[1], c[2], 1);
1866 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);