+static float snap_to_pixel_x(float x, float roundUpAt);
+extern int con_linewidth; // to force rewrapping
+void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
+{
+ int i, ch;
+ float maxwidth;
+ char widthfile[MAX_QPATH];
+ char *widthbuf;
+ fs_offset_t widthbufsize;
+
+ if(override || !fnt->texpath[0])
+ {
+ strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
+ // load the cvars when the font is FIRST loader
+ fnt->settings.scale = scale;
+ fnt->settings.voffset = voffset;
+ 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;
+ }
+ // fix bad scale
+ if (fnt->settings.scale <= 0)
+ fnt->settings.scale = 1;
+
+ 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 | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0))->tex;
+ if(fnt->tex == r_texture_notexture)
+ {
+ 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 | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0))->tex;
+ if(fnt->tex != r_texture_notexture)
+ break;
+ }
+ if(fnt->tex == r_texture_notexture)
+ {
+ fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0))->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);
+
+ // unspecified width == 1 (base width)
+ for(ch = 0; ch < 256; ++ch)
+ fnt->width_of[ch] = 1;
+
+ // FIXME load "name.width", if it fails, fill all with 1
+ if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
+ {
+ float extraspacing = 0;
+ const char *p = widthbuf;
+
+ ch = 0;
+ while(ch < 256)
+ {
+ if(!COM_ParseToken_Simple(&p, false, false, true))
+ return;
+
+ switch(*com_token)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ case '.':
+ fnt->width_of[ch] = atof(com_token) + extraspacing;
+ ch++;
+ break;
+ default:
+ if(!strcmp(com_token, "extraspacing"))
+ {
+ if(!COM_ParseToken_Simple(&p, false, false, true))
+ return;
+ extraspacing = atof(com_token);
+ }
+ else if(!strcmp(com_token, "scale"))
+ {
+ if(!COM_ParseToken_Simple(&p, false, false, true))
+ return;
+ fnt->settings.scale = atof(com_token);
+ }
+ else
+ {
+ Con_Printf("Warning: skipped unknown font property %s\n", com_token);
+ if(!COM_ParseToken_Simple(&p, false, false, true))
+ return;
+ }
+ break;
+ }
+ }
+
+ Mem_Free(widthbuf);
+ }
+
+ if(fnt->ft2)
+ {
+ for (i = 0; i < MAX_FONT_SIZES; ++i)
+ {
+ ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
+ if (!map)
+ break;
+ for(ch = 0; ch < 256; ++ch)
+ map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
+ }
+ }
+
+ maxwidth = fnt->width_of[0];
+ for(i = 1; i < 256; ++i)
+ maxwidth = max(maxwidth, fnt->width_of[i]);
+ fnt->maxwidth = maxwidth;
+
+ // fix up maxwidth for overlap
+ fnt->maxwidth *= fnt->settings.scale;
+
+ if(fnt == FONT_CONSOLE)
+ con_linewidth = -1; // rewrap console in next frame
+}
+
+extern cvar_t developer_font;
+dp_font_t *FindFont(const char *title, qboolean allocate_new)
+{
+ int i, oldsize;
+
+ // find font
+ for(i = 0; i < dp_fonts.maxsize; ++i)
+ if(!strcmp(dp_fonts.f[i].title, title))
+ return &dp_fonts.f[i];
+ // if not found - try allocate
+ if (allocate_new)
+ {
+ // find any font with empty title
+ for(i = 0; i < dp_fonts.maxsize; ++i)
+ {
+ if(!strcmp(dp_fonts.f[i].title, ""))
+ {
+ strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
+ return &dp_fonts.f[i];
+ }
+ }
+ // if no any 'free' fonts - expand buffer
+ oldsize = dp_fonts.maxsize;
+ dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
+ if (developer_font.integer)
+ Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
+ dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
+ // relink ft2 structures
+ for(i = 0; i < oldsize; ++i)
+ if (dp_fonts.f[i].ft2)
+ dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
+ // register a font in first expanded slot
+ strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
+ return &dp_fonts.f[oldsize];
+ }
+ 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, sizes;
+ const char *filelist, *c, *cm;
+ float sz, scale, voffset;
+ char mainfont[MAX_QPATH];
+
+ if(Cmd_Argc() < 2)
+ {
+ Con_Printf("Available font commands:\n");
+ for(i = 0; i < dp_fonts.maxsize; ++i)
+ if (dp_fonts.f[i].title[0])
+ Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[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"
+ "custom switches:\n"
+ " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
+ " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
+ );
+ return;
+ }
+ f = FindFont(Cmd_Argv(1), true);
+ if(f == NULL)
+ {
+ Con_Printf("font function not found\n");
+ return;
+ }
+
+ 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;
+
+ scale = 1;
+ voffset = 0;
+ if(Cmd_Argc() >= 4)
+ {
+ for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
+ {
+ // special switches
+ if (!strcmp(Cmd_Argv(i), "scale"))
+ {
+ i++;
+ if (i < Cmd_Argc())
+ scale = atof(Cmd_Argv(i));
+ continue;
+ }
+ if (!strcmp(Cmd_Argv(i), "voffset"))
+ {
+ i++;
+ if (i < Cmd_Argc())
+ voffset = atof(Cmd_Argv(i));
+ continue;
+ }
+
+ if (sizes == -1)
+ continue; // no slot for other sizes
+
+ // parse one of sizes
+ sz = atof(Cmd_Argv(i));
+ if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
+ {
+ // search for duplicated sizes
+ int j;
+ for (j=0; j<sizes; j++)
+ if (f->req_sizes[j] == sz)
+ break;
+ if (j != sizes)
+ continue; // sz already in req_sizes, don't add it again
+
+ if (sizes == MAX_FONT_SIZES)
+ {
+ Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
+ sizes = -1;
+ continue;
+ }
+ f->req_sizes[sizes] = sz;
+ sizes++;
+ }
+ }
+ }
+
+ LoadFont(true, mainfont, f, scale, voffset);
+}
+