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 extern cvar_t v_glslgamma;
39 //=============================================================================
40 /* Support Routines */
42 #define FONT_FILESIZE 13468
43 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
44 static cachepic_t cachepics[MAX_CACHED_PICS];
45 static int numcachepics;
47 static rtexturepool_t *drawtexturepool;
49 static const unsigned char concharimage[FONT_FILESIZE] =
54 static rtexture_t *draw_generateconchars(void)
61 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
63 for (i = 0;i < 8192;i++)
65 random = lhrandom (0.0,1.0);
66 data[i*4+3] = data[i*4+0];
67 data[i*4+2] = 83 + (unsigned char)(random * 64);
68 data[i*4+1] = 71 + (unsigned char)(random * 32);
69 data[i*4+0] = 23 + (unsigned char)(random * 16);
72 for (i = 8192;i < 32768;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 95 + (unsigned char)(random * 64);
77 data[i*4+1] = 95 + (unsigned char)(random * 64);
78 data[i*4+0] = 95 + (unsigned char)(random * 64);
81 for (i = 32768;i < 40960;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 83 + (unsigned char)(random * 64);
86 data[i*4+1] = 71 + (unsigned char)(random * 32);
87 data[i*4+0] = 23 + (unsigned char)(random * 16);
90 for (i = 40960;i < 65536;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 96 + (unsigned char)(random * 64);
95 data[i*4+1] = 43 + (unsigned char)(random * 32);
96 data[i*4+0] = 27 + (unsigned char)(random * 32);
100 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
103 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, NULL);
108 static rtexture_t *draw_generateditherpattern(void)
111 unsigned char pixels[8][8];
112 for (y = 0;y < 8;y++)
113 for (x = 0;x < 8;x++)
114 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
115 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, palette_bgra_transparent);
118 typedef struct embeddedpic_s
127 static const embeddedpic_t embeddedpics[] =
130 "gfx/prydoncursor001", 16, 16,
149 "ui/mousepointer", 16, 16,
168 "gfx/crosshair1", 16, 16,
187 "gfx/crosshair2", 16, 16,
206 "gfx/crosshair3", 16, 16,
225 "gfx/crosshair4", 16, 16,
244 "gfx/crosshair5", 8, 8,
255 "gfx/crosshair6", 2, 2,
260 "gfx/crosshair7", 16, 16,
281 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
283 const embeddedpic_t *p;
284 for (p = embeddedpics;p->name;p++)
285 if (!strcmp(name, p->name))
286 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, palette_bgra_embeddedpic);
287 if (!strcmp(name, "gfx/conchars"))
288 return draw_generateconchars();
289 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
290 return draw_generateditherpattern();
292 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
293 return r_texture_notexture;
302 // FIXME: move this to client somehow
303 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
306 unsigned char *pixels;
309 unsigned char *lmpdata;
310 char lmpname[MAX_QPATH];
312 // check whether the picture has already been cached
313 crc = CRC_Block((unsigned char *)path, strlen(path));
314 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
315 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
316 if (!strcmp (path, pic->name))
319 if (numcachepics == MAX_CACHED_PICS)
321 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
322 // FIXME: support NULL in callers?
323 return cachepics; // return the first one
325 pic = cachepics + (numcachepics++);
326 strlcpy (pic->name, path, sizeof(pic->name));
328 pic->chain = cachepichash[hashkey];
329 cachepichash[hashkey] = pic;
331 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
332 pic->tex = CL_GetDynTexture( path );
333 // if so, set the width/height, too
335 pic->width = R_TextureWidth(pic->tex);
336 pic->height = R_TextureHeight(pic->tex);
337 // we're done now (early-out)
341 pic->texflags = TEXF_ALPHA;
342 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
343 pic->texflags |= TEXF_CLAMP;
344 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
345 pic->texflags |= TEXF_COMPRESS;
347 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
349 // load a high quality image from disk if possible
350 pixels = loadimagepixelsbgra(path, false, true);
351 if (pixels == NULL && !strncmp(path, "gfx/", 4))
352 pixels = loadimagepixelsbgra(path+4, false, true);
355 pic->width = image_width;
356 pic->height = image_height;
358 pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, NULL);
362 pic->autoload = false;
363 // never compress the fallback images
364 pic->texflags &= ~TEXF_COMPRESS;
367 // now read the low quality version (wad or lmp file), and take the pic
368 // size from that even if we don't upload the texture, this way the pics
369 // show up the right size in the menu even if they were replaced with
370 // higher or lower resolution versions
371 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
372 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
374 if (developer_loading.integer)
375 Con_Printf("loading lump \"%s\"\n", path);
379 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
380 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
381 // if no high quality replacement image was found, upload the original low quality texture
383 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
387 else if ((lmpdata = W_GetLumpName (path + 4)))
389 if (developer_loading.integer)
390 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
392 if (!strcmp(path, "gfx/conchars"))
394 // conchars is a raw image and with color 0 as transparent instead of 255
397 // if no high quality replacement image was found, upload the original low quality texture
399 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, palette_bgra_font);
403 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
404 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
405 // if no high quality replacement image was found, upload the original low quality texture
407 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent);
416 else if (pic->tex == NULL)
418 // if it's not found on disk, generate an image
419 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
420 pic->width = R_TextureWidth(pic->tex);
421 pic->height = R_TextureHeight(pic->tex);
427 cachepic_t *Draw_CachePic (const char *path)
429 return Draw_CachePic_Flags (path, 0);
434 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
436 if (pic->autoload && !pic->tex)
438 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true);
439 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
440 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true);
441 if (pic->tex == NULL)
442 pic->tex = draw_generatepic(pic->name, true);
444 pic->lastusedframe = draw_frame;
448 void Draw_Frame(void)
452 static double nextpurgetime;
454 if (nextpurgetime > realtime)
456 nextpurgetime = realtime + 0.05;
457 purgeframe = draw_frame - 1;
458 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
460 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
462 R_FreeTexture(pic->tex);
469 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
474 crc = CRC_Block((unsigned char *)picname, strlen(picname));
475 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
476 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
477 if (!strcmp (picname, pic->name))
482 if (pic->tex && pic->width == width && pic->height == height)
484 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
492 if (numcachepics == MAX_CACHED_PICS)
494 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
495 // FIXME: support NULL in callers?
496 return cachepics; // return the first one
498 pic = cachepics + (numcachepics++);
499 strlcpy (pic->name, picname, sizeof(pic->name));
501 pic->chain = cachepichash[hashkey];
502 cachepichash[hashkey] = pic;
507 pic->height = height;
509 R_FreeTexture(pic->tex);
510 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL);
514 void Draw_FreePic(const char *picname)
519 // this doesn't really free the pic, but does free it's texture
520 crc = CRC_Block((unsigned char *)picname, strlen(picname));
521 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
522 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
524 if (!strcmp (picname, pic->name) && pic->tex)
526 R_FreeTexture(pic->tex);
535 extern int con_linewidth; // to force rewrapping
536 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
539 float maxwidth, scale;
540 char widthfile[MAX_QPATH];
542 fs_offset_t widthbufsize;
544 if(override || !fnt->texpath[0])
545 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
547 if(drawtexturepool == NULL)
548 return; // before gl_draw_start, so will be loaded later
552 // clear freetype font
553 Font_UnloadFont(fnt->ft2);
558 if(fnt->req_face != -1)
560 if(!Font_LoadFont(fnt->texpath, fnt))
561 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
564 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
565 if(fnt->tex == r_texture_notexture)
567 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
568 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
571 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
573 // unspecified width == 1 (base width)
574 for(i = 1; i < 256; ++i)
575 fnt->width_of[i] = 1;
578 // FIXME load "name.width", if it fails, fill all with 1
579 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
581 float extraspacing = 0;
582 const char *p = widthbuf;
587 if(!COM_ParseToken_Simple(&p, false, false))
605 fnt->width_of[ch++] = atof(com_token) + extraspacing;
608 if(!strcmp(com_token, "extraspacing"))
610 if(!COM_ParseToken_Simple(&p, false, false))
612 extraspacing = atof(com_token);
614 else if(!strcmp(com_token, "scale"))
616 if(!COM_ParseToken_Simple(&p, false, false))
618 scale = atof(com_token);
622 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
623 if(!COM_ParseToken_Simple(&p, false, false))
633 maxwidth = fnt->width_of[1];
634 for(i = 2; i < 256; ++i)
635 maxwidth = max(maxwidth, fnt->width_of[i]);
636 fnt->maxwidth = maxwidth;
638 // fix up maxwidth for overlap
639 fnt->maxwidth *= scale;
642 if(fnt == FONT_CONSOLE)
643 con_linewidth = -1; // rewrap console in next frame
646 static dp_font_t *FindFont(const char *title)
649 for(i = 0; i < MAX_FONTS; ++i)
650 if(!strcmp(dp_fonts[i].title, title))
655 static float snap_to_pixel_x(float x, float roundUpAt)
657 float pixelpos = x * vid.width / vid_conwidth.value;
658 int snap = (int) pixelpos;
659 if (pixelpos - snap >= roundUpAt) ++snap;
660 return ((float)snap * vid_conwidth.value / vid.width);
662 x = (int)(x * vid.width / vid_conwidth.value);
663 x = (x * vid_conwidth.value / vid.width);
668 static float snap_to_pixel_y(float y, float roundUpAt)
670 float pixelpos = y * vid.height / vid_conheight.value;
671 int snap = (int) pixelpos;
672 if (pixelpos - snap > roundUpAt) ++snap;
673 return ((float)snap * vid_conheight.value / vid.height);
675 y = (int)(y * vid.height / vid_conheight.value);
676 y = (y * vid_conheight.value / vid.height);
681 static void LoadFont_f(void)
685 const char *filelist, *c, *cm;
687 char mainfont[MAX_QPATH];
691 Con_Printf("Available font commands:\n");
692 for(i = 0; i < MAX_FONTS; ++i)
693 Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts[i].title);
694 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
695 "can specify multiple fonts and faces\n"
696 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
697 "to load face 2 of the font gfx/vera-sans and use face 1\n"
698 "of gfx/fallback as fallback font.\n"
699 "You can also specify a list of font sizes to load, like this:\n"
700 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
701 "In many cases, 8 12 16 24 32 should be a good choice.\n"
705 f = FindFont(Cmd_Argv(1));
708 Con_Printf("font function not found\n");
713 filelist = "gfx/conchars";
715 filelist = Cmd_Argv(2);
717 memset(f->fallbacks, 0, sizeof(f->fallbacks));
718 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
720 // first font is handled "normally"
721 c = strchr(filelist, ':');
722 cm = strchr(filelist, ',');
723 if(c && (!cm || c < cm))
724 f->req_face = atoi(c+1);
731 if(!c || (c - filelist) > MAX_QPATH)
732 strlcpy(mainfont, filelist, sizeof(mainfont));
735 memcpy(mainfont, filelist, c - filelist);
736 mainfont[c - filelist] = 0;
739 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
741 c = strchr(filelist, ',');
747 c = strchr(filelist, ':');
748 cm = strchr(filelist, ',');
749 if(c && (!cm || c < cm))
750 f->fallback_faces[i] = atoi(c+1);
753 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
756 if(!c || (c-filelist) > MAX_QPATH)
758 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
762 memcpy(f->fallbacks[i], filelist, c - filelist);
763 f->fallbacks[i][c - filelist] = 0;
767 // for now: by default load only one size: the default size
769 for(i = 1; i < MAX_FONT_SIZES; ++i)
770 f->req_sizes[i] = -1;
772 // for some reason this argc is 3 even when using 2 arguments here, maybe nexuiz screws up
775 for(i = 0; i < Cmd_Argc()-3; ++i)
777 sz = atof(Cmd_Argv(i+3));
778 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
779 f->req_sizes[i] = sz;
782 LoadFont(true, mainfont, f);
790 static void gl_draw_start(void)
793 drawtexturepool = R_AllocTexturePool();
796 memset(cachepichash, 0, sizeof(cachepichash));
800 for(i = 0; i < MAX_FONTS; ++i)
801 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
803 // draw the loading screen so people have something to see in the newly opened window
804 SCR_UpdateLoadingScreen(true);
807 static void gl_draw_shutdown(void)
811 R_FreeTexturePool(&drawtexturepool);
814 memset(cachepichash, 0, sizeof(cachepichash));
817 static void gl_draw_newmap(void)
822 void GL_Draw_Init (void)
825 Cvar_RegisterVariable(&r_textshadow);
826 Cvar_RegisterVariable(&r_textbrightness);
827 Cvar_RegisterVariable(&r_textcontrast);
828 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
829 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
831 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
832 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
833 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
834 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
835 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
836 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
837 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
838 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
839 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
840 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
841 if(!FONT_USER[i].title[0])
842 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
846 void _DrawQ_Setup(void)
848 r_viewport_t viewport;
849 if (r_refdef.draw2dstage)
851 r_refdef.draw2dstage = true;
853 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);
854 R_SetViewport(&viewport);
855 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
856 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
857 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
858 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
859 R_EntityMatrix(&identitymatrix);
863 GL_PolygonOffset(0, 0);
867 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
870 static void _DrawQ_ProcessDrawFlag(int flags)
874 if(flags == DRAWFLAG_ADDITIVE)
875 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
876 else if(flags == DRAWFLAG_MODULATE)
877 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
878 else if(flags == DRAWFLAG_2XMODULATE)
879 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
880 else if(flags == DRAWFLAG_SCREEN)
881 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
883 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
886 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
890 _DrawQ_ProcessDrawFlag(flags);
891 GL_Color(red, green, blue, alpha);
893 R_Mesh_VertexPointer(floats, 0, 0);
894 R_Mesh_ColorPointer(NULL, 0, 0);
895 R_Mesh_ResetTextureState();
901 height = pic->height;
902 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
903 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
906 floats[12] = 0.0f;floats[13] = 0.0f;
907 floats[14] = 1.0f;floats[15] = 0.0f;
908 floats[16] = 1.0f;floats[17] = 1.0f;
909 floats[18] = 0.0f;floats[19] = 1.0f;
911 // AK07: lets be texel correct on the corners
913 float horz_offset = 0.5f / pic->width;
914 float vert_offset = 0.5f / pic->height;
916 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
917 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
918 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
919 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
924 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
926 floats[2] = floats[5] = floats[8] = floats[11] = 0;
927 floats[0] = floats[9] = x;
928 floats[1] = floats[4] = y;
929 floats[3] = floats[6] = x + width;
930 floats[7] = floats[10] = y + height;
932 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
935 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)
938 float af = DEG2RAD(-angle); // forward
939 float ar = DEG2RAD(-angle + 90); // right
940 float sinaf = sin(af);
941 float cosaf = cos(af);
942 float sinar = sin(ar);
943 float cosar = cos(ar);
945 _DrawQ_ProcessDrawFlag(flags);
946 GL_Color(red, green, blue, alpha);
948 R_Mesh_VertexPointer(floats, 0, 0);
949 R_Mesh_ColorPointer(NULL, 0, 0);
950 R_Mesh_ResetTextureState();
956 height = pic->height;
957 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
958 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
960 floats[12] = 0.0f;floats[13] = 0.0f;
961 floats[14] = 1.0f;floats[15] = 0.0f;
962 floats[16] = 1.0f;floats[17] = 1.0f;
963 floats[18] = 0.0f;floats[19] = 1.0f;
966 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
968 floats[2] = floats[5] = floats[8] = floats[11] = 0;
971 floats[0] = x - cosaf*org_x - cosar*org_y;
972 floats[1] = y - sinaf*org_x - sinar*org_y;
975 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
976 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
979 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
980 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
983 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
984 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
986 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
989 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
993 _DrawQ_ProcessDrawFlag(flags);
994 GL_Color(red, green, blue, alpha);
996 R_Mesh_VertexPointer(floats, 0, 0);
997 R_Mesh_ColorPointer(NULL, 0, 0);
998 R_Mesh_ResetTextureState();
999 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1001 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1002 floats[0] = floats[9] = x;
1003 floats[1] = floats[4] = y;
1004 floats[3] = floats[6] = x + width;
1005 floats[7] = floats[10] = y + height;
1007 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1010 /// color tag printing
1011 static const vec4_t string_colors[] =
1014 // LordHavoc: why on earth is cyan before magenta in Quake3?
1015 // LordHavoc: note: Doom3 uses white for [0] and [7]
1016 {0.0, 0.0, 0.0, 1.0}, // black
1017 {1.0, 0.0, 0.0, 1.0}, // red
1018 {0.0, 1.0, 0.0, 1.0}, // green
1019 {1.0, 1.0, 0.0, 1.0}, // yellow
1020 {0.0, 0.0, 1.0, 1.0}, // blue
1021 {0.0, 1.0, 1.0, 1.0}, // cyan
1022 {1.0, 0.0, 1.0, 1.0}, // magenta
1023 {1.0, 1.0, 1.0, 1.0}, // white
1024 // [515]'s BX_COLOREDTEXT extension
1025 {1.0, 1.0, 1.0, 0.5}, // half transparent
1026 {0.5, 0.5, 0.5, 1.0} // half brightness
1027 // Black's color table
1028 //{1.0, 1.0, 1.0, 1.0},
1029 //{1.0, 0.0, 0.0, 1.0},
1030 //{0.0, 1.0, 0.0, 1.0},
1031 //{0.0, 0.0, 1.0, 1.0},
1032 //{1.0, 1.0, 0.0, 1.0},
1033 //{0.0, 1.0, 1.0, 1.0},
1034 //{1.0, 0.0, 1.0, 1.0},
1035 //{0.1, 0.1, 0.1, 1.0}
1038 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1040 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1042 float C = r_textcontrast.value;
1043 float B = r_textbrightness.value;
1044 if (colorindex & 0x10000) // that bit means RGB color
1046 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1047 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1048 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1049 color[3] = (colorindex & 0xf) / 15.0;
1052 Vector4Copy(string_colors[colorindex], color);
1053 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1056 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1057 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1061 // NOTE: this function always draws exactly one character if maxwidth <= 0
1062 float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size_Scale(const char *text, float w, float h, float sw, float sh, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1064 const char *text_start = text;
1065 int colorindex = STRING_COLOR_DEFAULT;
1068 Uchar ch, mapch, nextch;
1069 Uchar prevch = 0; // used for kerning
1074 ft2_font_map_t *fontmap = NULL;
1075 ft2_font_map_t *map = NULL;
1076 ft2_font_map_t *prevmap = NULL;
1077 ft2_font_t *ft2 = fnt->ft2;
1079 qboolean snap = true;
1080 qboolean least_one = false;
1081 float dw, dh; // display w/h
1088 // do this in the end
1092 // find the most fitting size:
1096 map_index = Font_IndexForSize(ft2, h, &w, &h);
1098 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1099 fontmap = Font_MapForIndex(ft2, map_index);
1103 if(fabs(sw - 1) > 0.001 || fabs(sh - 1) > 0.001)
1104 snap = false; // turn off pixel snapping for better animation
1115 if (!outcolor || *outcolor == -1)
1116 colorindex = STRING_COLOR_DEFAULT;
1118 colorindex = *outcolor;
1120 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1121 // ftbase_x = snap_to_pixel_x(0);
1126 maxwidth = -maxwidth;
1129 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1132 nextch = ch = u8_getnchar(text, &text, bytes_left);
1133 i = text - text_start;
1137 x = snap_to_pixel_x(x, 0.4);
1138 if (ch == ' ' && !fontmap)
1140 if(!least_one || i0) // never skip the first character
1141 if(x + fnt->width_of[(int) ' '] * dw > maxwidth)
1144 break; // oops, can't draw this
1146 x += fnt->width_of[(int) ' '] * dw;
1149 // i points to the char after ^
1150 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1152 ch = *text; // colors are ascii, so no u8_ needed
1153 if (ch <= '9' && ch >= '0') // ^[0-9] found
1155 colorindex = ch - '0';
1160 // i points to the char after ^...
1161 // i+3 points to 3 in ^x123
1162 // i+3 == *maxlen would mean that char is missing
1163 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1165 // building colorindex...
1166 ch = tolower(text[1]);
1167 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1168 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1169 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1170 else tempcolorindex = 0;
1173 ch = tolower(text[2]);
1174 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1175 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1176 else tempcolorindex = 0;
1179 ch = tolower(text[3]);
1180 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1181 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1182 else tempcolorindex = 0;
1185 colorindex = tempcolorindex | 0xf;
1186 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1194 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1203 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1210 map = ft2_oldstyle_map;
1212 if(!least_one || i0) // never skip the first character
1213 if(x + fnt->width_of[ch] * dw > maxwidth)
1216 break; // oops, can't draw this
1218 x += fnt->width_of[ch] * dw;
1220 if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch)
1222 map = FontMap_FindForChar(fontmap, ch);
1225 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1231 mapch = ch - map->start;
1232 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1234 x += map->glyphs[mapch].advance_x * dw;
1243 *outcolor = colorindex;
1248 float DrawQ_String_Font_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)
1250 int shadow, colorindex = STRING_COLOR_DEFAULT;
1252 float x = startx, y, s, t, u, v, thisw;
1253 float *av, *at, *ac;
1256 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1257 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1258 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1259 Uchar ch, mapch, nextch;
1260 Uchar prevch = 0; // used for kerning
1263 ft2_font_map_t *prevmap = NULL; // the previous map
1264 ft2_font_map_t *map = NULL; // the currently used map
1265 ft2_font_map_t *fontmap = NULL; // the font map for the size
1267 const char *text_start = text;
1269 ft2_font_t *ft2 = fnt->ft2;
1270 qboolean snap = true;
1276 tw = R_TextureWidth(fnt->tex);
1277 th = R_TextureHeight(fnt->tex);
1285 starty -= (fnt->scale - 1) * h * 0.5; // center
1292 map_index = Font_IndexForSize(ft2, h, &w, &h);
1294 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1295 fontmap = Font_MapForIndex(ft2, map_index);
1299 if(fabs(sw - 1) > 0.001 || fabs(sh - 1) > 0.001)
1300 snap = false; // turn off pixel snapping for better animation
1308 // draw the font at its baseline when using freetype
1310 ftbase_y = dh * (4.5/6.0);
1315 _DrawQ_ProcessDrawFlag(flags);
1317 R_Mesh_ColorPointer(color4f, 0, 0);
1318 R_Mesh_ResetTextureState();
1320 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
1321 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1322 R_Mesh_VertexPointer(vertex3f, 0, 0);
1323 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1330 //ftbase_x = snap_to_pixel_x(ftbase_x);
1332 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1334 pix_x = vid.width / vid_conwidth.value;
1335 pix_y = vid.height / vid_conheight.value;
1336 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1340 if (!outcolor || *outcolor == -1)
1341 colorindex = STRING_COLOR_DEFAULT;
1343 colorindex = *outcolor;
1345 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1352 x += r_textshadow.value * vid.width / vid_conwidth.value;
1353 y += r_textshadow.value * vid.height / vid_conheight.value;
1356 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1358 nextch = ch = u8_getnchar(text, &text, bytes_left);
1359 i = text - text_start;
1364 x = snap_to_pixel_x(x, 0.4);
1365 y = snap_to_pixel_y(y, 0.4);
1367 if (ch == ' ' && !fontmap)
1369 x += fnt->width_of[(int) ' '] * dw;
1372 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1374 ch = *text; // colors are ascii, so no u8_ needed
1375 if (ch <= '9' && ch >= '0') // ^[0-9] found
1377 colorindex = ch - '0';
1378 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1383 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1385 // building colorindex...
1386 ch = tolower(text[1]);
1387 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1388 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1389 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1390 else tempcolorindex = 0;
1393 ch = tolower(text[2]);
1394 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1395 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1396 else tempcolorindex = 0;
1399 ch = tolower(text[3]);
1400 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1401 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1402 else tempcolorindex = 0;
1405 colorindex = tempcolorindex | 0xf;
1406 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1407 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1408 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1416 else if (ch == STRING_COLOR_TAG)
1425 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1426 // this way we don't need to rebind fnt->tex for every old-style character
1427 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1430 x += 1.0/pix_x * r_textshadow.value;
1431 y += 1.0/pix_y * r_textshadow.value;
1433 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1441 if (map != ft2_oldstyle_map)
1445 // switching from freetype to non-freetype rendering
1446 GL_LockArrays(0, batchcount * 4);
1447 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1448 GL_LockArrays(0, 0);
1454 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1);
1455 map = ft2_oldstyle_map;
1459 //num = (unsigned char) text[i];
1460 //thisw = fnt->width_of[num];
1461 thisw = fnt->width_of[ch];
1462 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1463 s = (ch & 15)*0.0625f + (0.5f / tw);
1464 t = (ch >> 4)*0.0625f + (0.5f / th);
1465 u = 0.0625f * thisw - (1.0f / tw);
1466 v = 0.0625f - (1.0f / th);
1467 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1468 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1469 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1470 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1471 at[ 0] = s ; at[ 1] = t ;
1472 at[ 2] = s+u ; at[ 3] = t ;
1473 at[ 4] = s+u ; at[ 5] = t+v ;
1474 at[ 6] = s ; at[ 7] = t+v ;
1475 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1476 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1477 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1478 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1483 if (batchcount >= QUADELEMENTS_MAXQUADS)
1485 GL_LockArrays(0, batchcount * 4);
1486 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1487 GL_LockArrays(0, 0);
1495 if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch)
1497 // new charmap - need to render
1500 // we need a different character map, render what we currently have:
1501 GL_LockArrays(0, batchcount * 4);
1502 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1503 GL_LockArrays(0, 0);
1510 map = FontMap_FindForChar(fontmap, ch);
1513 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1520 // this shouldn't happen
1525 R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1);
1528 mapch = ch - map->start;
1529 thisw = map->glyphs[mapch].advance_x;
1533 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1540 ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3];
1541 ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3];
1542 ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3];
1543 ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3];
1544 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1545 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1546 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1547 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1548 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1549 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1550 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1551 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1560 if (batchcount >= QUADELEMENTS_MAXQUADS)
1562 GL_LockArrays(0, batchcount * 4);
1563 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1564 GL_LockArrays(0, 0);
1576 x -= 1.0/pix_x * r_textshadow.value;
1577 y -= 1.0/pix_y * r_textshadow.value;
1583 GL_LockArrays(0, batchcount * 4);
1584 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0);
1585 GL_LockArrays(0, 0);
1589 *outcolor = colorindex;
1591 // note: this relies on the proper text (not shadow) being drawn last
1595 float DrawQ_String_Font(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)
1597 return DrawQ_String_Font_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1600 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)
1602 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1605 float DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(const char *text, float w, float h, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1607 return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size_Scale(text, w, h, 1, 1, maxlen, outcolor, ignorecolorcodes, fnt, maxwidth);
1610 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1612 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1615 float DrawQ_TextWidth_Font_Size(const char *text, float w, float h, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1617 return DrawQ_TextWidth_Font_UntilWidth_Size(text, w, h, &maxlen, ignorecolorcodes, fnt, 1000000000);
1620 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1622 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1625 float DrawQ_TextWidth_Font_UntilWidth_Size(const char *text, float w, float h, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1627 return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(text, w, h, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1630 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
1632 return DrawQ_TextWidth_Font_UntilWidth_TrackColors_Size(text, 0, 0, maxlen, outcolor, ignorecolorcodes, fnt, maxwidth);
1637 // no ^xrgb management
1638 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1640 int color, numchars = 0;
1641 char *outputend2c = output2c + maxoutchars - 2;
1642 if (!outcolor || *outcolor == -1)
1643 color = STRING_COLOR_DEFAULT;
1647 maxreadchars = 1<<30;
1648 textend = text + maxreadchars;
1649 while (text != textend && *text)
1651 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1653 if (text[1] == STRING_COLOR_TAG)
1655 else if (text[1] >= '0' && text[1] <= '9')
1657 color = text[1] - '0';
1662 if (output2c >= outputend2c)
1664 *output2c++ = *text++;
1665 *output2c++ = color;
1668 output2c[0] = output2c[1] = 0;
1675 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)
1679 _DrawQ_ProcessDrawFlag(flags);
1681 R_Mesh_VertexPointer(floats, 0, 0);
1682 R_Mesh_ColorPointer(floats + 20, 0, 0);
1683 R_Mesh_ResetTextureState();
1689 height = pic->height;
1690 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
1691 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1692 floats[12] = s1;floats[13] = t1;
1693 floats[14] = s2;floats[15] = t2;
1694 floats[16] = s4;floats[17] = t4;
1695 floats[18] = s3;floats[19] = t3;
1698 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1700 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1701 floats[0] = floats[9] = x;
1702 floats[1] = floats[4] = y;
1703 floats[3] = floats[6] = x + width;
1704 floats[7] = floats[10] = y + height;
1705 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1706 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1707 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1708 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1710 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
1713 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1715 _DrawQ_ProcessDrawFlag(flags);
1717 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1718 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1719 R_Mesh_ResetTextureState();
1720 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1721 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1);
1723 GL_LockArrays(0, mesh->num_vertices);
1724 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0);
1725 GL_LockArrays(0, 0);
1728 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1732 _DrawQ_ProcessDrawFlag(flags);
1736 qglBegin(GL_LINE_LOOP);
1737 for (num = 0;num < mesh->num_vertices;num++)
1739 if (mesh->data_color4f)
1740 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]);
1741 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1747 //[515]: this is old, delete
1748 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1750 _DrawQ_ProcessDrawFlag(flags);
1752 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1755 //qglLineWidth(width);CHECKGLERROR
1757 GL_Color(r,g,b,alpha);
1760 qglVertex2f(x1, y1);
1761 qglVertex2f(x2, y2);
1766 void DrawQ_SetClipArea(float x, float y, float width, float height)
1771 // We have to convert the con coords into real coords
1772 // OGL uses top to bottom
1773 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
1774 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
1775 iw = (int)(width * ((float)vid.width / vid_conwidth.integer));
1776 ih = (int)(height * ((float)vid.height / vid_conheight.integer));
1777 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
1779 GL_ScissorTest(true);
1782 void DrawQ_ResetClipArea(void)
1785 GL_ScissorTest(false);
1788 void DrawQ_Finish(void)
1790 r_refdef.draw2dstage = false;
1793 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1794 void R_DrawGamma(void)
1797 switch(vid.renderpath)
1799 case RENDERPATH_GL20:
1800 case RENDERPATH_CGGL:
1801 if (vid_usinghwgamma || v_glslgamma.integer)
1804 case RENDERPATH_GL13:
1805 case RENDERPATH_GL11:
1806 if (vid_usinghwgamma)
1810 // all the blends ignore depth
1811 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1812 R_Mesh_ColorPointer(NULL, 0, 0);
1813 R_Mesh_ResetTextureState();
1814 R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
1816 GL_DepthRange(0, 1);
1817 GL_PolygonOffset(0, 0);
1818 GL_DepthTest(false);
1819 if (v_color_enable.integer)
1821 c[0] = v_color_white_r.value;
1822 c[1] = v_color_white_g.value;
1823 c[2] = v_color_white_b.value;
1826 c[0] = c[1] = c[2] = v_contrast.value;
1827 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1829 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1830 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1832 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1833 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);
1834 VectorScale(c, 0.5, c);
1837 if (v_color_enable.integer)
1839 c[0] = v_color_black_r.value;
1840 c[1] = v_color_black_g.value;
1841 c[2] = v_color_black_b.value;
1844 c[0] = c[1] = c[2] = v_brightness.value;
1845 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1847 GL_BlendFunc(GL_ONE, GL_ONE);
1848 GL_Color(c[0], c[1], c[2], 1);
1849 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0);