patch by Blub and me:
authordivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 1 Jan 2010 13:35:40 +0000 (13:35 +0000)
committerdivverent <divverent@d7cf8633-e32d-0410-b094-e92efae38249>
Fri, 1 Jan 2010 13:35:40 +0000 (13:35 +0000)
- 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
ft2_fontdefs.h
gl_draw.c

diff --git a/ft2.c b/ft2.c
index df81aba..420cc38 100644 (file)
--- 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)
index 20726d0..87fba4c 100644 (file)
@@ -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
index b53bebc..94358a8 100644 (file)
--- 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)
                                {