cvar_t r_font_size_snapping = {CVAR_SAVE, "r_font_size_snapping", "1", "stick to good looking font sizes whenever possible - bad when the mod doesn't support it!"};
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" */};
+cvar_t r_font_kerning = {CVAR_SAVE, "r_font_kerning", "1", "Use kerning if available"};
cvar_t developer_font = {CVAR_SAVE, "developer_font", "0", "prints debug messages about fonts"};
/*
Cvar_RegisterVariable(&r_font_size_snapping);
Cvar_RegisterVariable(&r_font_hinting);
Cvar_RegisterVariable(&r_font_antialias);
+ Cvar_RegisterVariable(&r_font_kerning);
Cvar_RegisterVariable(&developer_font);
}
return si;
}
+static float Font_SnapTo(float val, float snapwidth)
+{
+ return rint(val / snapwidth) * snapwidth;
+}
+
static qboolean Font_LoadFile(const char *name, int _face, ft2_font_t *font);
static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, qboolean no_kerning);
qboolean Font_LoadFont(const char *name, dp_font_t *dpfnt)
}
else
{
- fmap->kerning.kerning[l][r][0] = (kernvec.x >> 6) / fmap->size;
- fmap->kerning.kerning[l][r][1] = (kernvec.y >> 6) / fmap->size;
+ fmap->kerning.kerning[l][r][0] = Font_SnapTo((kernvec.x / 64.0) / fmap->size, 1 / fmap->size);
+ fmap->kerning.kerning[l][r][1] = Font_SnapTo((kernvec.y / 64.0) / fmap->size, 1 / fmap->size);
}
}
}
int nval;
int matchsize = -10000;
int m;
- int size;
- float fsize;
+ float fsize_x, fsize_y;
ft2_font_map_t **maps = font->font_maps;
- fsize = _fsize * vid.height / vid_conheight.value;
+ fsize_x = fsize_y = _fsize * vid.height / vid_conheight.value;
+ if(outw && *outw)
+ fsize_x = *outw * vid.width / vid_conwidth.value;
+ if(outh && *outh)
+ fsize_y = *outh * vid.height / vid_conheight.value;
- if (fsize < 0)
- size = 16;
+ if (fsize_x < 0)
+ {
+ if(fsize_y < 0)
+ fsize_x = fsize_y = 16;
+ else
+ fsize_x = fsize_y;
+ }
else
{
- // round up
- size = (int)fsize;
- if (fsize - (float)size >= 0.49)
- ++size;
+ if(fsize_y < 0)
+ fsize_y = fsize_x;
}
for (m = 0; m < MAX_FONT_SIZES; ++m)
if (!maps[m])
continue;
// "round up" to the bigger size if two equally-valued matches exist
- nval = abs(maps[m]->size - size);
+ nval = 0.5 * (abs(maps[m]->size - fsize_x) + abs(maps[m]->size - fsize_y));
if (match == -1 || nval < value || (nval == value && matchsize < maps[m]->size))
{
value = nval;
}
if (value <= r_font_size_snapping.value)
{
- if (outw && outh)
- {
- if (!*outh) *outh = *outw;
- if (!*outw) *outw = *outh;
- }
- // keep the aspect
+ // do NOT keep the aspect for perfect rendering
if (outh) *outh = maps[match]->size * vid_conheight.value / vid.height;
- if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width * *outw / _fsize;
+ if (outw) *outw = maps[match]->size * vid_conwidth.value / vid.width;
}
return match;
}
qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h, Uchar left, Uchar right, float *outx, float *outy)
{
ft2_font_map_t *fmap;
- if (!font->has_kerning)
+ if (!font->has_kerning || !r_font_kerning.integer)
return false;
if (map_index < 0 || map_index >= MAX_FONT_SIZES)
return false;
return false;
if (left < 256 && right < 256)
{
+ //Con_Printf("%g : %f, %f, %f :: %f\n", (w / (float)fmap->size), w, fmap->size, fmap->intSize, Font_VirtualToRealSize(w));
// quick-kerning, be aware of the size: scale it
- if (outx) *outx = fmap->kerning.kerning[left][right][0] * w / (float)fmap->size;
- if (outy) *outy = fmap->kerning.kerning[left][right][1] * h / (float)fmap->size;
+ if (outx) *outx = fmap->kerning.kerning[left][right][0];// * (w / (float)fmap->size);
+ if (outy) *outy = fmap->kerning.kerning[left][right][1];// * (h / (float)fmap->size);
return true;
}
else
FT_ULong ul, ur;
//if (qFT_Set_Pixel_Sizes((FT_Face)font->face, 0, fmap->size))
+#if 0
if (!Font_SetSize(font, w, h))
{
// this deserves an error message
ur = qFT_Get_Char_Index(font->face, right);
if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
{
- if (outx) *outx = kernvec.x * fmap->sfx;
- if (outy) *outy = kernvec.y * fmap->sfy;
+ if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);
+ if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);
+ return true;
+ }
+#endif
+ if (!Font_SetSize(font, fmap->intSize, fmap->intSize))
+ {
+ // this deserves an error message
+ Con_Printf("Failed to get kerning for %s\n", font->name);
+ return false;
+ }
+ ul = qFT_Get_Char_Index(font->face, left);
+ ur = qFT_Get_Char_Index(font->face, right);
+ if (qFT_Get_Kerning(font->face, ul, ur, FT_KERNING_DEFAULT, &kernvec))
+ {
+ if (outx) *outx = Font_SnapTo(kernvec.x * fmap->sfx, 1 / fmap->size);// * (w / (float)fmap->size);
+ if (outy) *outy = Font_SnapTo(kernvec.y * fmap->sfy, 1 / fmap->size);// * (h / (float)fmap->size);
return true;
}
return false;
Mem_Free(map);
return false;
}
+ memset(map->width_of, 0, sizeof(map->width_of));
// initialize as white texture with zero alpha
tp = 0;
// old way
// double advance = (double)glyph->metrics.horiAdvance * map->sfx;
- double bearingX = (glyph->metrics.horiBearingX >> 6) / map->size;
+ double bearingX = (glyph->metrics.horiBearingX / 64.0) / map->size;
//double bearingY = (glyph->metrics.horiBearingY >> 6) / map->size;
- double advance = (glyph->advance.x >> 6) / map->size;
+ double advance = (glyph->advance.x / 64.0) / map->size;
//double mWidth = (glyph->metrics.width >> 6) / map->size;
//double mHeight = (glyph->metrics.height >> 6) / map->size;
mapglyph->vymax = mapglyph->vymin + bmp->rows / map->size;
//Con_Printf("dpi = %f %f (%f %d) %d %d\n", bmp->width / (mapglyph->vxmax - mapglyph->vxmin), bmp->rows / (mapglyph->vymax - mapglyph->vymin), map->size, map->glyphSize, (int)fontface->size->metrics.x_ppem, (int)fontface->size->metrics.y_ppem);
//mapglyph->advance_x = advance * usefont->size;
- mapglyph->advance_x = advance;
+ //mapglyph->advance_x = advance;
+ mapglyph->advance_x = Font_SnapTo(advance, 1 / map->size);
mapglyph->advance_y = 0;
if (developer_font.integer)
}
}
+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)
{
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 && fnt->req_sizes[i] >= 0; ++i)
+ Font_MapForIndex(fnt->ft2, i)->width_of[ch] = snap_to_pixel_x(fnt->width_of[ch] * fnt->req_sizes[i], 0.4);
+ }
+ ch++;
break;
default:
if(!strcmp(com_token, "extraspacing"))
qboolean snap = true;
qboolean least_one = false;
float dw, dh; // display w/h
+ float width_of_factor;
+ const float *width_of;
if (!h) h = w;
if (!h) {
map_index = Font_IndexForSize(ft2, h, NULL, NULL);
fontmap = Font_MapForIndex(ft2, map_index);
}
- if(snap)
- {
- if(fabs(sw - 1) > 0.001 || fabs(sh - 1) > 0.001)
- snap = false; // turn off pixel snapping for better animation
- else
- sw = sh = 1;
- }
dw = w * sw;
dh = h * sh;
maxwidth = -maxwidth;
}
+ //if (snap)
+ // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
+
+ if (fontmap)
+ {
+ width_of_factor = 1;
+ width_of = fontmap->width_of;
+ }
+ else
+ {
+ width_of_factor = dw;
+ width_of = fnt->width_of;
+ }
+
for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
{
size_t i0 = i;
i = text - text_start;
if (!ch)
break;
- if (snap)
- x = snap_to_pixel_x(x, 0.4);
if (ch == ' ' && !fontmap)
{
if(!least_one || i0) // never skip the first character
- if(x + fnt->width_of[(int) ' '] * dw > maxwidth)
+ if(x + width_of[(int) ' '] * width_of_factor > maxwidth)
{
i = i0;
break; // oops, can't draw this
}
- x += fnt->width_of[(int) ' '] * dw;
+ x += width_of[(int) ' '] * width_of_factor;
continue;
}
// i points to the char after ^
map = ft2_oldstyle_map;
prevch = 0;
if(!least_one || i0) // never skip the first character
- if(x + fnt->width_of[ch] * dw > maxwidth)
+ if(x + width_of[ch] * width_of_factor > maxwidth)
{
i = i0;
break; // oops, can't draw this
}
- x += fnt->width_of[ch] * dw;
+ x += width_of[ch] * width_of_factor;
} else {
if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch)
{
float pix_x, pix_y;
size_t bytes_left;
float dw, dh;
+ float width_of_factor;
+ const float *width_of;
int tw, th;
tw = R_TextureWidth(fnt->tex);
map_index = Font_IndexForSize(ft2, h, NULL, NULL);
fontmap = Font_MapForIndex(ft2, map_index);
}
- if(snap)
- {
- if(fabs(sw - 1) > 0.001 || fabs(sh - 1) > 0.001)
- snap = false; // turn off pixel snapping for better animation
- else
- sw = sh = 1;
- }
dw = w * sw;
dh = h * sh;
//ftbase_x = snap_to_pixel_x(ftbase_x);
if(snap)
+ {
+ startx = snap_to_pixel_y(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_factor = 1;
+ width_of = fontmap->width_of;
+ }
+ else
+ {
+ width_of_factor = dw;
+ width_of = fnt->width_of;
+ }
+
for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
{
text = text_start;
i = text - text_start;
if (!ch)
break;
- if (snap)
- {
- x = snap_to_pixel_x(x, 0.4);
- y = snap_to_pixel_y(y, 0.4);
- }
if (ch == ' ' && !fontmap)
{
- x += fnt->width_of[(int) ' '] * dw;
+ x += width_of[(int) ' '] * width_of_factor;
continue;
}
if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
at = texcoord2f;
av = vertex3f;
}
- x += thisw * dw;
+ x += width_of[ch] * width_of_factor;
} else {
if (!map || map == ft2_oldstyle_map || map->start < ch || map->start + FONT_CHARS_PER_MAP >= ch)
{