From 0a30db49908c1ebe86961af4366fe1b8312c2cb6 Mon Sep 17 00:00:00 2001 From: divverent Date: Fri, 1 Jan 2010 13:35:40 +0000 Subject: [PATCH] patch by Blub and me: - width_of: separate snapped character width array per-size - snap fonts at load time, not at render time (saves on render time) - fix font size snapping to both compare width and height - r_font_kerning cvar git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@9762 d7cf8633-e32d-0410-b094-e92efae38249 --- ft2.c | 81 +++++++++++++++++++++++++++++++++----------------- ft2_fontdefs.h | 3 ++ gl_draw.c | 76 +++++++++++++++++++++++++++++----------------- 3 files changed, 105 insertions(+), 55 deletions(-) diff --git a/ft2.c b/ft2.c index df81aba0..420cc386 100644 --- a/ft2.c +++ b/ft2.c @@ -37,6 +37,7 @@ cvar_t r_font_use_alpha_textures = {CVAR_SAVE, "r_font_use_alpha_textures", "0", 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"}; /* @@ -258,6 +259,7 @@ void Font_Init(void) 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); } @@ -310,6 +312,11 @@ static float Font_VirtualToRealSize(float sz) 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) @@ -572,8 +579,8 @@ static qboolean Font_LoadSize(ft2_font_t *font, float size, qboolean no_texture, } 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); } } } @@ -590,20 +597,26 @@ int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh) 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) @@ -611,7 +624,7 @@ int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh) 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; @@ -623,14 +636,9 @@ int Font_IndexForSize(ft2_font_t *font, float _fsize, float *outw, float *outh) } 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; } @@ -671,7 +679,7 @@ static qboolean Font_SetSize(ft2_font_t *font, float w, float h) 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; @@ -680,9 +688,10 @@ qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h 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 @@ -691,6 +700,7 @@ qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h 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 @@ -701,8 +711,23 @@ qboolean Font_GetKerningForMap(ft2_font_t *font, int map_index, float w, float h 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; @@ -877,6 +902,7 @@ static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ Mem_Free(map); return false; } + memset(map->width_of, 0, sizeof(map->width_of)); // initialize as white texture with zero alpha tp = 0; @@ -1077,9 +1103,9 @@ static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ // 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; @@ -1097,7 +1123,8 @@ static qboolean Font_LoadMap(ft2_font_t *font, ft2_font_map_t *mapstart, Uchar _ 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) diff --git a/ft2_fontdefs.h b/ft2_fontdefs.h index 20726d03..87fba4c5 100644 --- a/ft2_fontdefs.h +++ b/ft2_fontdefs.h @@ -43,6 +43,9 @@ struct ft2_font_map_s ft2_kerning_t kerning; // safes us the trouble of calculating these over and over again double sfx, sfy; + + // the width_of for the image-font, pixel-snapped for this size + float width_of[256]; }; struct ft2_attachment_s diff --git a/gl_draw.c b/gl_draw.c index b53bebce..94358a84 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -532,6 +532,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) { @@ -615,7 +616,13 @@ 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")) @@ -1092,6 +1099,8 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max 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) { @@ -1111,13 +1120,6 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max 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; @@ -1139,6 +1141,20 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max 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; @@ -1146,17 +1162,15 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max 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 ^ @@ -1223,12 +1237,12 @@ float DrawQ_TextWidth_UntilWidth_TrackColors_Scale(const char *text, size_t *max 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) { @@ -1284,6 +1298,8 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma 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); @@ -1307,13 +1323,6 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma 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; @@ -1342,10 +1351,26 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma //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; @@ -1372,14 +1397,9 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma 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) @@ -1503,7 +1523,7 @@ float DrawQ_String_Scale(float startx, float starty, const char *text, size_t ma 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) { -- 2.39.2