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"
28 dp_font_t dp_fonts[MAX_FONTS] = {{0}};
30 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)"};
31 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)"};
32 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)"};
34 extern cvar_t v_glslgamma;
36 //=============================================================================
37 /* Support Routines */
39 #define FONT_FILESIZE 13468
40 #define MAX_CACHED_PICS 1024
41 #define CACHEPICHASHSIZE 256
42 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
43 static cachepic_t cachepics[MAX_CACHED_PICS];
44 static int numcachepics;
46 static rtexturepool_t *drawtexturepool;
48 static const unsigned char concharimage[FONT_FILESIZE] =
53 static rtexture_t *draw_generateconchars(void)
56 unsigned char buffer[65536][4], *data = NULL;
59 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE);
61 for (i = 0;i < 8192;i++)
63 random = lhrandom (0.0,1.0);
64 buffer[i][2] = 83 + (unsigned char)(random * 64);
65 buffer[i][1] = 71 + (unsigned char)(random * 32);
66 buffer[i][0] = 23 + (unsigned char)(random * 16);
67 buffer[i][3] = data[i*4+0];
70 for (i = 8192;i < 32768;i++)
72 random = lhrandom (0.0,1.0);
73 buffer[i][2] = 95 + (unsigned char)(random * 64);
74 buffer[i][1] = 95 + (unsigned char)(random * 64);
75 buffer[i][0] = 95 + (unsigned char)(random * 64);
76 buffer[i][3] = data[i*4+0];
79 for (i = 32768;i < 40960;i++)
81 random = lhrandom (0.0,1.0);
82 buffer[i][2] = 83 + (unsigned char)(random * 64);
83 buffer[i][1] = 71 + (unsigned char)(random * 32);
84 buffer[i][0] = 23 + (unsigned char)(random * 16);
85 buffer[i][3] = data[i*4+0];
88 for (i = 40960;i < 65536;i++)
90 random = lhrandom (0.0,1.0);
91 buffer[i][2] = 96 + (unsigned char)(random * 64);
92 buffer[i][1] = 43 + (unsigned char)(random * 32);
93 buffer[i][0] = 27 + (unsigned char)(random * 32);
94 buffer[i][3] = data[i*4+0];
98 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]);
102 return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
105 static rtexture_t *draw_generateditherpattern(void)
108 unsigned char pixels[8][8];
109 for (y = 0;y < 8;y++)
110 for (x = 0;x < 8;x++)
111 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
112 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, palette_bgra_transparent);
115 typedef struct embeddedpic_s
124 static const embeddedpic_t embeddedpics[] =
127 "gfx/prydoncursor001", 16, 16,
146 "ui/mousepointer", 16, 16,
165 "gfx/crosshair1", 16, 16,
184 "gfx/crosshair2", 16, 16,
203 "gfx/crosshair3", 16, 16,
222 "gfx/crosshair4", 16, 16,
241 "gfx/crosshair5", 8, 8,
252 "gfx/crosshair6", 2, 2,
257 "gfx/crosshair7", 16, 16,
276 "gfx/editlights/cursor", 16, 16,
295 "gfx/editlights/light", 16, 16,
314 "gfx/editlights/noshadow", 16, 16,
333 "gfx/editlights/selection", 16, 16,
352 "gfx/editlights/cubemaplight", 16, 16,
371 "gfx/editlights/cubemapnoshadowlight", 16, 16,
392 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
394 const embeddedpic_t *p;
395 for (p = embeddedpics;p->name;p++)
396 if (!strcmp(name, p->name))
397 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic);
398 if (!strcmp(name, "gfx/conchars"))
399 return draw_generateconchars();
400 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
401 return draw_generateditherpattern();
403 Con_Printf("Draw_CachePic: failed to load %s\n", name);
404 return r_texture_notexture;
413 // FIXME: move this to client somehow
414 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
420 unsigned char *lmpdata;
421 char lmpname[MAX_QPATH];
423 // check whether the picture has already been cached
424 crc = CRC_Block((unsigned char *)path, strlen(path));
425 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
426 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
427 if (!strcmp (path, pic->name))
430 if (numcachepics == MAX_CACHED_PICS)
432 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
433 // FIXME: support NULL in callers?
434 return cachepics; // return the first one
436 pic = cachepics + (numcachepics++);
437 strlcpy (pic->name, path, sizeof(pic->name));
439 pic->chain = cachepichash[hashkey];
440 cachepichash[hashkey] = pic;
442 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
443 pic->tex = CL_GetDynTexture( path );
444 // if so, set the width/height, too
446 pic->width = R_TextureWidth(pic->tex);
447 pic->height = R_TextureHeight(pic->tex);
448 // we're done now (early-out)
453 if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
454 flags |= TEXF_PRECACHE;
455 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
457 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer)
458 flags |= TEXF_COMPRESS;
460 // load a high quality image from disk if possible
461 pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true);
462 if (pic->tex == NULL && !strncmp(path, "gfx/", 4))
464 // compatibility with older versions which did not require gfx/ prefix
465 pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true);
467 // if a high quality image was loaded, set the pic's size to match it, just
468 // in case there's no low quality version to get the size from
471 pic->width = R_TextureWidth(pic->tex);
472 pic->height = R_TextureHeight(pic->tex);
475 // now read the low quality version (wad or lmp file), and take the pic
476 // size from that even if we don't upload the texture, this way the pics
477 // show up the right size in the menu even if they were replaced with
478 // higher or lower resolution versions
479 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", path);
480 if (!strncmp(path, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
482 if (developer_loading.integer)
483 Con_Printf("loading lump \"%s\"\n", path);
487 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
488 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
489 // if no high quality replacement image was found, upload the original low quality texture
491 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
495 else if ((lmpdata = W_GetLumpName (path + 4)))
497 if (developer_loading.integer)
498 Con_Printf("loading gfx.wad lump \"%s\"\n", path + 4);
500 if (!strcmp(path, "gfx/conchars"))
502 // conchars is a raw image and with color 0 as transparent instead of 255
505 // if no high quality replacement image was found, upload the original low quality texture
507 pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font);
511 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
512 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
513 // if no high quality replacement image was found, upload the original low quality texture
515 pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent);
519 // if it's not found on disk, generate an image
520 if (pic->tex == NULL)
522 pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
523 pic->width = R_TextureWidth(pic->tex);
524 pic->height = R_TextureHeight(pic->tex);
530 cachepic_t *Draw_CachePic (const char *path)
532 return Draw_CachePic_Flags (path, 0);
535 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
540 crc = CRC_Block((unsigned char *)picname, strlen(picname));
541 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
542 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
543 if (!strcmp (picname, pic->name))
548 if (pic->tex && pic->width == width && pic->height == height)
550 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, width, height);
558 if (numcachepics == MAX_CACHED_PICS)
560 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
561 // FIXME: support NULL in callers?
562 return cachepics; // return the first one
564 pic = cachepics + (numcachepics++);
565 strlcpy (pic->name, picname, sizeof(pic->name));
567 pic->chain = cachepichash[hashkey];
568 cachepichash[hashkey] = pic;
573 pic->height = height;
575 R_FreeTexture(pic->tex);
576 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL);
580 void Draw_FreePic(const char *picname)
585 // this doesn't really free the pic, but does free it's texture
586 crc = CRC_Block((unsigned char *)picname, strlen(picname));
587 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
588 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
590 if (!strcmp (picname, pic->name) && pic->tex)
592 R_FreeTexture(pic->tex);
600 extern int con_linewidth; // to force rewrapping
601 static void LoadFont(qboolean override, const char *name, dp_font_t *fnt)
604 float maxwidth, scale;
605 char widthfile[MAX_QPATH];
607 fs_offset_t widthbufsize;
609 if(override || !fnt->texpath[0])
610 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
612 if(drawtexturepool == NULL)
613 return; // before gl_draw_start, so will be loaded later
615 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
616 if(fnt->tex == r_texture_notexture)
618 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
619 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
622 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
624 // unspecified width == 1 (base width)
625 for(i = 1; i < 256; ++i)
626 fnt->width_of[i] = 1;
629 // FIXME load "name.width", if it fails, fill all with 1
630 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
632 float extraspacing = 0;
633 const char *p = widthbuf;
638 if(!COM_ParseToken_Simple(&p, false, false))
656 fnt->width_of[ch++] = atof(com_token) + extraspacing;
659 if(!strcmp(com_token, "extraspacing"))
661 if(!COM_ParseToken_Simple(&p, false, false))
663 extraspacing = atof(com_token);
665 else if(!strcmp(com_token, "scale"))
667 if(!COM_ParseToken_Simple(&p, false, false))
669 scale = atof(com_token);
673 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
674 if(!COM_ParseToken_Simple(&p, false, false))
684 maxwidth = fnt->width_of[1];
685 for(i = 2; i < 256; ++i)
686 maxwidth = max(maxwidth, fnt->width_of[i]);
687 fnt->maxwidth = maxwidth;
689 // fix up maxwidth for overlap
690 fnt->maxwidth *= scale;
693 if(fnt == FONT_CONSOLE)
694 con_linewidth = -1; // rewrap console in next frame
697 static dp_font_t *FindFont(const char *title)
700 for(i = 0; i < MAX_FONTS; ++i)
701 if(!strcmp(dp_fonts[i].title, title))
706 static void LoadFont_f(void)
712 Con_Printf("Available font commands:\n");
713 for(i = 0; i < MAX_FONTS; ++i)
714 Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title);
717 f = FindFont(Cmd_Argv(1));
720 Con_Printf("font function not found\n");
723 LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f);
731 static void gl_draw_start(void)
734 drawtexturepool = R_AllocTexturePool();
737 memset(cachepichash, 0, sizeof(cachepichash));
739 for(i = 0; i < MAX_FONTS; ++i)
740 LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]);
742 // draw the loading screen so people have something to see in the newly opened window
743 SCR_UpdateLoadingScreen(true);
746 static void gl_draw_shutdown(void)
748 R_FreeTexturePool(&drawtexturepool);
751 memset(cachepichash, 0, sizeof(cachepichash));
754 static void gl_draw_newmap(void)
758 void GL_Draw_Init (void)
761 Cvar_RegisterVariable(&r_textshadow);
762 Cvar_RegisterVariable(&r_textbrightness);
763 Cvar_RegisterVariable(&r_textcontrast);
764 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
765 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap);
767 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
768 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
769 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
770 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
771 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
772 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
773 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
774 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
775 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
776 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
777 if(!FONT_USER[i].title[0])
778 dpsnprintf(FONT_USER[i].title, sizeof(FONT_USER[i].title), "user%d", j++);
781 void _DrawQ_Setup(void)
783 if (r_refdef.draw2dstage)
785 r_refdef.draw2dstage = true;
787 qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);CHECKGLERROR
788 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
789 GL_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100);
790 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
791 qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
792 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
793 R_Mesh_Matrix(&identitymatrix);
797 GL_PolygonOffset(0, 0);
801 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
803 R_SetupGenericShader(true);
806 static void _DrawQ_ProcessDrawFlag(int flags)
810 if(flags == DRAWFLAG_ADDITIVE)
811 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
812 else if(flags == DRAWFLAG_MODULATE)
813 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
814 else if(flags == DRAWFLAG_2XMODULATE)
815 GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
816 else if(flags == DRAWFLAG_SCREEN)
817 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR,GL_ONE);
819 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
822 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
826 _DrawQ_ProcessDrawFlag(flags);
827 GL_Color(red, green, blue, alpha);
829 R_Mesh_VertexPointer(floats, 0, 0);
830 R_Mesh_ColorPointer(NULL, 0, 0);
831 R_Mesh_ResetTextureState();
832 R_SetupGenericShader(pic != NULL);
838 height = pic->height;
839 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
840 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
843 floats[12] = 0.0f;floats[13] = 0.0f;
844 floats[14] = 1.0f;floats[15] = 0.0f;
845 floats[16] = 1.0f;floats[17] = 1.0f;
846 floats[18] = 0.0f;floats[19] = 1.0f;
848 // AK07: lets be texel correct on the corners
850 float horz_offset = 0.5f / pic->width;
851 float vert_offset = 0.5f / pic->height;
853 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
854 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
855 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
856 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
861 floats[2] = floats[5] = floats[8] = floats[11] = 0;
862 floats[0] = floats[9] = x;
863 floats[1] = floats[4] = y;
864 floats[3] = floats[6] = x + width;
865 floats[7] = floats[10] = y + height;
867 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
870 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
874 _DrawQ_ProcessDrawFlag(flags);
875 GL_Color(red, green, blue, alpha);
877 R_Mesh_VertexPointer(floats, 0, 0);
878 R_Mesh_ColorPointer(NULL, 0, 0);
879 R_Mesh_ResetTextureState();
880 R_SetupGenericShader(false);
882 floats[2] = floats[5] = floats[8] = floats[11] = 0;
883 floats[0] = floats[9] = x;
884 floats[1] = floats[4] = y;
885 floats[3] = floats[6] = x + width;
886 floats[7] = floats[10] = y + height;
888 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
891 // color tag printing
892 static const vec4_t string_colors[] =
895 // LordHavoc: why on earth is cyan before magenta in Quake3?
896 // LordHavoc: note: Doom3 uses white for [0] and [7]
897 {0.0, 0.0, 0.0, 1.0}, // black
898 {1.0, 0.0, 0.0, 1.0}, // red
899 {0.0, 1.0, 0.0, 1.0}, // green
900 {1.0, 1.0, 0.0, 1.0}, // yellow
901 {0.0, 0.0, 1.0, 1.0}, // blue
902 {0.0, 1.0, 1.0, 1.0}, // cyan
903 {1.0, 0.0, 1.0, 1.0}, // magenta
904 {1.0, 1.0, 1.0, 1.0}, // white
905 // [515]'s BX_COLOREDTEXT extension
906 {1.0, 1.0, 1.0, 0.5}, // half transparent
907 {0.5, 0.5, 0.5, 1.0} // half brightness
908 // Black's color table
909 //{1.0, 1.0, 1.0, 1.0},
910 //{1.0, 0.0, 0.0, 1.0},
911 //{0.0, 1.0, 0.0, 1.0},
912 //{0.0, 0.0, 1.0, 1.0},
913 //{1.0, 1.0, 0.0, 1.0},
914 //{0.0, 1.0, 1.0, 1.0},
915 //{1.0, 0.0, 1.0, 1.0},
916 //{0.1, 0.1, 0.1, 1.0}
919 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
921 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
923 float C = r_textcontrast.value;
924 float B = r_textbrightness.value;
925 if (colorindex & 0x10000) // that bit means RGB color
927 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
928 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
929 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
930 color[3] = (colorindex & 0xf) / 15.0;
933 Vector4Copy(string_colors[colorindex], color);
934 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
937 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
938 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
942 float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth)
944 int num, colorindex = STRING_COLOR_DEFAULT;
953 if (!outcolor || *outcolor == -1)
954 colorindex = STRING_COLOR_DEFAULT;
956 colorindex = *outcolor;
958 maxwidth /= fnt->scale;
960 for (i = 0;i < *maxlen && text[i];i++)
964 if(x + fnt->width_of[(int) ' '] > maxwidth)
965 break; // oops, can't draw this
966 x += fnt->width_of[(int) ' '];
969 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen)
972 if (ch <= '9' && ch >= '0') // ^[0-9] found
974 colorindex = ch - '0';
977 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
979 // building colorindex...
980 ch = tolower(text[i+1]);
981 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
982 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
983 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
984 else tempcolorindex = 0;
987 ch = tolower(text[i+2]);
988 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
989 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
990 else tempcolorindex = 0;
993 ch = tolower(text[i+3]);
994 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
995 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
996 else tempcolorindex = 0;
999 colorindex = tempcolorindex | 0xf;
1000 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1007 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1011 num = (unsigned char) text[i];
1012 if(x + fnt->width_of[num] > maxwidth)
1013 break; // oops, can't draw this
1014 x += fnt->width_of[num];
1020 *outcolor = colorindex;
1022 return x * fnt->scale;
1025 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)
1027 int num, shadow, colorindex = STRING_COLOR_DEFAULT;
1029 float x = startx, y, s, t, u, v, thisw;
1030 float *av, *at, *ac;
1033 float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1034 float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1035 float color4f[QUADELEMENTS_MAXQUADS*4*4];
1040 tw = R_TextureWidth(fnt->tex);
1041 th = R_TextureHeight(fnt->tex);
1043 starty -= (fnt->scale - 1) * h * 0.5; // center
1050 _DrawQ_ProcessDrawFlag(flags);
1052 R_Mesh_ColorPointer(color4f, 0, 0);
1053 R_Mesh_ResetTextureState();
1054 R_Mesh_TexBind(0, R_GetTexture(fnt->tex));
1055 R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0);
1056 R_Mesh_VertexPointer(vertex3f, 0, 0);
1057 R_SetupGenericShader(true);
1064 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1066 if (!outcolor || *outcolor == -1)
1067 colorindex = STRING_COLOR_DEFAULT;
1069 colorindex = *outcolor;
1071 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1077 x += r_textshadow.value;
1078 y += r_textshadow.value;
1080 for (i = 0;i < maxlen && text[i];i++)
1084 x += fnt->width_of[(int) ' '] * w;
1087 if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen)
1090 if (ch <= '9' && ch >= '0') // ^[0-9] found
1092 colorindex = ch - '0';
1093 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1096 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1098 // building colorindex...
1099 ch = tolower(text[i+1]);
1100 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1101 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1102 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1103 else tempcolorindex = 0;
1106 ch = tolower(text[i+2]);
1107 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1108 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1109 else tempcolorindex = 0;
1112 ch = tolower(text[i+3]);
1113 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1114 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1115 else tempcolorindex = 0;
1118 colorindex = tempcolorindex | 0xf;
1119 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1120 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1121 DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow);
1128 else if (ch == STRING_COLOR_TAG)
1132 num = (unsigned char) text[i];
1133 thisw = fnt->width_of[num];
1134 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1135 s = (num & 15)*0.0625f + (0.5f / tw);
1136 t = (num >> 4)*0.0625f + (0.5f / th);
1137 u = 0.0625f * thisw - (1.0f / tw);
1138 v = 0.0625f - (1.0f / th);
1139 ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3];
1140 ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3];
1141 ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3];
1142 ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3];
1143 at[ 0] = s ; at[ 1] = t ;
1144 at[ 2] = s+u ; at[ 3] = t ;
1145 at[ 4] = s+u ; at[ 5] = t+v ;
1146 at[ 6] = s ; at[ 7] = t+v ;
1147 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1148 av[ 3] = x+w*thisw ; av[ 4] = y ; av[ 5] = 10;
1149 av[ 6] = x+w*thisw ; av[ 7] = y+h ; av[ 8] = 10;
1150 av[ 9] = x ; av[10] = y+h ; av[11] = 10;
1155 if (batchcount >= QUADELEMENTS_MAXQUADS)
1157 GL_LockArrays(0, batchcount * 4);
1158 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1159 GL_LockArrays(0, 0);
1170 GL_LockArrays(0, batchcount * 4);
1171 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0);
1172 GL_LockArrays(0, 0);
1176 *outcolor = colorindex;
1178 // note: this relies on the proper text (not shadow) being drawn last
1182 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)
1184 return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]);
1187 float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt)
1189 return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000);
1192 float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1194 return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth);
1199 // no ^xrgb management
1200 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1202 int color, numchars = 0;
1203 char *outputend2c = output2c + maxoutchars - 2;
1204 if (!outcolor || *outcolor == -1)
1205 color = STRING_COLOR_DEFAULT;
1209 maxreadchars = 1<<30;
1210 textend = text + maxreadchars;
1211 while (text != textend && *text)
1213 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1215 if (text[1] == STRING_COLOR_TAG)
1217 else if (text[1] >= '0' && text[1] <= '9')
1219 color = text[1] - '0';
1224 if (output2c >= outputend2c)
1226 *output2c++ = *text++;
1227 *output2c++ = color;
1230 output2c[0] = output2c[1] = 0;
1237 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)
1241 _DrawQ_ProcessDrawFlag(flags);
1243 R_Mesh_VertexPointer(floats, 0, 0);
1244 R_Mesh_ColorPointer(floats + 20, 0, 0);
1245 R_Mesh_ResetTextureState();
1246 R_SetupGenericShader(pic != NULL);
1252 height = pic->height;
1253 R_Mesh_TexBind(0, R_GetTexture(pic->tex));
1254 R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0);
1255 floats[12] = s1;floats[13] = t1;
1256 floats[14] = s2;floats[15] = t2;
1257 floats[16] = s4;floats[17] = t4;
1258 floats[18] = s3;floats[19] = t3;
1261 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1262 floats[0] = floats[9] = x;
1263 floats[1] = floats[4] = y;
1264 floats[3] = floats[6] = x + width;
1265 floats[7] = floats[10] = y + height;
1266 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1267 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1268 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1269 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1271 R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0);
1274 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags)
1276 _DrawQ_ProcessDrawFlag(flags);
1278 R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0);
1279 R_Mesh_ColorPointer(mesh->data_color4f, 0, 0);
1280 R_Mesh_ResetTextureState();
1281 R_Mesh_TexBind(0, R_GetTexture(mesh->texture));
1282 R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0);
1283 R_SetupGenericShader(mesh->texture != NULL);
1285 GL_LockArrays(0, mesh->num_vertices);
1286 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, NULL, mesh->data_element3s, 0, 0);
1287 GL_LockArrays(0, 0);
1290 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1294 _DrawQ_ProcessDrawFlag(flags);
1298 qglBegin(GL_LINE_LOOP);
1299 for (num = 0;num < mesh->num_vertices;num++)
1301 if (mesh->data_color4f)
1302 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]);
1303 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1309 //[515]: this is old, delete
1310 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
1312 _DrawQ_ProcessDrawFlag(flags);
1314 R_SetupGenericShader(false);
1317 qglLineWidth(width);CHECKGLERROR
1319 GL_Color(r,g,b,alpha);
1322 qglVertex2f(x1, y1);
1323 qglVertex2f(x2, y2);
1328 void DrawQ_SetClipArea(float x, float y, float width, float height)
1332 // We have to convert the con coords into real coords
1333 // OGL uses top to bottom
1334 GL_Scissor((int)(x * ((float)vid.width / vid_conwidth.integer)), (int)(y * ((float) vid.height / vid_conheight.integer)), (int)(width * ((float)vid.width / vid_conwidth.integer)), (int)(height * ((float)vid.height / vid_conheight.integer)));
1336 GL_ScissorTest(true);
1339 void DrawQ_ResetClipArea(void)
1342 GL_ScissorTest(false);
1345 void DrawQ_Finish(void)
1347 r_refdef.draw2dstage = false;
1350 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
1351 void R_DrawGamma(void)
1354 if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer))
1356 // all the blends ignore depth
1357 R_Mesh_VertexPointer(blendvertex3f, 0, 0);
1358 R_Mesh_ColorPointer(NULL, 0, 0);
1359 R_Mesh_ResetTextureState();
1360 R_SetupGenericShader(false);
1362 GL_DepthRange(0, 1);
1363 GL_PolygonOffset(0, 0);
1364 GL_DepthTest(false);
1365 if (v_color_enable.integer)
1367 c[0] = v_color_white_r.value;
1368 c[1] = v_color_white_g.value;
1369 c[2] = v_color_white_b.value;
1372 c[0] = c[1] = c[2] = v_contrast.value;
1373 if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1375 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
1376 while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f)
1378 GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1);
1379 R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0);
1380 VectorScale(c, 0.5, c);
1383 if (v_color_enable.integer)
1385 c[0] = v_color_black_r.value;
1386 c[1] = v_color_black_g.value;
1387 c[2] = v_color_black_b.value;
1390 c[0] = c[1] = c[2] = v_brightness.value;
1391 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
1393 GL_BlendFunc(GL_ONE, GL_ONE);
1394 GL_Color(c[0], c[1], c[2], 1);
1395 R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0);