X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=gl_draw.c;h=de8bc0850a6b3cc4dafc4bd771053af0873734f1;hp=d71e836fdbee952460a1d66c3d23c5edfae98f0e;hb=72943b86b04da4f0ee7b4074287ec3743b8cd077;hpb=c72641f5e795ed0e0d58a38d79860cc92597e4da diff --git a/gl_draw.c b/gl_draw.c index d71e836f..de8bc085 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -25,20 +25,29 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "cl_video.h" #include "cl_dyntexture.h" +#include "ft2.h" +#include "ft2_fontdefs.h" + dp_font_t dp_fonts[MAX_FONTS] = {{0}}; 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)"}; 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)"}; 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)"}; +cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"}; +cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"}; +cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"}; +cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"}; +cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"}; +cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"}; +cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */}; + extern cvar_t v_glslgamma; //============================================================================= /* Support Routines */ #define FONT_FILESIZE 13468 -#define MAX_CACHED_PICS 1024 -#define CACHEPICHASHSIZE 256 static cachepic_t *cachepichash[CACHEPICHASHSIZE]; static cachepic_t cachepics[MAX_CACHED_PICS]; static int numcachepics; @@ -53,53 +62,55 @@ static const unsigned char concharimage[FONT_FILESIZE] = static rtexture_t *draw_generateconchars(void) { int i; - unsigned char buffer[65536][4], *data = NULL; + unsigned char *data; double random; + rtexture_t *tex; data = LoadTGA_BGRA (concharimage, FONT_FILESIZE); // Gold numbers for (i = 0;i < 8192;i++) { random = lhrandom (0.0,1.0); - buffer[i][2] = 83 + (unsigned char)(random * 64); - buffer[i][1] = 71 + (unsigned char)(random * 32); - buffer[i][0] = 23 + (unsigned char)(random * 16); - buffer[i][3] = data[i*4+0]; + data[i*4+3] = data[i*4+0]; + data[i*4+2] = 83 + (unsigned char)(random * 64); + data[i*4+1] = 71 + (unsigned char)(random * 32); + data[i*4+0] = 23 + (unsigned char)(random * 16); } // White chars for (i = 8192;i < 32768;i++) { random = lhrandom (0.0,1.0); - buffer[i][2] = 95 + (unsigned char)(random * 64); - buffer[i][1] = 95 + (unsigned char)(random * 64); - buffer[i][0] = 95 + (unsigned char)(random * 64); - buffer[i][3] = data[i*4+0]; + data[i*4+3] = data[i*4+0]; + data[i*4+2] = 95 + (unsigned char)(random * 64); + data[i*4+1] = 95 + (unsigned char)(random * 64); + data[i*4+0] = 95 + (unsigned char)(random * 64); } // Gold numbers for (i = 32768;i < 40960;i++) { random = lhrandom (0.0,1.0); - buffer[i][2] = 83 + (unsigned char)(random * 64); - buffer[i][1] = 71 + (unsigned char)(random * 32); - buffer[i][0] = 23 + (unsigned char)(random * 16); - buffer[i][3] = data[i*4+0]; + data[i*4+3] = data[i*4+0]; + data[i*4+2] = 83 + (unsigned char)(random * 64); + data[i*4+1] = 71 + (unsigned char)(random * 32); + data[i*4+0] = 23 + (unsigned char)(random * 16); } // Red chars for (i = 40960;i < 65536;i++) { random = lhrandom (0.0,1.0); - buffer[i][2] = 96 + (unsigned char)(random * 64); - buffer[i][1] = 43 + (unsigned char)(random * 32); - buffer[i][0] = 27 + (unsigned char)(random * 32); - buffer[i][3] = data[i*4+0]; + data[i*4+3] = data[i*4+0]; + data[i*4+2] = 96 + (unsigned char)(random * 64); + data[i*4+1] = 43 + (unsigned char)(random * 32); + data[i*4+0] = 27 + (unsigned char)(random * 32); } #if 0 - Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, &buffer[0][0]); + Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data); #endif + tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, NULL); Mem_Free(data); - return R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, &buffer[0][0], TEXTYPE_BGRA, TEXF_ALPHA | TEXF_PRECACHE, NULL); + return tex; } static rtexture_t *draw_generateditherpattern(void) @@ -109,7 +120,7 @@ static rtexture_t *draw_generateditherpattern(void) for (y = 0;y < 8;y++) for (x = 0;x < 8;x++) pixels[y][x] = ((x^y) & 4) ? 254 : 0; - return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST | TEXF_PRECACHE, palette_bgra_transparent); + return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, palette_bgra_transparent); } typedef struct embeddedpic_s @@ -272,120 +283,6 @@ static const embeddedpic_t embeddedpics[] = ".3............3." "................" }, - { - "gfx/editlights/cursor", 16, 16, - "................" - ".3............3." - "..5...2332...5.." - "...7.3....3.7..." - "....7......7...." - "...3.7....7.3..." - "..2...7..7...2.." - "..3..........3.." - "..3..........3.." - "..2...7..7...2.." - "...3.7....7.3..." - "....7......7...." - "...7.3....3.7..." - "..5...2332...5.." - ".3............3." - "................" - }, - { - "gfx/editlights/light", 16, 16, - "................" - "................" - "......1111......" - "....11233211...." - "...1234554321..." - "...1356776531..." - "..124677776421.." - "..135777777531.." - "..135777777531.." - "..124677776421.." - "...1356776531..." - "...1234554321..." - "....11233211...." - "......1111......" - "................" - "................" - }, - { - "gfx/editlights/noshadow", 16, 16, - "................" - "................" - "......1111......" - "....11233211...." - "...1234554321..." - "...1356226531..." - "..12462..26421.." - "..1352....2531.." - "..1352....2531.." - "..12462..26421.." - "...1356226531..." - "...1234554321..." - "....11233211...." - "......1111......" - "................" - "................" - }, - { - "gfx/editlights/selection", 16, 16, - "................" - ".777752..257777." - ".742........247." - ".72..........27." - ".7............7." - ".5............5." - ".2............2." - "................" - "................" - ".2............2." - ".5............5." - ".7............7." - ".72..........27." - ".742........247." - ".777752..257777." - "................" - }, - { - "gfx/editlights/cubemaplight", 16, 16, - "................" - "................" - "......2772......" - "....27755772...." - "..277533335772.." - "..753333333357.." - "..777533335777.." - "..735775577537.." - "..733357753337.." - "..733337733337.." - "..753337733357.." - "..277537735772.." - "....27777772...." - "......2772......" - "................" - "................" - }, - { - "gfx/editlights/cubemapnoshadowlight", 16, 16, - "................" - "................" - "......2772......" - "....27722772...." - "..2772....2772.." - "..72........27.." - "..7772....2777.." - "..7.27722772.7.." - "..7...2772...7.." - "..7....77....7.." - "..72...77...27.." - "..2772.77.2772.." - "....27777772...." - "......2772......" - "................" - "................" - }, {NULL, 0, 0, NULL} }; @@ -394,13 +291,13 @@ static rtexture_t *draw_generatepic(const char *name, qboolean quiet) const embeddedpic_t *p; for (p = embeddedpics;p->name;p++) if (!strcmp(name, p->name)) - return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA | TEXF_PRECACHE, palette_bgra_embeddedpic); + return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, palette_bgra_embeddedpic); if (!strcmp(name, "gfx/conchars")) return draw_generateconchars(); if (!strcmp(name, "gfx/colorcontrol/ditherpattern")) return draw_generateditherpattern(); if (!quiet) - Con_Printf("Draw_CachePic: failed to load %s\n", name); + Con_DPrintf("Draw_CachePic: failed to load %s\n", name); return r_texture_notexture; } @@ -414,8 +311,8 @@ Draw_CachePic cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags) { int crc, hashkey; + unsigned char *pixels; cachepic_t *pic; - int flags; fs_offset_t lmpsize; unsigned char *lmpdata; char lmpname[MAX_QPATH]; @@ -449,27 +346,30 @@ cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags) return pic; } - flags = TEXF_ALPHA; - if (!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT)) - flags |= TEXF_PRECACHE; + pic->texflags = TEXF_ALPHA; if (!(cachepicflags & CACHEPICFLAG_NOCLAMP)) - flags |= TEXF_CLAMP; + pic->texflags |= TEXF_CLAMP; if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer) - flags |= TEXF_COMPRESS; + pic->texflags |= TEXF_COMPRESS; + + pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT); // load a high quality image from disk if possible - pic->tex = loadtextureimage(drawtexturepool, path, false, flags, true); - if (pic->tex == NULL && !strncmp(path, "gfx/", 4)) + pixels = loadimagepixelsbgra(path, false, true, r_texture_convertsRGB_2d.integer); + if (pixels == NULL && !strncmp(path, "gfx/", 4)) + pixels = loadimagepixelsbgra(path+4, false, true, r_texture_convertsRGB_2d.integer); + if (pixels) { - // compatibility with older versions which did not require gfx/ prefix - pic->tex = loadtextureimage(drawtexturepool, path + 4, false, flags, true); + pic->width = image_width; + pic->height = image_height; + if (!pic->autoload) + pic->tex = R_LoadTexture2D(drawtexturepool, path, image_width, image_height, pixels, TEXTYPE_BGRA, pic->texflags, NULL); } - // if a high quality image was loaded, set the pic's size to match it, just - // in case there's no low quality version to get the size from - if (pic->tex) + else { - pic->width = R_TextureWidth(pic->tex); - pic->height = R_TextureHeight(pic->tex); + pic->autoload = false; + // never compress the fallback images + pic->texflags &= ~TEXF_COMPRESS; } // now read the low quality version (wad or lmp file), and take the pic @@ -487,8 +387,8 @@ cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags) pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216; pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216; // if no high quality replacement image was found, upload the original low quality texture - if (!pic->tex) - pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent); + if (!pixels) + pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent); } Mem_Free(lmpdata); } @@ -503,22 +403,27 @@ cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags) pic->width = 128; pic->height = 128; // if no high quality replacement image was found, upload the original low quality texture - if (!pic->tex) - pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_font); + if (!pixels) + pic->tex = R_LoadTexture2D(drawtexturepool, path, 128, 128, lmpdata, TEXTYPE_PALETTE, pic->texflags, palette_bgra_font); } else { pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216; pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216; // if no high quality replacement image was found, upload the original low quality texture - if (!pic->tex) - pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, flags & ~TEXF_COMPRESS, palette_bgra_transparent); + if (!pixels) + pic->tex = R_LoadTexture2D(drawtexturepool, path, pic->width, pic->height, lmpdata + 8, TEXTYPE_PALETTE, pic->texflags, palette_bgra_transparent); } } - // if it's not found on disk, generate an image - if (pic->tex == NULL) + if (pixels) { + Mem_Free(pixels); + pixels = NULL; + } + else if (pic->tex == NULL) + { + // if it's not found on disk, generate an image pic->tex = draw_generatepic(path, (cachepicflags & CACHEPICFLAG_QUIET) != 0); pic->width = R_TextureWidth(pic->tex); pic->height = R_TextureHeight(pic->tex); @@ -532,6 +437,41 @@ cachepic_t *Draw_CachePic (const char *path) return Draw_CachePic_Flags (path, 0); } +int draw_frame = 1; + +rtexture_t *Draw_GetPicTexture(cachepic_t *pic) +{ + if (pic->autoload && !pic->tex) + { + pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, r_texture_convertsRGB_2d.integer); + if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4)) + pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, r_texture_convertsRGB_2d.integer); + if (pic->tex == NULL) + pic->tex = draw_generatepic(pic->name, true); + } + pic->lastusedframe = draw_frame; + return pic->tex; +} + +void Draw_Frame(void) +{ + int i; + cachepic_t *pic; + static double nextpurgetime; + if (nextpurgetime > realtime) + return; + nextpurgetime = realtime + 0.05; + for (i = 0, pic = cachepics;i < numcachepics;i++, pic++) + { + if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame) + { + R_FreeTexture(pic->tex); + pic->tex = NULL; + } + } + draw_frame++; +} + cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra) { int crc, hashkey; @@ -573,7 +513,7 @@ cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, u pic->height = height; if (pic->tex) R_FreeTexture(pic->tex); - pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, alpha ? TEXF_ALPHA : 0, NULL); + pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0) | TEXF_ALLOWUPDATES, NULL); return pic; } @@ -590,6 +530,7 @@ void Draw_FreePic(const char *picname) if (!strcmp (picname, pic->name) && pic->tex) { R_FreeTexture(pic->tex); + pic->tex = NULL; pic->width = 0; pic->height = 0; return; @@ -597,6 +538,7 @@ void Draw_FreePic(const char *picname) } } +static float snap_to_pixel_x(float x, float roundUpAt); extern int con_linewidth; // to force rewrapping static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) { @@ -607,16 +549,54 @@ static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) fs_offset_t widthbufsize; if(override || !fnt->texpath[0]) + { strlcpy(fnt->texpath, name, sizeof(fnt->texpath)); + // load the cvars when the font is FIRST loaded + fnt->settings.antialias = r_font_antialias.integer; + fnt->settings.hinting = r_font_hinting.integer; + fnt->settings.outline = r_font_postprocess_outline.value; + fnt->settings.blur = r_font_postprocess_blur.value; + fnt->settings.shadowx = r_font_postprocess_shadow_x.value; + fnt->settings.shadowy = r_font_postprocess_shadow_y.value; + fnt->settings.shadowz = r_font_postprocess_shadow_z.value; + } + if(drawtexturepool == NULL) return; // before gl_draw_start, so will be loaded later + if(fnt->ft2) + { + // clear freetype font + Font_UnloadFont(fnt->ft2); + Mem_Free(fnt->ft2); + fnt->ft2 = NULL; + } + + if(fnt->req_face != -1) + { + if(!Font_LoadFont(fnt->texpath, fnt)) + Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath); + } + fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex; if(fnt->tex == r_texture_notexture) { - fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex; - strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile)); + for (i = 0; i < MAX_FONT_FALLBACKS; ++i) + { + if (!fnt->fallbacks[i][0]) + break; + fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex; + if(fnt->tex != r_texture_notexture) + break; + } + if(fnt->tex == r_texture_notexture) + { + fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex; + strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile)); + } + else + dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]); } else dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath); @@ -653,7 +633,19 @@ static void LoadFont(qboolean override, const char *name, dp_font_t *fnt) case '+': case '-': case '.': - fnt->width_of[ch++] = atof(com_token) + extraspacing; + fnt->width_of[ch] = atof(com_token) + extraspacing; + if (fnt->ft2) + { + for (i = 0; i < MAX_FONT_SIZES; ++i) + { + //Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4); + ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i); + if (!map) + break; + map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size); + } + } + ch++; break; default: if(!strcmp(com_token, "extraspacing")) @@ -703,15 +695,54 @@ static dp_font_t *FindFont(const char *title) return NULL; } +static float snap_to_pixel_x(float x, float roundUpAt) +{ + float pixelpos = x * vid.width / vid_conwidth.value; + int snap = (int) pixelpos; + if (pixelpos - snap >= roundUpAt) ++snap; + return ((float)snap * vid_conwidth.value / vid.width); + /* + x = (int)(x * vid.width / vid_conwidth.value); + x = (x * vid_conwidth.value / vid.width); + return x; + */ +} + +static float snap_to_pixel_y(float y, float roundUpAt) +{ + float pixelpos = y * vid.height / vid_conheight.value; + int snap = (int) pixelpos; + if (pixelpos - snap > roundUpAt) ++snap; + return ((float)snap * vid_conheight.value / vid.height); + /* + y = (int)(y * vid.height / vid_conheight.value); + y = (y * vid_conheight.value / vid.height); + return y; + */ +} + static void LoadFont_f(void) { dp_font_t *f; int i; + const char *filelist, *c, *cm; + float sz; + char mainfont[MAX_QPATH]; + if(Cmd_Argc() < 2) { Con_Printf("Available font commands:\n"); for(i = 0; i < MAX_FONTS; ++i) - Con_Printf(" loadfont %s gfx/tgafile\n", dp_fonts[i].title); + Con_Printf(" loadfont %s gfx/tgafile[...] [sizes...]\n", dp_fonts[i].title); + Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n" + "can specify multiple fonts and faces\n" + "Like this: gfx/vera-sans:2,gfx/fallback:1\n" + "to load face 2 of the font gfx/vera-sans and use face 1\n" + "of gfx/fallback as fallback font.\n" + "You can also specify a list of font sizes to load, like this:\n" + "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n" + "In many cases, 8 12 16 24 32 should be a good choice.\n" + ); return; } f = FindFont(Cmd_Argv(1)); @@ -720,7 +751,78 @@ static void LoadFont_f(void) Con_Printf("font function not found\n"); return; } - LoadFont(true, (Cmd_Argc() < 3) ? "gfx/conchars" : Cmd_Argv(2), f); + + if(Cmd_Argc() < 3) + filelist = "gfx/conchars"; + else + filelist = Cmd_Argv(2); + + memset(f->fallbacks, 0, sizeof(f->fallbacks)); + memset(f->fallback_faces, 0, sizeof(f->fallback_faces)); + + // first font is handled "normally" + c = strchr(filelist, ':'); + cm = strchr(filelist, ','); + if(c && (!cm || c < cm)) + f->req_face = atoi(c+1); + else + { + f->req_face = 0; + c = cm; + } + + if(!c || (c - filelist) > MAX_QPATH) + strlcpy(mainfont, filelist, sizeof(mainfont)); + else + { + memcpy(mainfont, filelist, c - filelist); + mainfont[c - filelist] = 0; + } + + for(i = 0; i < MAX_FONT_FALLBACKS; ++i) + { + c = strchr(filelist, ','); + if(!c) + break; + filelist = c + 1; + if(!*filelist) + break; + c = strchr(filelist, ':'); + cm = strchr(filelist, ','); + if(c && (!cm || c < cm)) + f->fallback_faces[i] = atoi(c+1); + else + { + f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index + c = cm; + } + if(!c || (c-filelist) > MAX_QPATH) + { + strlcpy(f->fallbacks[i], filelist, sizeof(mainfont)); + } + else + { + memcpy(f->fallbacks[i], filelist, c - filelist); + f->fallbacks[i][c - filelist] = 0; + } + } + + // for now: by default load only one size: the default size + f->req_sizes[0] = 0; + for(i = 1; i < MAX_FONT_SIZES; ++i) + f->req_sizes[i] = -1; + + if(Cmd_Argc() >= 3) + { + for(i = 0; i < Cmd_Argc()-3; ++i) + { + sz = atof(Cmd_Argv(i+3)); + if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes + f->req_sizes[i] = sz; + } + } + + LoadFont(true, mainfont, f); } /* @@ -736,6 +838,8 @@ static void gl_draw_start(void) numcachepics = 0; memset(cachepichash, 0, sizeof(cachepichash)); + font_start(); + for(i = 0; i < MAX_FONTS; ++i) LoadFont(false, va("gfx/font_%s", dp_fonts[i].title), &dp_fonts[i]); @@ -745,6 +849,8 @@ static void gl_draw_start(void) static void gl_draw_shutdown(void) { + font_shutdown(); + R_FreeTexturePool(&drawtexturepool); numcachepics = 0; @@ -753,11 +859,19 @@ static void gl_draw_shutdown(void) 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); @@ -780,17 +894,18 @@ void GL_Draw_Init (void) void _DrawQ_Setup(void) { + r_viewport_t viewport; if (r_refdef.draw2dstage) return; r_refdef.draw2dstage = true; CHECKGLERROR - qglViewport(r_refdef.view.x, vid.height - (r_refdef.view.y + r_refdef.view.height), r_refdef.view.width, r_refdef.view.height);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_SetupView_Mode_Ortho(0, 0, vid_conwidth.integer, vid_conheight.integer, -10, 100); qglDepthFunc(GL_LEQUAL);CHECKGLERROR qglDisable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces - R_Mesh_Matrix(&identitymatrix); + R_EntityMatrix(&identitymatrix); GL_DepthMask(true); GL_DepthRange(0, 1); @@ -799,8 +914,6 @@ void _DrawQ_Setup(void) GL_Color(1,1,1,1); GL_AlphaTest(false); GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - R_SetupGenericShader(true); } static void _DrawQ_ProcessDrawFlag(int flags) @@ -829,14 +942,13 @@ void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, flo R_Mesh_VertexPointer(floats, 0, 0); R_Mesh_ColorPointer(NULL, 0, 0); R_Mesh_ResetTextureState(); - R_SetupGenericShader(pic != NULL); if (pic) { if (width == 0) width = pic->width; if (height == 0) height = pic->height; - R_Mesh_TexBind(0, R_GetTexture(pic->tex)); + R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1); R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0); #if 1 @@ -857,6 +969,8 @@ void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, flo } #endif } + else + R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); floats[2] = floats[5] = floats[8] = floats[11] = 0; floats[0] = floats[9] = x; @@ -864,7 +978,7 @@ void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, flo floats[3] = floats[6] = x + width; floats[7] = floats[10] = y + height; - R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0); + R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 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) @@ -883,14 +997,13 @@ void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, R_Mesh_VertexPointer(floats, 0, 0); R_Mesh_ColorPointer(NULL, 0, 0); R_Mesh_ResetTextureState(); - R_SetupGenericShader(pic != NULL); if (pic) { if (width == 0) width = pic->width; if (height == 0) height = pic->height; - R_Mesh_TexBind(0, R_GetTexture(pic->tex)); + R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1); R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0); floats[12] = 0.0f;floats[13] = 0.0f; @@ -898,6 +1011,8 @@ void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, floats[16] = 1.0f;floats[17] = 1.0f; floats[18] = 0.0f;floats[19] = 1.0f; } + else + R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); floats[2] = floats[5] = floats[8] = floats[11] = 0; @@ -917,7 +1032,7 @@ void DrawQ_RotPic(float x, float y, cachepic_t *pic, float width, float height, floats[9] = x - cosaf*org_x + cosar*(height-org_y); floats[10] = y - sinaf*org_x + sinar*(height-org_y); - R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0); + R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0); } void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags) @@ -930,7 +1045,7 @@ void DrawQ_Fill(float x, float y, float width, float height, float red, float gr R_Mesh_VertexPointer(floats, 0, 0); R_Mesh_ColorPointer(NULL, 0, 0); R_Mesh_ResetTextureState(); - R_SetupGenericShader(false); + R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); floats[2] = floats[5] = floats[8] = floats[11] = 0; floats[0] = floats[9] = x; @@ -938,7 +1053,7 @@ void DrawQ_Fill(float x, float y, float width, float height, float red, float gr floats[3] = floats[6] = x + width; floats[7] = floats[10] = y + height; - R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0); + R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0); } /// color tag printing @@ -992,13 +1107,51 @@ static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, } } -float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxlen, int *outcolor, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxwidth) +// 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) { - int num, colorindex = STRING_COLOR_DEFAULT; + const char *text_start = text; + int colorindex = STRING_COLOR_DEFAULT; size_t i; float x = 0; - char ch; + 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->scale; + h *= fnt->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; @@ -1008,42 +1161,72 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxl else colorindex = *outcolor; - maxwidth /= fnt->scale; + // 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;i < *maxlen && text[i];i++) + for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;) { - if (text[i] == ' ') + size_t i0 = i; + nextch = ch = u8_getnchar(text, &text, bytes_left); + i = text - text_start; + if (!ch) + break; + if (ch == ' ' && !fontmap) { - if(x + fnt->width_of[(int) ' '] > maxwidth) + 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 += fnt->width_of[(int) ' ']; + } + x += width_of[(int) ' '] * dw; continue; } - if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < *maxlen) + // i points to the char after ^ + if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen) { - ch = text[++i]; - if (ch <= '9' && ch >= '0') // ^[0-9] found + ch = *text; // colors are ascii, so no u8_ needed + if (ch <= '9' && ch >= '0') // ^[0-9] found { colorindex = ch - '0'; - continue; + ++text; + ++i; + continue; } + // i points to the char after ^... + // i+3 points to 3 in ^x123 + // i+3 == *maxlen would mean that char is missing else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found { // building colorindex... - ch = tolower(text[i+1]); + ch = tolower(text[1]); tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12; else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12; else tempcolorindex = 0; if (tempcolorindex) { - ch = tolower(text[i+2]); + ch = tolower(text[2]); if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8; else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8; else tempcolorindex = 0; if (tempcolorindex) { - ch = tolower(text[i+3]); + ch = tolower(text[3]); if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4; else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4; else tempcolorindex = 0; @@ -1051,20 +1234,57 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxl { colorindex = tempcolorindex | 0xf; // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa) - i+=3; + i+=4; + text += 4; continue; } } } } else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second + { i++; + text++; + } i--; } - num = (unsigned char) text[i]; - if(x + fnt->width_of[num] > maxwidth) - break; // oops, can't draw this - x += fnt->width_of[num]; + ch = nextch; + + if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF)) + { + if (ch > 0xE000) + ch -= 0xE000; + if (ch > 0xFF) + continue; + if (fontmap) + map = ft2_oldstyle_map; + prevch = 0; + if(!least_one || i0) // never skip the first character + if(x + width_of[ch] * dw > maxwidth) + { + i = i0; + break; // oops, can't draw this + } + x += width_of[ch] * dw; + } else { + if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP) + { + map = FontMap_FindForChar(fontmap, ch); + if (!map) + { + if (!Font_LoadMapForIndex(ft2, map_index, ch, &map)) + break; + if (!map) + break; + } + } + mapch = ch - map->start; + if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL)) + x += kx * dw; + x += map->glyphs[mapch].advance_x * dw; + //prevmap = map; + prevch = ch; + } } *maxlen = i; @@ -1072,31 +1292,67 @@ float DrawQ_TextWidth_Font_UntilWidth_TrackColors(const char *text, size_t *maxl if (outcolor) *outcolor = colorindex; - return x * fnt->scale; + return x; } -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) +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) { - int num, shadow, colorindex = STRING_COLOR_DEFAULT; + int shadow, colorindex = STRING_COLOR_DEFAULT; size_t i; float x = startx, y, s, t, u, v, thisw; float *av, *at, *ac; float color[4]; int batchcount; - float vertex3f[QUADELEMENTS_MAXQUADS*4*3]; - float texcoord2f[QUADELEMENTS_MAXQUADS*4*2]; - float color4f[QUADELEMENTS_MAXQUADS*4*4]; - int ch; + static float vertex3f[QUADELEMENTS_MAXQUADS*4*3]; + static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2]; + static float color4f[QUADELEMENTS_MAXQUADS*4*4]; + Uchar ch, mapch, nextch; + Uchar prevch = 0; // used for kerning int tempcolorindex; + int map_index = 0; + //ft2_font_map_t *prevmap = NULL; // the previous map + ft2_font_map_t *map = NULL; // the currently used map + ft2_font_map_t *fontmap = NULL; // the font map for the size + float ftbase_y; + const char *text_start = text; + float kx, ky; + ft2_font_t *ft2 = fnt->ft2; + qboolean snap = true; + float pix_x, pix_y; + size_t bytes_left; + float dw, dh; + const float *width_of; int tw, th; tw = R_TextureWidth(fnt->tex); th = R_TextureHeight(fnt->tex); + if (!h) h = w; + if (!h) { + h = w = 1; + snap = false; + } + starty -= (fnt->scale - 1) * h * 0.5; // center w *= fnt->scale; h *= fnt->scale; + 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; + + // draw the font at its baseline when using freetype + //ftbase_x = 0; + ftbase_y = dh * (4.5/6.0); + if (maxlen < 1) maxlen = 1<<30; @@ -1104,65 +1360,93 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max R_Mesh_ColorPointer(color4f, 0, 0); R_Mesh_ResetTextureState(); - R_Mesh_TexBind(0, R_GetTexture(fnt->tex)); + if (!fontmap) + R_Mesh_TexBind(0, fnt->tex); R_Mesh_TexCoordPointer(0, 2, texcoord2f, 0, 0); R_Mesh_VertexPointer(vertex3f, 0, 0); - R_SetupGenericShader(true); + R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1); ac = color4f; at = texcoord2f; av = vertex3f; batchcount = 0; + //ftbase_x = snap_to_pixel_x(ftbase_x); + if(snap) + { + startx = snap_to_pixel_x(startx, 0.4); + starty = snap_to_pixel_y(starty, 0.4); + ftbase_y = snap_to_pixel_y(ftbase_y, 0.3); + } + + pix_x = vid.width / vid_conwidth.value; + pix_y = vid.height / vid_conheight.value; + + if (fontmap) + width_of = fontmap->width_of; + else + width_of = fnt->width_of; + for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--) { + prevch = 0; + text = text_start; + if (!outcolor || *outcolor == -1) colorindex = STRING_COLOR_DEFAULT; else colorindex = *outcolor; - DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow); + DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); x = startx; y = starty; + /* if (shadow) { - x += r_textshadow.value; - y += r_textshadow.value; + x += r_textshadow.value * vid.width / vid_conwidth.value; + y += r_textshadow.value * vid.height / vid_conheight.value; } - for (i = 0;i < maxlen && text[i];i++) + */ + for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;) { - if (text[i] == ' ') + nextch = ch = u8_getnchar(text, &text, bytes_left); + i = text - text_start; + if (!ch) + break; + if (ch == ' ' && !fontmap) { - x += fnt->width_of[(int) ' '] * w; + x += width_of[(int) ' '] * dw; continue; } - if (text[i] == STRING_COLOR_TAG && !ignorecolorcodes && i + 1 < maxlen) + if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen) { - ch = text[++i]; + ch = *text; // colors are ascii, so no u8_ needed if (ch <= '9' && ch >= '0') // ^[0-9] found { colorindex = ch - '0'; - DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow); + DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); + ++text; + ++i; continue; } else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found { // building colorindex... - ch = tolower(text[i+1]); + ch = tolower(text[1]); tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12; else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12; else tempcolorindex = 0; if (tempcolorindex) { - ch = tolower(text[i+2]); + ch = tolower(text[2]); if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8; else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8; else tempcolorindex = 0; if (tempcolorindex) { - ch = tolower(text[i+3]); + ch = tolower(text[3]); if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4; else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4; else tempcolorindex = 0; @@ -1171,59 +1455,173 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max colorindex = tempcolorindex | 0xf; // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa) //Con_Printf("^1colorindex:^7 %x\n", colorindex); - DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow); - i+=3; + DrawQ_GetTextColor(color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0); + i+=4; + text+=4; continue; } } } } else if (ch == STRING_COLOR_TAG) + { i++; + text++; + } i--; } - num = (unsigned char) text[i]; - thisw = fnt->width_of[num]; - // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering - s = (num & 15)*0.0625f + (0.5f / tw); - t = (num >> 4)*0.0625f + (0.5f / th); - u = 0.0625f * thisw - (1.0f / tw); - v = 0.0625f - (1.0f / th); - ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3]; - ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3]; - ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3]; - ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3]; - at[ 0] = s ; at[ 1] = t ; - at[ 2] = s+u ; at[ 3] = t ; - at[ 4] = s+u ; at[ 5] = t+v ; - at[ 6] = s ; at[ 7] = t+v ; - av[ 0] = x ; av[ 1] = y ; av[ 2] = 10; - av[ 3] = x+w*thisw ; av[ 4] = y ; av[ 5] = 10; - av[ 6] = x+w*thisw ; av[ 7] = y+h ; av[ 8] = 10; - av[ 9] = x ; av[10] = y+h ; av[11] = 10; - ac += 16; - at += 8; - av += 12; - batchcount++; - if (batchcount >= QUADELEMENTS_MAXQUADS) + // get the backup + ch = nextch; + // using a value of -1 for the oldstyle map because NULL means uninitialized... + // this way we don't need to rebind fnt->tex for every old-style character + // E000..E0FF: emulate old-font characters (to still have smileys and such available) + if (shadow) + { + x += 1.0/pix_x * r_textshadow.value; + y += 1.0/pix_y * r_textshadow.value; + } + if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF)) { - GL_LockArrays(0, batchcount * 4); - R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0); - GL_LockArrays(0, 0); - batchcount = 0; - ac = color4f; - at = texcoord2f; - av = vertex3f; + if (ch > 0xE000) + ch -= 0xE000; + if (ch > 0xFF) + continue; + if (fontmap) + { + if (map != ft2_oldstyle_map) + { + if (batchcount) + { + // switching from freetype to non-freetype rendering + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); + batchcount = 0; + ac = color4f; + at = texcoord2f; + av = vertex3f; + } + R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1); + map = ft2_oldstyle_map; + } + } + prevch = 0; + //num = (unsigned char) text[i]; + //thisw = fnt->width_of[num]; + thisw = fnt->width_of[ch]; + // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering + s = (ch & 15)*0.0625f + (0.5f / tw); + t = (ch >> 4)*0.0625f + (0.5f / th); + u = 0.0625f * thisw - (1.0f / tw); + v = 0.0625f - (1.0f / th); + ac[ 0] = color[0];ac[ 1] = color[1];ac[ 2] = color[2];ac[ 3] = color[3]; + ac[ 4] = color[0];ac[ 5] = color[1];ac[ 6] = color[2];ac[ 7] = color[3]; + ac[ 8] = color[0];ac[ 9] = color[1];ac[10] = color[2];ac[11] = color[3]; + ac[12] = color[0];ac[13] = color[1];ac[14] = color[2];ac[15] = color[3]; + at[ 0] = s ; at[ 1] = t ; + at[ 2] = s+u ; at[ 3] = t ; + at[ 4] = s+u ; at[ 5] = t+v ; + at[ 6] = s ; at[ 7] = t+v ; + av[ 0] = x ; av[ 1] = y ; av[ 2] = 10; + av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10; + av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10; + av[ 9] = x ; av[10] = y+dh ; av[11] = 10; + ac += 16; + at += 8; + av += 12; + batchcount++; + if (batchcount >= QUADELEMENTS_MAXQUADS) + { + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); + batchcount = 0; + ac = color4f; + at = texcoord2f; + av = vertex3f; + } + x += width_of[ch] * dw; + } else { + if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP) + { + // new charmap - need to render + if (batchcount) + { + // we need a different character map, render what we currently have: + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); + batchcount = 0; + ac = color4f; + at = texcoord2f; + av = vertex3f; + } + // find the new map + map = FontMap_FindForChar(fontmap, ch); + if (!map) + { + if (!Font_LoadMapForIndex(ft2, map_index, ch, &map)) + { + shadow = -1; + break; + } + if (!map) + { + // this shouldn't happen + shadow = -1; + break; + } + } + R_SetupShader_Generic(map->texture, NULL, GL_MODULATE, 1); + } + + mapch = ch - map->start; + thisw = map->glyphs[mapch].advance_x; + + //x += ftbase_x; + y += ftbase_y; + if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky)) + { + x += kx * dw; + y += ky * dh; + } + else + kx = ky = 0; + ac[ 0] = color[0]; ac[ 1] = color[1]; ac[ 2] = color[2]; ac[ 3] = color[3]; + ac[ 4] = color[0]; ac[ 5] = color[1]; ac[ 6] = color[2]; ac[ 7] = color[3]; + ac[ 8] = color[0]; ac[ 9] = color[1]; ac[10] = color[2]; ac[11] = color[3]; + ac[12] = color[0]; ac[13] = color[1]; ac[14] = color[2]; ac[15] = color[3]; + at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin; + at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin; + at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax; + at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax; + av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10; + av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10; + av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10; + av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10; + //x -= ftbase_x; + y -= ftbase_y; + + x += thisw * dw; + ac += 16; + at += 8; + av += 12; + batchcount++; + if (batchcount >= QUADELEMENTS_MAXQUADS) + { + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); + batchcount = 0; + ac = color4f; + at = texcoord2f; + av = vertex3f; + } + + //prevmap = map; + prevch = ch; + } + if (shadow) + { + x -= 1.0/pix_x * r_textshadow.value; + y -= 1.0/pix_y * r_textshadow.value; } - x += thisw * w; } } if (batchcount > 0) - { - GL_LockArrays(0, batchcount * 4); - R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, NULL, quadelements, 0, 0); - GL_LockArrays(0, 0); - } + R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, quadelement3s, 0, 0); if (outcolor) *outcolor = colorindex; @@ -1232,19 +1630,24 @@ float DrawQ_String_Font(float startx, float starty, const char *text, size_t max return x; } -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) +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) +{ + return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt); +} + +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) { - return DrawQ_String_Font(startx, starty, text, maxlen, w, h, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, &dp_fonts[0]); + return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth); } -float DrawQ_TextWidth_Font(const char *text, size_t maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt) +float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt) { - return DrawQ_TextWidth_Font_UntilWidth(text, &maxlen, ignorecolorcodes, fnt, 1000000000); + return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000); } -float DrawQ_TextWidth_Font_UntilWidth(const char *text, size_t *maxlen, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth) +float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth) { - return DrawQ_TextWidth_Font_UntilWidth_TrackColors(text, maxlen, NULL, ignorecolorcodes, fnt, maxWidth); + return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth); } #if 0 @@ -1296,20 +1699,21 @@ void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height R_Mesh_VertexPointer(floats, 0, 0); R_Mesh_ColorPointer(floats + 20, 0, 0); R_Mesh_ResetTextureState(); - R_SetupGenericShader(pic != NULL); if (pic) { if (width == 0) width = pic->width; if (height == 0) height = pic->height; - R_Mesh_TexBind(0, R_GetTexture(pic->tex)); + R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1); R_Mesh_TexCoordPointer(0, 2, floats + 12, 0, 0); floats[12] = s1;floats[13] = t1; floats[14] = s2;floats[15] = t2; floats[16] = s4;floats[17] = t4; floats[18] = s3;floats[19] = t3; } + else + R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); floats[2] = floats[5] = floats[8] = floats[11] = 0; floats[0] = floats[9] = x; @@ -1321,7 +1725,7 @@ void DrawQ_SuperPic(float x, float y, cachepic_t *pic, float width, float height floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4; floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3; - R_Mesh_Draw(0, 4, 0, 2, NULL, polygonelements, 0, 0); + R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0); } void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags) @@ -1331,13 +1735,10 @@ void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags) R_Mesh_VertexPointer(mesh->data_vertex3f, 0, 0); R_Mesh_ColorPointer(mesh->data_color4f, 0, 0); R_Mesh_ResetTextureState(); - R_Mesh_TexBind(0, R_GetTexture(mesh->texture)); R_Mesh_TexCoordPointer(0, 2, mesh->data_texcoord2f, 0, 0); - R_SetupGenericShader(mesh->texture != NULL); + R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1); - GL_LockArrays(0, mesh->num_vertices); - R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, NULL, mesh->data_element3s, 0, 0); - GL_LockArrays(0, 0); + R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, mesh->data_element3s, 0, 0); } void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags) @@ -1364,10 +1765,10 @@ void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, f { _DrawQ_ProcessDrawFlag(flags); - R_SetupGenericShader(false); + R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); CHECKGLERROR - qglLineWidth(width);CHECKGLERROR + //qglLineWidth(width);CHECKGLERROR GL_Color(r,g,b,alpha); CHECKGLERROR @@ -1380,11 +1781,16 @@ void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, f void DrawQ_SetClipArea(float x, float y, float width, float height) { + int ix, iy, iw, ih; _DrawQ_Setup(); // We have to convert the con coords into real coords // OGL uses top to bottom - GL_Scissor((int)(0.5 + x * ((float)vid.width / vid_conwidth.integer)), (int)(0.5 + y * ((float) vid.height / vid_conheight.integer)), (int)(width * ((float)vid.width / vid_conwidth.integer)), (int)(height * ((float)vid.height / vid_conheight.integer))); + ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer)); + iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer)); + iw = (int)(width * ((float)vid.width / vid_conwidth.integer)); + ih = (int)(height * ((float)vid.height / vid_conheight.integer)); + GL_Scissor(ix, vid.height - iy - ih, iw, ih); GL_ScissorTest(true); } @@ -1404,49 +1810,59 @@ static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 1000 void R_DrawGamma(void) { float c[4]; - if (!vid_usinghwgamma && !(r_glsl.integer && v_glslgamma.integer)) + switch(vid.renderpath) { - // all the blends ignore depth - R_Mesh_VertexPointer(blendvertex3f, 0, 0); - R_Mesh_ColorPointer(NULL, 0, 0); - R_Mesh_ResetTextureState(); - R_SetupGenericShader(false); - GL_DepthMask(true); - GL_DepthRange(0, 1); - GL_PolygonOffset(0, 0); - GL_DepthTest(false); - if (v_color_enable.integer) - { - c[0] = v_color_white_r.value; - c[1] = v_color_white_g.value; - c[2] = v_color_white_b.value; - } - else - c[0] = c[1] = c[2] = v_contrast.value; - if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f) - { - GL_BlendFunc(GL_DST_COLOR, GL_ONE); - while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f) - { - GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1); - R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0); - VectorScale(c, 0.5, c); - } - } - if (v_color_enable.integer) - { - c[0] = v_color_black_r.value; - c[1] = v_color_black_g.value; - c[2] = v_color_black_b.value; - } - else - c[0] = c[1] = c[2] = v_brightness.value; - if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f) + case RENDERPATH_GL20: + case RENDERPATH_CGGL: + if (vid_usinghwgamma || v_glslgamma.integer) + return; + break; + case RENDERPATH_GL13: + case RENDERPATH_GL11: + if (vid_usinghwgamma) + return; + break; + } + // all the blends ignore depth + R_Mesh_VertexPointer(blendvertex3f, 0, 0); + R_Mesh_ColorPointer(NULL, 0, 0); + R_Mesh_ResetTextureState(); + R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1); + GL_DepthMask(true); + GL_DepthRange(0, 1); + GL_PolygonOffset(0, 0); + GL_DepthTest(false); + if (v_color_enable.integer) + { + c[0] = v_color_white_r.value; + c[1] = v_color_white_g.value; + c[2] = v_color_white_b.value; + } + else + c[0] = c[1] = c[2] = v_contrast.value; + if (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f) + { + GL_BlendFunc(GL_DST_COLOR, GL_ONE); + while (c[0] >= 1.01f || c[1] >= 1.01f || c[2] >= 1.01f) { - GL_BlendFunc(GL_ONE, GL_ONE); - GL_Color(c[0], c[1], c[2], 1); - R_Mesh_Draw(0, 3, 0, 1, NULL, polygonelements, 0, 0); + GL_Color(bound(0, c[0] - 1, 1), bound(0, c[1] - 1, 1), bound(0, c[2] - 1, 1), 1); + R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0); + VectorScale(c, 0.5, c); } } + if (v_color_enable.integer) + { + c[0] = v_color_black_r.value; + c[1] = v_color_black_g.value; + c[2] = v_color_black_b.value; + } + else + c[0] = c[1] = c[2] = v_brightness.value; + if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f) + { + GL_BlendFunc(GL_ONE, GL_ONE); + GL_Color(c[0], c[1], c[2], 1); + R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, polygonelement3s, 0, 0); + } }