+ LoadFont(true, mainfont, f, scale, voffset);
+}
+
+/*
+===============
+Draw_Init
+===============
+*/
+static void gl_draw_start(void)
+{
+ int i;
+ drawtexturepool = R_AllocTexturePool();
+
+ numcachepics = 0;
+ memset(cachepichash, 0, sizeof(cachepichash));
+
+ font_start();
+
+ // load default font textures
+ for(i = 0; i < dp_fonts.maxsize; ++i)
+ if (dp_fonts.f[i].title[0])
+ LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
+
+ // draw the loading screen so people have something to see in the newly opened window
+ SCR_UpdateLoadingScreen(true);
+}
+
+static void gl_draw_shutdown(void)
+{
+ font_shutdown();
+
+ R_FreeTexturePool(&drawtexturepool);
+
+ numcachepics = 0;
+ memset(cachepichash, 0, sizeof(cachepichash));
+}
+
+static void gl_draw_newmap(void)
+{
+ font_newmap();
+}
+
+void GL_Draw_Init (void)
+{
+ int i, j;
+
+ Cvar_RegisterVariable(&r_font_postprocess_blur);
+ Cvar_RegisterVariable(&r_font_postprocess_outline);
+ Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
+ Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
+ Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
+ Cvar_RegisterVariable(&r_font_hinting);
+ Cvar_RegisterVariable(&r_font_antialias);
+ Cvar_RegisterVariable(&r_textshadow);
+ Cvar_RegisterVariable(&r_textbrightness);
+ Cvar_RegisterVariable(&r_textcontrast);
+
+ // allocate fonts storage
+ fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
+ dp_fonts.maxsize = MAX_FONTS;
+ dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
+ memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
+
+ // assign starting font names
+ strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
+ strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
+ strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
+ strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
+ strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
+ strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
+ strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
+ strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
+ strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
+ for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
+ if(!FONT_USER(i)->title[0])
+ dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
+
+ Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
+ R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
+}
+
+static void _DrawQ_Setup(void)
+{
+ r_viewport_t viewport;
+ if (r_refdef.draw2dstage == 1)
+ return;
+ r_refdef.draw2dstage = 1;
+ CHECKGLERROR
+ 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);
+ R_SetViewport(&viewport);
+ GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
+ GL_DepthFunc(GL_LEQUAL);
+ GL_PolygonOffset(0,0);
+ GL_CullFace(GL_NONE);
+ R_EntityMatrix(&identitymatrix);
+
+ GL_DepthRange(0, 1);
+ GL_PolygonOffset(0, 0);
+ GL_DepthTest(false);
+ GL_Color(1,1,1,1);
+ GL_AlphaTest(false);
+}
+
+qboolean r_draw2d_force = false;
+void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
+{
+ _DrawQ_Setup();
+ CHECKGLERROR
+ if(!r_draw2d.integer && !r_draw2d_force)
+ return;
+ DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
+}
+void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
+{
+ if(flags == DRAWFLAG_ADDITIVE)
+ {
+ GL_DepthMask(false);
+ GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
+ }
+ else if(flags == DRAWFLAG_MODULATE)
+ {
+ GL_DepthMask(false);
+ GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
+ }
+ else if(flags == DRAWFLAG_2XMODULATE)
+ {
+ GL_DepthMask(false);
+ GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+ }
+ else if(flags == DRAWFLAG_SCREEN)
+ {
+ GL_DepthMask(false);
+ GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
+ }
+ else if(alpha)
+ {
+ GL_DepthMask(false);
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ else
+ {
+ GL_DepthMask(true);
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ }
+}
+
+void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
+{
+ float floats[36];
+
+ _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
+ if(!r_draw2d.integer && !r_draw2d_force)
+ return;
+
+// R_Mesh_ResetTextureState();
+ floats[12] = 0.0f;floats[13] = 0.0f;
+ floats[14] = 1.0f;floats[15] = 0.0f;
+ floats[16] = 1.0f;floats[17] = 1.0f;
+ floats[18] = 0.0f;floats[19] = 1.0f;
+ floats[20] = floats[24] = floats[28] = floats[32] = red;
+ floats[21] = floats[25] = floats[29] = floats[33] = green;
+ floats[22] = floats[26] = floats[30] = floats[34] = blue;
+ floats[23] = floats[27] = floats[31] = floats[35] = alpha;
+ if (pic)
+ {
+ if (width == 0)
+ width = pic->width;
+ if (height == 0)
+ height = pic->height;
+ R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
+
+#if 0
+ // AK07: lets be texel correct on the corners
+ {
+ float horz_offset = 0.5f / pic->width;
+ float vert_offset = 0.5f / pic->height;
+
+ floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
+ floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
+ floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
+ floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
+ }
+#endif
+ }
+ else
+ R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+
+ floats[2] = floats[5] = floats[8] = floats[11] = 0;
+ floats[0] = floats[9] = x;
+ floats[1] = floats[4] = y;
+ floats[3] = floats[6] = x + width;
+ floats[7] = floats[10] = y + height;
+
+ R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
+ R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+}
+
+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)
+{
+ float floats[36];
+ float af = DEG2RAD(-angle); // forward
+ float ar = DEG2RAD(-angle + 90); // right
+ float sinaf = sin(af);
+ float cosaf = cos(af);
+ float sinar = sin(ar);
+ float cosar = cos(ar);
+
+ _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
+ if(!r_draw2d.integer && !r_draw2d_force)
+ return;
+
+// R_Mesh_ResetTextureState();
+ if (pic)
+ {
+ if (width == 0)
+ width = pic->width;
+ if (height == 0)
+ height = pic->height;
+ R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1);
+ }
+ else
+ R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+
+ floats[2] = floats[5] = floats[8] = floats[11] = 0;
+
+// top left
+ floats[0] = x - cosaf*org_x - cosar*org_y;
+ floats[1] = y - sinaf*org_x - sinar*org_y;
+
+// top right
+ floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
+ floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
+
+// bottom right
+ floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
+ floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
+
+// bottom left
+ floats[9] = x - cosaf*org_x + cosar*(height-org_y);
+ floats[10] = y - sinaf*org_x + sinar*(height-org_y);
+
+ floats[12] = 0.0f;floats[13] = 0.0f;
+ floats[14] = 1.0f;floats[15] = 0.0f;
+ floats[16] = 1.0f;floats[17] = 1.0f;
+ floats[18] = 0.0f;floats[19] = 1.0f;
+ floats[20] = floats[24] = floats[28] = floats[32] = red;
+ floats[21] = floats[25] = floats[29] = floats[33] = green;
+ floats[22] = floats[26] = floats[30] = floats[34] = blue;
+ floats[23] = floats[27] = floats[31] = floats[35] = alpha;
+
+ R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
+ R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+}
+
+void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
+{
+ float floats[36];
+
+ _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
+ if(!r_draw2d.integer && !r_draw2d_force)
+ return;
+
+// R_Mesh_ResetTextureState();
+ R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+
+ floats[2] = floats[5] = floats[8] = floats[11] = 0;
+ floats[0] = floats[9] = x;
+ floats[1] = floats[4] = y;
+ floats[3] = floats[6] = x + width;
+ floats[7] = floats[10] = y + height;
+ floats[12] = 0.0f;floats[13] = 0.0f;
+ floats[14] = 1.0f;floats[15] = 0.0f;
+ floats[16] = 1.0f;floats[17] = 1.0f;
+ floats[18] = 0.0f;floats[19] = 1.0f;
+ floats[20] = floats[24] = floats[28] = floats[32] = red;
+ floats[21] = floats[25] = floats[29] = floats[33] = green;
+ floats[22] = floats[26] = floats[30] = floats[34] = blue;
+ floats[23] = floats[27] = floats[31] = floats[35] = alpha;
+
+ R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
+ R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+}
+
+/// color tag printing
+static const vec4_t string_colors[] =
+{
+ // Quake3 colors
+ // LordHavoc: why on earth is cyan before magenta in Quake3?
+ // LordHavoc: note: Doom3 uses white for [0] and [7]
+ {0.0, 0.0, 0.0, 1.0}, // black
+ {1.0, 0.0, 0.0, 1.0}, // red
+ {0.0, 1.0, 0.0, 1.0}, // green
+ {1.0, 1.0, 0.0, 1.0}, // yellow
+ {0.0, 0.0, 1.0, 1.0}, // blue
+ {0.0, 1.0, 1.0, 1.0}, // cyan
+ {1.0, 0.0, 1.0, 1.0}, // magenta
+ {1.0, 1.0, 1.0, 1.0}, // white
+ // [515]'s BX_COLOREDTEXT extension
+ {1.0, 1.0, 1.0, 0.5}, // half transparent
+ {0.5, 0.5, 0.5, 1.0} // half brightness
+ // Black's color table
+ //{1.0, 1.0, 1.0, 1.0},
+ //{1.0, 0.0, 0.0, 1.0},
+ //{0.0, 1.0, 0.0, 1.0},
+ //{0.0, 0.0, 1.0, 1.0},
+ //{1.0, 1.0, 0.0, 1.0},
+ //{0.0, 1.0, 1.0, 1.0},
+ //{1.0, 0.0, 1.0, 1.0},
+ //{0.1, 0.1, 0.1, 1.0}
+};
+
+#define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
+
+static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
+{
+ float C = r_textcontrast.value;
+ float B = r_textbrightness.value;
+ if (colorindex & 0x10000) // that bit means RGB color
+ {
+ color[0] = ((colorindex >> 12) & 0xf) / 15.0;
+ color[1] = ((colorindex >> 8) & 0xf) / 15.0;
+ color[2] = ((colorindex >> 4) & 0xf) / 15.0;
+ color[3] = (colorindex & 0xf) / 15.0;
+ }
+ else
+ Vector4Copy(string_colors[colorindex], color);
+ Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
+ if (shadow)
+ {
+ float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
+ Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
+ }
+}
+
+// NOTE: this function always draws exactly one character if maxwidth <= 0
+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)
+{
+ const char *text_start = text;
+ int colorindex = STRING_COLOR_DEFAULT;
+ size_t i;
+ float x = 0;
+ Uchar ch, mapch, nextch;
+ Uchar prevch = 0; // used for kerning
+ int tempcolorindex;
+ float kx;
+ int map_index = 0;
+ size_t bytes_left;
+ ft2_font_map_t *fontmap = NULL;
+ ft2_font_map_t *map = NULL;
+ //ft2_font_map_t *prevmap = NULL;
+ ft2_font_t *ft2 = fnt->ft2;
+ // float ftbase_x;
+ qboolean snap = true;
+ qboolean least_one = false;
+ float dw; // display w
+ //float dh; // display h
+ const float *width_of;
+
+ if (!h) h = w;
+ if (!h) {
+ w = h = 1;
+ snap = false;
+ }
+ // do this in the end
+ w *= fnt->settings.scale;
+ h *= fnt->settings.scale;
+
+ // find the most fitting size:
+ if (ft2 != NULL)
+ {
+ if (snap)
+ map_index = Font_IndexForSize(ft2, h, &w, &h);
+ else
+ map_index = Font_IndexForSize(ft2, h, NULL, NULL);
+ fontmap = Font_MapForIndex(ft2, map_index);
+ }
+
+ dw = w * sw;
+ //dh = h * sh;
+
+ if (*maxlen < 1)
+ *maxlen = 1<<30;
+
+ if (!outcolor || *outcolor == -1)
+ colorindex = STRING_COLOR_DEFAULT;
+ else
+ colorindex = *outcolor;
+
+ // maxwidth /= fnt->scale; // w and h are multiplied by it already
+ // ftbase_x = snap_to_pixel_x(0);
+
+ if(maxwidth <= 0)
+ {
+ least_one = true;
+ maxwidth = -maxwidth;
+ }
+
+ //if (snap)
+ // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
+
+ if (fontmap)
+ width_of = fontmap->width_of;
+ else
+ width_of = fnt->width_of;
+
+ for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
+ {
+ size_t i0 = i;
+ nextch = ch = u8_getnchar(text, &text, bytes_left);
+ i = text - text_start;
+ if (!ch)
+ break;
+ if (ch == ' ' && !fontmap)
+ {
+ if(!least_one || i0) // never skip the first character
+ if(x + width_of[(int) ' '] * dw > maxwidth)
+ {
+ i = i0;
+ break; // oops, can't draw this
+ }
+ x += width_of[(int) ' '] * dw;
+ continue;