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);
941 R_Mesh_ResetTextureState();
942 floats[12] = 0.0f;floats[13] = 0.0f;
943 floats[14] = 1.0f;floats[15] = 0.0f;
944 floats[16] = 1.0f;floats[17] = 1.0f;
945 floats[18] = 0.0f;floats[19] = 1.0f;
946 floats[20] = floats[24] = floats[28] = floats[32] = red;
947 floats[21] = floats[25] = floats[29] = floats[33] = green;
948 floats[22] = floats[26] = floats[30] = floats[34] = blue;
949 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
955 height = pic->height;
956 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
959 // AK07: lets be texel correct on the corners
961 float horz_offset = 0.5f / pic->width;
962 float vert_offset = 0.5f / pic->height;
964 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
965 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
966 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
967 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
972 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
974 floats[2] = floats[5] = floats[8] = floats[11] = 0;
975 floats[0] = floats[9] = x;
976 floats[1] = floats[4] = y;
977 floats[3] = floats[6] = x + width;
978 floats[7] = floats[10] = y + height;
980 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
981 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 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);
996 R_Mesh_ResetTextureState();
1002 height = pic->height;
1003 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1006 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1008 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1011 floats[0] = x - cosaf*org_x - cosar*org_y;
1012 floats[1] = y - sinaf*org_x - sinar*org_y;
1015 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1016 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1019 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1020 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1023 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1024 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1026 floats[12] = 0.0f;floats[13] = 0.0f;
1027 floats[14] = 1.0f;floats[15] = 0.0f;
1028 floats[16] = 1.0f;floats[17] = 1.0f;
1029 floats[18] = 0.0f;floats[19] = 1.0f;
1030 floats[20] = floats[24] = floats[28] = floats[32] = red;
1031 floats[21] = floats[25] = floats[29] = floats[33] = green;
1032 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1033 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1035 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1036 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1039 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1043 _DrawQ_ProcessDrawFlag(flags);
1045 R_Mesh_ResetTextureState();
1046 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1048 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1049 floats[0] = floats[9] = x;
1050 floats[1] = floats[4] = y;
1051 floats[3] = floats[6] = x + width;
1052 floats[7] = floats[10] = y + height;
1053 floats[12] = 0.0f;floats[13] = 0.0f;
1054 floats[14] = 1.0f;floats[15] = 0.0f;
1055 floats[16] = 1.0f;floats[17] = 1.0f;
1056 floats[18] = 0.0f;floats[19] = 1.0f;
1057 floats[20] = floats[24] = floats[28] = floats[32] = red;
1058 floats[21] = floats[25] = floats[29] = floats[33] = green;
1059 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1060 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1062 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1063 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1066 /// color tag printing
1067 static const vec4_t string_colors[] =
1070 // LordHavoc: why on earth is cyan before magenta in Quake3?
1071 // LordHavoc: note: Doom3 uses white for [0] and [7]
1072 {0.0, 0.0, 0.0, 1.0}, // black
1073 {1.0, 0.0, 0.0, 1.0}, // red
1074 {0.0, 1.0, 0.0, 1.0}, // green
1075 {1.0, 1.0, 0.0, 1.0}, // yellow
1076 {0.0, 0.0, 1.0, 1.0}, // blue
1077 {0.0, 1.0, 1.0, 1.0}, // cyan
1078 {1.0, 0.0, 1.0, 1.0}, // magenta
1079 {1.0, 1.0, 1.0, 1.0}, // white
1080 // [515]'s BX_COLOREDTEXT extension
1081 {1.0, 1.0, 1.0, 0.5}, // half transparent
1082 {0.5, 0.5, 0.5, 1.0} // half brightness
1083 // Black's color table
1084 //{1.0, 1.0, 1.0, 1.0},
1085 //{1.0, 0.0, 0.0, 1.0},
1086 //{0.0, 1.0, 0.0, 1.0},
1087 //{0.0, 0.0, 1.0, 1.0},
1088 //{1.0, 1.0, 0.0, 1.0},
1089 //{0.0, 1.0, 1.0, 1.0},
1090 //{1.0, 0.0, 1.0, 1.0},
1091 //{0.1, 0.1, 0.1, 1.0}
1094 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1096 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1098 float C = r_textcontrast.value;
1099 float B = r_textbrightness.value;
1100 if (colorindex & 0x10000) // that bit means RGB color
1102 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1103 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1104 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1105 color[3] = (colorindex & 0xf) / 15.0;
1108 Vector4Copy(string_colors[colorindex], color);
1109 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1112 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1113 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1117 // NOTE: this function always draws exactly one character if maxwidth <= 0
1118 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)
1120 const char *text_start = text;
1121 int colorindex = STRING_COLOR_DEFAULT;
1124 Uchar ch, mapch, nextch;
1125 Uchar prevch = 0; // used for kerning
1130 ft2_font_map_t *fontmap = NULL;
1131 ft2_font_map_t *map = NULL;
1132 //ft2_font_map_t *prevmap = NULL;
1133 ft2_font_t *ft2 = fnt->ft2;
1135 qboolean snap = true;
1136 qboolean least_one = false;
1137 float dw; // display w
1138 //float dh; // display h
1139 const float *width_of;
1146 // do this in the end
1150 // find the most fitting size:
1154 map_index = Font_IndexForSize(ft2, h, &w, &h);
1156 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1157 fontmap = Font_MapForIndex(ft2, map_index);
1166 if (!outcolor || *outcolor == -1)
1167 colorindex = STRING_COLOR_DEFAULT;
1169 colorindex = *outcolor;
1171 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1172 // ftbase_x = snap_to_pixel_x(0);
1177 maxwidth = -maxwidth;
1181 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1184 width_of = fontmap->width_of;
1186 width_of = fnt->width_of;
1188 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1191 nextch = ch = u8_getnchar(text, &text, bytes_left);
1192 i = text - text_start;
1195 if (ch == ' ' && !fontmap)
1197 if(!least_one || i0) // never skip the first character
1198 if(x + width_of[(int) ' '] * dw > maxwidth)
1201 break; // oops, can't draw this
1203 x += width_of[(int) ' '] * dw;
1206 // i points to the char after ^
1207 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1209 ch = *text; // colors are ascii, so no u8_ needed
1210 if (ch <= '9' && ch >= '0') // ^[0-9] found
1212 colorindex = ch - '0';
1217 // i points to the char after ^...
1218 // i+3 points to 3 in ^x123
1219 // i+3 == *maxlen would mean that char is missing
1220 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1222 // building colorindex...
1223 ch = tolower(text[1]);
1224 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1225 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1226 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1227 else tempcolorindex = 0;
1230 ch = tolower(text[2]);
1231 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1232 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1233 else tempcolorindex = 0;
1236 ch = tolower(text[3]);
1237 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1238 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1239 else tempcolorindex = 0;
1242 colorindex = tempcolorindex | 0xf;
1243 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1251 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1260 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1267 map = ft2_oldstyle_map;
1269 if(!least_one || i0) // never skip the first character
1270 if(x + width_of[ch] * dw > maxwidth)
1273 break; // oops, can't draw this
1275 x += width_of[ch] * dw;
1277 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1279 map = FontMap_FindForChar(fontmap, ch);
1282 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1288 mapch = ch - map->start;
1289 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1291 x += map->glyphs[mapch].advance_x * dw;
1300 *outcolor = colorindex;
1305 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)
1307 int shadow, colorindex = STRING_COLOR_DEFAULT;
1309 float x = startx, y, s, t, u, v, thisw;
1310 float *av, *at, *ac;
1313 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1314 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1315 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1316 Uchar ch, mapch, nextch;
1317 Uchar prevch = 0; // used for kerning
1320 //ft2_font_map_t *prevmap = NULL; // the previous map
1321 ft2_font_map_t *map = NULL; // the currently used map
1322 ft2_font_map_t *fontmap = NULL; // the font map for the size
1324 const char *text_start = text;
1326 ft2_font_t *ft2 = fnt->ft2;
1327 qboolean snap = true;
1331 const float *width_of;
1334 tw = R_TextureWidth(fnt->tex);
1335 th = R_TextureHeight(fnt->tex);
1343 starty -= (fnt->scale - 1) * h * 0.5; // center
1350 map_index = Font_IndexForSize(ft2, h, &w, &h);
1352 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1353 fontmap = Font_MapForIndex(ft2, map_index);
1359 // draw the font at its baseline when using freetype
1361 ftbase_y = dh * (4.5/6.0);
1366 _DrawQ_ProcessDrawFlag(flags);
1368 R_Mesh_ResetTextureState();
1370 R_Mesh_TexBind(0, fnt->tex);
1371 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1378 //ftbase_x = snap_to_pixel_x(ftbase_x);
1381 startx = snap_to_pixel_x(startx, 0.4);
1382 starty = snap_to_pixel_y(starty, 0.4);
1383 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1386 pix_x = vid.width / vid_conwidth.value;
1387 pix_y = vid.height / vid_conheight.value;
1390 width_of = fontmap->width_of;
1392 width_of = fnt->width_of;
1394 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1399 if (!outcolor || *outcolor == -1)
1400 colorindex = STRING_COLOR_DEFAULT;
1402 colorindex = *outcolor;
1404 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1411 x += r_textshadow.value * vid.width / vid_conwidth.value;
1412 y += r_textshadow.value * vid.height / vid_conheight.value;
1415 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1417 nextch = ch = u8_getnchar(text, &text, bytes_left);
1418 i = text - text_start;
1421 if (ch == ' ' && !fontmap)
1423 x += width_of[(int) ' '] * dw;
1426 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1428 ch = *text; // colors are ascii, so no u8_ needed
1429 if (ch <= '9' && ch >= '0') // ^[0-9] found
1431 colorindex = ch - '0';
1432 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1437 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1439 // building colorindex...
1440 ch = tolower(text[1]);
1441 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1442 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1443 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1444 else tempcolorindex = 0;
1447 ch = tolower(text[2]);
1448 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1449 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1450 else tempcolorindex = 0;
1453 ch = tolower(text[3]);
1454 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1455 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1456 else tempcolorindex = 0;
1459 colorindex = tempcolorindex | 0xf;
1460 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1461 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1462 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1470 else if (ch == STRING_COLOR_TAG)
1479 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1480 // this way we don't need to rebind fnt->tex for every old-style character
1481 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1484 x += 1.0/pix_x * r_textshadow.value;
1485 y += 1.0/pix_y * r_textshadow.value;
1487 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1495 if (map != ft2_oldstyle_map)
1499 // switching from freetype to non-freetype rendering
1500 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1501 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1507 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1508 map = ft2_oldstyle_map;
1512 //num = (unsigned char) text[i];
1513 //thisw = fnt->width_of[num];
1514 thisw = fnt->width_of[ch];
1515 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1516 s = (ch & 15)*0.0625f + (0.5f / tw);
1517 t = (ch >> 4)*0.0625f + (0.5f / th);
1518 u = 0.0625f * thisw - (1.0f / tw);
1519 v = 0.0625f - (1.0f / th);
1520 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1521 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1522 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1523 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1524 at[ 0] = s ; at[ 1] = t ;
1525 at[ 2] = s+u ; at[ 3] = t ;
1526 at[ 4] = s+u ; at[ 5] = t+v ;
1527 at[ 6] = s ; at[ 7] = t+v ;
1528 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1529 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1530 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1531 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1536 if (batchcount >= QUADELEMENTS_MAXQUADS)
1538 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1539 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1545 x += width_of[ch] * dw;
1547 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1549 // new charmap - need to render
1552 // we need a different character map, render what we currently have:
1553 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1554 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1561 map = FontMap_FindForChar(fontmap, ch);
1564 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1571 // this shouldn't happen
1576 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1579 mapch = ch - map->start;
1580 thisw = map->glyphs[mapch].advance_x;
1584 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1591 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1592 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1593 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1594 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1595 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1596 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1597 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1598 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1599 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1600 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1601 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1602 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1611 if (batchcount >= QUADELEMENTS_MAXQUADS)
1613 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1614 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1626 x -= 1.0/pix_x * r_textshadow.value;
1627 y -= 1.0/pix_y * r_textshadow.value;
1633 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1634 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1638 *outcolor = colorindex;
1640 // note: this relies on the proper text (not shadow) being drawn last
1644 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)
1646 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1649 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)
1651 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1654 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1656 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1659 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1661 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1666 // no ^xrgb management
1667 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1669 int color, numchars = 0;
1670 char *outputend2c = output2c + maxoutchars - 2;
1671 if (!outcolor || *outcolor == -1)
1672 color = STRING_COLOR_DEFAULT;
1676 maxreadchars = 1<<30;
1677 textend = text + maxreadchars;
1678 while (text != textend && *text)
1680 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1682 if (text[1] == STRING_COLOR_TAG)
1684 else if (text[1] >= '0' && text[1] <= '9')
1686 color = text[1] - '0';
1691 if (output2c >= outputend2c)
1693 *output2c++ = *text++;
1694 *output2c++ = color;
1697 output2c[0] = output2c[1] = 0;
1704 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)
1708 _DrawQ_ProcessDrawFlag(flags);
1710 R_Mesh_ResetTextureState();
1716 height = pic->height;
1717 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1720 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1722 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1723 floats[0] = floats[9] = x;
1724 floats[1] = floats[4] = y;
1725 floats[3] = floats[6] = x + width;
1726 floats[7] = floats[10] = y + height;
1727 floats[12] = s1;floats[13] = t1;
1728 floats[14] = s2;floats[15] = t2;
1729 floats[16] = s4;floats[17] = t4;
1730 floats[18] = s3;floats[19] = t3;
1731 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1732 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1733 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1734 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1736 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1737 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1740 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1742 _DrawQ_ProcessDrawFlag(flags);
1744 R_Mesh_ResetTextureState();
1745 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1747 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1748 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1751 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1755 _DrawQ_ProcessDrawFlag(flags);
1759 qglBegin(GL_LINE_LOOP);
1760 for (num = 0;num < mesh->num_vertices;num++)
1762 if (mesh->data_color4f)
1763 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]);
1764 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1770 //[515]: this is old, delete
1771 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1773 _DrawQ_ProcessDrawFlag(flags);
1775 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1778 //qglLineWidth(width);CHECKGLERROR
1780 GL_Color(r,g,b,alpha);
1783 qglVertex2f(x1, y1);
1784 qglVertex2f(x2, y2);
1789 void DrawQ_SetClipArea(float x, float y, float width, float height)
1794 // We have to convert the con coords into real coords
1795 // OGL uses top to bottom
1796 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1797 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1798 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1799 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1800 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1802 GL_ScissorTest(true);
1805 void DrawQ_ResetClipArea(void)
1808 GL_ScissorTest(false);
1811 void DrawQ_Finish(void)
1813 r_refdef.draw2dstage = false;
1816 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1817 void R_DrawGamma(void)
1820 switch(vid.renderpath)
1822 case RENDERPATH_GL20:
1823 case RENDERPATH_CGGL:
1824 if (vid_usinghwgamma || v_glslgamma.integer)
1827 case RENDERPATH_GL13:
1828 case RENDERPATH_GL11:
1829 if (vid_usinghwgamma)
1833 // all the blends ignore depth
1834 R_Mesh_ResetTextureState();
1835 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1837 GL_DepthRange(0, 1);
1838 GL_PolygonOffset(0, 0);
1839 GL_DepthTest(false);
1840 if (v_color_enable.integer)
1842 c[0] = v_color_white_r.value;
1843 c[1] = v_color_white_g.value;
1844 c[2] = v_color_white_b.value;
1847 c[0] = c[1] = c[2] = v_contrast.value;
1848 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1850 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1851 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1853 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1854 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1855 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1856 VectorScale(c, 0.5, c);
1859 if (v_color_enable.integer)
1861 c[0] = v_color_black_r.value;
1862 c[1] = v_color_black_g.value;
1863 c[2] = v_color_black_b.value;
1866 c[0] = c[1] = c[2] = v_brightness.value;
1867 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1869 GL_BlendFunc(GL_ONE, GL_ONE);
1870 GL_Color(c[0] - 1, c[1] - 1, c[2] - 1, 1);
1871 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
1872 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);