Commits

Lenard Lindstrom  committed 80eeccf

freetype: Rename ft_text.c to ft_layout.c. Move underline positioning into ft_render.py. Scale underline thickness for strong style. Multiple a negative underline adjustment with the face's ascender.

  • Participants
  • Parent commits 02f253b

Comments (0)

Files changed (8)

 
 #optional freetype module (do not break in multiple lines
 #or the configuration script will choke!)
-#freetype src/freetype/ft_cache.c src/freetype/ft_wrap.c src/freetype/ft_render.c  src/freetype/ft_render_cb.c src/freetype/ft_text.c src/freetype/ft_unicode.c src/freetype.c $(SDL) $(FREETYPE) $(DEBUG)
+#freetype src/freetype/ft_cache.c src/freetype/ft_wrap.c src/freetype/ft_render.c  src/freetype/ft_render_cb.c src/freetype/ft_layout.c src/freetype/ft_unicode.c src/freetype.c $(SDL) $(FREETYPE) $(DEBUG)
 
 #these modules are required for pygame to run. they only require
 #SDL as a dependency. these should not be altered

File docs/reST/ref/freetype.rst

       | :sl:`Gets or sets an adjustment factor for the underline position`
       | :sg:`underline_adjustment -> float`
 
-      Gets or sets a factor which is multiplied with the face's underline
-      offset to adjust the underline position. Accepted values are between
-      -2.0 and 2.0 inclusive. A negative value can turn an underline into
-      a strikethrough or overline. A value of 1.0 restores the default
-      position.
+      Gets or sets a factor which, when positive, is multiplied with the
+      face's underline offset to adjust the underline position. A negative
+      value turns an underline into a strikethrough or overline. It is
+      multiplied with the ascender. Accepted values are between -2.0 and 2.0
+      inclusive. A value of 0.5 closely matches Tango underlining. A value of
+      1.0 mimics SDL_ttf.
 
    .. attribute:: fixed_width
 

File docs/ref/freetype.html

 <div class="line"><span class="summaryline">Gets or sets an adjustment factor for the underline position</span></div>
 <div class="line"><span class="signature">underline_adjustment -&gt; float</span></div>
 </div>
-<p>Gets or sets a factor which is multiplied with the face&#8217;s underline
-offset to adjust the underline position. Accepted values are between
--2.0 and 2.0 inclusive. A negative value can turn an underline into
-a strikethrough or overline. A value of 1.0 restores the default
-position.</p>
+<p>Gets or sets a factor which, when positive, is multiplied with the
+face&#8217;s underline offset to adjust the underline position. A negative
+value turns an underline into a strikethrough or overline. It is
+multiplied with the ascender. Accepted values are between -2.0 and 2.0
+inclusive. A value of 0.5 closely matches Tango underlining. A value of
+1.0 mimics SDL_ttf.</p>
 </dd></dl>
 
 <dl class="definition attribute">

File src/freetype/ft_layout.c

+/*
+  pygame - Python Game Library
+  Copyright (C) 2009 Vicent Marti
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Library General Public
+  License as published by the Free Software Foundation; either
+  version 2 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Library General Public License for more details.
+
+  You should have received a copy of the GNU Library General Public
+  License along with this library; if not, write to the Free
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+#define PYGAME_FREETYPE_INTERNAL
+
+#include "ft_wrap.h"
+#include FT_MODULE_H
+#include FT_TRIGONOMETRY_H
+#include FT_OUTLINE_H
+#include FT_BITMAP_H
+#include FT_CACHE_H
+
+/* Multiply the face's x ppem by this factor to get the x strength
+ * factor in 16.16 Fixed.
+ */
+#define FX16_WIDE_FACTOR (FX16_ONE / 12)
+
+#define SLANT_FACTOR    0.22
+static FT_Matrix slant_matrix = {
+    FX16_ONE,  (FT_Fixed)(SLANT_FACTOR * FX16_ONE),
+    0,         FX16_ONE
+};
+
+static FT_Matrix unit_matrix = {
+    FX16_ONE,  0,
+    0,         FX16_ONE
+};
+
+typedef struct textcontext_ {
+    FT_Library lib;
+    FTC_FaceID id;
+    FT_Face face;
+    FTC_CMapCache charmap;
+    int do_transform;
+    FT_Matrix transform;
+} TextContext;
+
+#if 0
+#define BOLD_STRENGTH_D (0.65)
+#define PIXEL_SIZE ((FT_Fixed)64)
+#define BOLD_STRENGTH ((FT_Fixed)(BOLD_STRENGTH_D * PIXEL_SIZE))
+#define BOLD_ADVANCE (BOLD_STRENGTH * (FT_Fixed)4)
+#endif
+#define FX16_BOLD_FACTOR (FX16_ONE / 36)
+#define UNICODE_SPACE ((PGFT_char)' ')
+
+static FT_UInt32 get_load_flags(const FaceRenderMode *);
+static void fill_metrics(FaceMetrics *, FT_Pos, FT_Pos,
+                         FT_Vector *, FT_Vector *);
+static void fill_context(TextContext *,
+                         const FreeTypeInstance *,
+                         const PgFaceObject *,
+                         const FaceRenderMode *,
+                         const FT_Face);
+
+int
+_PGFT_FaceTextInit(FreeTypeInstance *ft, PgFaceObject *faceobj)
+{
+    FaceText *ftext = &(PGFT_INTERNALS(faceobj)->active_text);
+
+    ftext->buffer_size = 0;
+    ftext->glyphs = 0;
+    ftext->posns = 0;
+
+    if (_PGFT_Cache_Init(ft, &ftext->glyph_cache)) {
+        PyErr_NoMemory();
+        return -1;
+    }
+
+    return 0;
+}
+
+void
+_PGFT_FaceTextFree(PgFaceObject *faceobj)
+{
+    FaceText *ftext = &(PGFT_INTERNALS(faceobj)->active_text);
+
+    if (ftext->buffer_size > 0) {
+        _PGFT_free(ftext->glyphs);
+        _PGFT_free(ftext->posns);
+    }
+    _PGFT_Cache_Destroy(&ftext->glyph_cache);
+}
+
+FaceText *
+_PGFT_LoadFaceText(FreeTypeInstance *ft, PgFaceObject *faceobj,
+                  const FaceRenderMode *mode, PGFT_String *text)
+{
+    Py_ssize_t  string_length = PGFT_String_GET_LENGTH(text);
+
+    PGFT_char * buffer = PGFT_String_GET_DATA(text);
+    PGFT_char * buffer_end;
+    PGFT_char * ch;
+
+    FaceText    *ftext = &(PGFT_INTERNALS(faceobj)->active_text);
+    FaceGlyph   *glyph = 0;
+    FaceGlyph   **glyph_array = 0;
+    FaceMetrics *metrics;
+    FT_BitmapGlyph image;
+    TextContext context;
+
+    FT_Face     face;
+    FT_Size_Metrics *sz_metrics;
+
+    FT_Vector   pen = {0, 0};                /* untransformed origin  */
+    FT_Vector   pen1 = {0, 0};
+    FT_Vector   pen2;
+
+    FT_Vector   *next_pos;
+
+    int         vertical = mode->render_flags & FT_RFLAG_VERTICAL;
+    int         use_kerning = mode->render_flags & FT_RFLAG_KERNING;
+    int         pad = mode->render_flags & FT_RFLAG_PAD;
+    FT_UInt     prev_glyph_index = 0;
+
+    /* All these are 16.16 precision */
+    FT_Angle    rotation_angle = mode->rotation_angle;
+
+    /* All these are 26.6 precision */
+    FT_Vector   kerning;
+    FT_Pos      min_x = FX6_MAX;
+    FT_Pos      max_x = FX6_MIN;
+    FT_Pos      min_y = FX6_MAX;
+    FT_Pos      max_y = FX6_MIN;
+    FT_Pos      glyph_width;
+    FT_Pos      glyph_height;
+    FT_Pos      top = FX6_MIN;
+    FT_Fixed    y_scale;
+
+    FT_Error    error = 0;
+
+    /* load our sized face */
+    face = _PGFT_GetFaceSized(ft, faceobj, mode->pt_size);
+    if (!face) {
+        PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft));
+        return 0;
+    }
+    sz_metrics = &face->size->metrics;
+    y_scale = sz_metrics->y_scale;
+
+    /* cleanup the cache */
+    _PGFT_Cache_Cleanup(&ftext->glyph_cache);
+
+    /* create the text struct */
+    if (string_length > ftext->buffer_size) {
+        _PGFT_free(ftext->glyphs);
+        ftext->glyphs = (FaceGlyph **)
+            _PGFT_malloc((size_t)string_length * sizeof(FaceGlyph *));
+        if (!ftext->glyphs) {
+            PyErr_NoMemory();
+            return 0;
+        }
+
+        _PGFT_free(ftext->posns);
+        ftext->posns = (FT_Vector *)
+        _PGFT_malloc((size_t)string_length * sizeof(FT_Vector));
+        if (!ftext->posns) {
+            PyErr_NoMemory();
+            return 0;
+        }
+        ftext->buffer_size = string_length;
+    }
+    ftext->length = string_length;
+    ftext->ascender = sz_metrics->ascender;
+    ftext->underline_pos = -FT_MulFix(face->underline_position, y_scale);
+    ftext->underline_size = FT_MulFix(face->underline_thickness, y_scale);
+    if (mode->style & FT_STYLE_STRONG) {
+        FT_Fixed bold_str = mode->strength * sz_metrics->x_ppem;
+
+        ftext->underline_size = FT_MulFix(ftext->underline_size,
+                                          FX16_ONE + bold_str / 4);
+    }
+
+    /* fill it with the glyphs */
+    fill_context(&context, ft, faceobj, mode, face);
+    glyph_array = ftext->glyphs;
+    next_pos = ftext->posns;
+
+    for (ch = buffer, buffer_end = ch + string_length; ch < buffer_end; ++ch) {
+        pen2.x = pen1.x;
+        pen2.y = pen1.y;
+        pen1.x = pen.x;
+        pen1.y = pen.y;
+        /*
+         * Load the corresponding glyph from the cache
+         */
+        glyph = _PGFT_Cache_FindGlyph(*((FT_UInt32 *)ch), mode,
+                                     &ftext->glyph_cache, &context);
+
+        if (!glyph) {
+            --ftext->length;
+            continue;
+        }
+        image = glyph->image;
+        glyph_width = glyph->width;
+        glyph_height = glyph->height;
+
+        /*
+         * Do size calculations for all the glyphs in the text
+         */
+        if (use_kerning && prev_glyph_index) {
+            error = FT_Get_Kerning(face, prev_glyph_index,
+                                   glyph->glyph_index,
+                                   FT_KERNING_UNFITTED, &kerning);
+            if (error) {
+                _PGFT_SetError(ft, "Loading glyphs", error);
+                PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft));
+                return 0;
+            }
+            if (rotation_angle != 0) {
+                FT_Vector_Rotate(&kerning, rotation_angle);
+            }
+            pen.x += FX6_ROUND(kerning.x);
+            pen.y += FX6_ROUND(kerning.y);
+            if (FT_Vector_Length(&pen2) > FT_Vector_Length(&pen)) {
+                pen.x = pen2.x;
+                pen.y = pen2.y;
+            }
+        }
+
+        prev_glyph_index = glyph->glyph_index;
+	metrics = vertical ? &glyph->v_metrics : &glyph->h_metrics;
+	if (metrics->bearing_rotated.y > top) {
+            top = metrics->bearing_rotated.y;
+        }
+        if (pen.x + metrics->bearing_rotated.x < min_x) {
+            min_x = pen.x + metrics->bearing_rotated.x;
+        }
+        if (pen.x + metrics->bearing_rotated.x + glyph_width > max_x) {
+            max_x = pen.x + metrics->bearing_rotated.x + glyph_width;
+        }
+        next_pos->x = pen.x + metrics->bearing_rotated.x;
+        pen.x += metrics->advance_rotated.x;
+        if (vertical) {
+            if (pen.y + metrics->bearing_rotated.y < min_y) {
+                min_y = pen.y + metrics->bearing_rotated.y;
+            }
+            if (pen.y + metrics->bearing_rotated.y + glyph_height > max_y) {
+                max_y = pen.y + metrics->bearing_rotated.y + glyph_height;
+            }
+            next_pos->y = pen.y + metrics->bearing_rotated.y;
+            pen.y += metrics->advance_rotated.y;
+        }
+        else {
+            if (pen.y - metrics->bearing_rotated.y < min_y) {
+                min_y = pen.y - metrics->bearing_rotated.y;
+            }
+            if (pen.y - metrics->bearing_rotated.y + glyph_height > max_y) {
+                max_y = pen.y - metrics->bearing_rotated.y + glyph_height;
+            }
+            next_pos->y = pen.y - metrics->bearing_rotated.y;
+            pen.y -= metrics->advance_rotated.y;
+        }
+        *glyph_array++ = glyph;
+        ++next_pos;
+    }
+
+    if (ftext->length == 0) {
+        min_x = 0;
+        max_x = 0;
+        if (vertical) {
+            ftext->min_y = 0;
+            max_y = sz_metrics->height;
+        }
+        else {
+            FT_Size_Metrics *sz_metrics = &face->size->metrics;
+
+            min_y = -sz_metrics->ascender;
+            max_y = -sz_metrics->descender;
+        }
+    }
+
+    if (pad) {
+        FT_Size_Metrics *sz_metrics = &face->size->metrics;
+
+        if (pen.x > max_x) {
+            max_x = pen.x;
+        }
+        else if (pen.x < min_x) {
+            min_x = pen.x;
+        }
+        if (pen.y > max_y) {
+            max_y = pen.y;
+        }
+        else if (pen.y < min_y) {
+            min_y = pen.y;
+        }
+        if (vertical) {
+            FT_Fixed right = sz_metrics->max_advance / 2;
+
+            if (max_x < right) {
+                max_x = right;
+            }
+            if (min_x > -right) {
+                min_x = -right;
+            }
+            if (min_y > 0) {
+                min_y = 0;
+            }
+            else if (max_y < pen.y) {
+                max_y = pen.y;
+            }
+        }
+        else {
+            FT_Fixed ascender = sz_metrics->ascender;
+            FT_Fixed descender = sz_metrics->descender;
+
+            if (min_x > 0) {
+                min_x = 0;
+            }
+            if (max_x < pen.x) {
+                max_x = pen.x;
+            }
+            if (min_y > -ascender) {
+                min_y = -ascender;
+            }
+            if (max_y <= -descender) {
+                max_y = -descender + /* baseline */ FX6_ONE;
+            }
+        }
+    }
+
+    ftext->left = FX6_TRUNC(FX6_FLOOR(min_x));
+    ftext->top = FX6_TRUNC(FX6_CEIL(top));
+    ftext->min_x = min_x;
+    ftext->max_x = max_x;
+    ftext->min_y = min_y;
+    ftext->max_y = max_y;
+    ftext->advance.x = pen.x;
+    ftext->advance.y = pen.y;
+
+    return ftext;
+}
+
+int _PGFT_GetMetrics(FreeTypeInstance *ft, PgFaceObject *faceobj,
+                    PGFT_char character, const FaceRenderMode *mode,
+                    FT_UInt *gindex, long *minx, long *maxx,
+                    long *miny, long *maxy,
+                    double *advance_x, double *advance_y)
+{
+    FaceText    *ftext = &(PGFT_INTERNALS(faceobj)->active_text);
+    FaceGlyph *glyph = 0;
+    TextContext context;
+    FT_Face     face;
+
+    /* load our sized face */
+    face = _PGFT_GetFaceSized(ft, faceobj, mode->pt_size);
+    if (!face) {
+        return -1;
+    }
+
+    /* cleanup the cache */
+    _PGFT_Cache_Cleanup(&ftext->glyph_cache);
+
+    fill_context(&context, ft, faceobj, mode, face);
+    glyph = _PGFT_Cache_FindGlyph(character, mode,
+                                 &PGFT_INTERNALS(faceobj)->active_text.glyph_cache,
+                                 &context);
+
+    if (!glyph) {
+        return -1;
+    }
+
+    *gindex = glyph->glyph_index;
+    *minx = (long)glyph->image->left;
+    *maxx = (long)(glyph->image->left + glyph->image->bitmap.width);
+    *maxy = (long)glyph->image->top;
+    *miny = (long)(glyph->image->top - glyph->image->bitmap.rows);
+    *advance_x = (double)(glyph->h_metrics.advance_rotated.x / 64.0);
+    *advance_y = (double)(glyph->h_metrics.advance_rotated.y / 64.0);
+
+    return 0;
+}
+
+int
+_PGFT_LoadGlyph(FaceGlyph *glyph, PGFT_char character,
+                const FaceRenderMode *mode, void *internal)
+{
+    static FT_Vector delta = {0, 0};
+
+    FT_Render_Mode rmode = (mode->render_flags & FT_RFLAG_ANTIALIAS ?
+                            FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
+    FT_Vector strong_delta = {0, 0};
+    FT_Glyph image = 0;
+
+    FT_Glyph_Metrics *ft_metrics;
+    TextContext *context = (TextContext *)internal;
+
+    FT_UInt32 load_flags;
+    FT_UInt gindex;
+
+    FT_Fixed rotation_angle = mode->rotation_angle;
+    /* FT_Matrix transform; */
+    FT_Vector h_bearing_rotated;
+    FT_Vector v_bearing_rotated;
+    FT_Vector h_advance_rotated;
+    FT_Vector v_advance_rotated;
+
+    FT_Error error = 0;
+
+    /*
+     * Calculate the corresponding glyph index for the char
+     */
+    gindex = FTC_CMapCache_Lookup(context->charmap, context->id,
+                                  -1, (FT_UInt32)character);
+
+    glyph->glyph_index = gindex;
+
+    /*
+     * Get loading information
+     */
+    load_flags = get_load_flags(mode);
+
+    /*
+     * Load the glyph into the glyph slot
+     */
+    if (FT_Load_Glyph(context->face, glyph->glyph_index, (FT_Int)load_flags) ||
+        FT_Get_Glyph(context->face->glyph, &image))
+        goto cleanup;
+
+    /*
+     * Perform any outline transformations
+     */
+    if (mode->style & FT_STYLE_STRONG) {
+        FT_UShort x_ppem = context->face->size->metrics.x_ppem;
+        FT_Fixed bold_str;
+        FT_BBox before;
+        FT_BBox after;
+
+        bold_str = FX16_CEIL_TO_FX6(mode->strength * x_ppem);
+        FT_Outline_Get_CBox(&((FT_OutlineGlyph)image)->outline, &before);
+        if (FT_Outline_Embolden(&((FT_OutlineGlyph)image)->outline, bold_str))
+            goto cleanup;
+        FT_Outline_Get_CBox(&((FT_OutlineGlyph)image)->outline, &after);
+        strong_delta.x += ((after.xMax - after.xMin) -
+                           (before.xMax - before.xMin));
+        strong_delta.y += ((after.yMax - after.yMin) -
+                           (before.yMax - before.yMin));
+    }
+
+    if (context->do_transform) {
+        if (FT_Glyph_Transform(image, &context->transform, &delta)) {
+            goto cleanup;
+        }
+    }
+
+    /*
+     * Finished with outline transformations, now replace with a bitmap
+     */
+    error = FT_Glyph_To_Bitmap(&image, rmode, 0, 1);
+    if (error) {
+        goto cleanup;
+    }
+
+    if (mode->style & FT_STYLE_WIDE) {
+        FT_Bitmap *bitmap = &((FT_BitmapGlyph)image)->bitmap;
+        int w = bitmap->width;
+        FT_UShort x_ppem = context->face->size->metrics.x_ppem;
+        FT_Pos x_strength;
+
+        x_strength = FX16_CEIL_TO_FX6(mode->strength * x_ppem);
+
+        /* FT_Bitmap_Embolden returns an error for a zero width bitmap */
+        if (w > 0) {
+            error = FT_Bitmap_Embolden(context->lib, bitmap,
+                                       x_strength, (FT_Pos)0);
+            if (error) {
+                goto cleanup;
+            }
+            strong_delta.x += INT_TO_FX6(bitmap->width - w);
+        }
+        else {
+            strong_delta.x += x_strength;
+        }
+    }
+
+    /* Fill the glyph */
+    ft_metrics = &context->face->glyph->metrics;
+
+    h_advance_rotated.x = ft_metrics->horiAdvance + strong_delta.x;
+    h_advance_rotated.y = 0;
+    v_advance_rotated.x = 0;
+    v_advance_rotated.y = ft_metrics->vertAdvance + strong_delta.y;
+    if (rotation_angle != 0) {
+        FT_Angle counter_rotation = INT_TO_FX6(360) - rotation_angle;
+
+        FT_Vector_Rotate(&h_advance_rotated, rotation_angle);
+        FT_Vector_Rotate(&v_advance_rotated, counter_rotation);
+    }
+
+    glyph->image = (FT_BitmapGlyph)image;
+    glyph->width = INT_TO_FX6(glyph->image->bitmap.width);
+    glyph->height = INT_TO_FX6(glyph->image->bitmap.rows);
+    h_bearing_rotated.x = INT_TO_FX6(glyph->image->left);
+    h_bearing_rotated.y = INT_TO_FX6(glyph->image->top);
+    fill_metrics(&glyph->h_metrics,
+                 ft_metrics->horiBearingX,
+                 ft_metrics->horiBearingY,
+                 &h_bearing_rotated, &h_advance_rotated);
+
+    if (rotation_angle == 0) {
+        v_bearing_rotated.x = ft_metrics->vertBearingX - strong_delta.x / 2;
+        v_bearing_rotated.y = ft_metrics->vertBearingY;
+    }
+    else {
+        /*
+         * Adjust the vertical metrics.
+         */
+        FT_Vector v_origin;
+
+        v_origin.x = (glyph->h_metrics.bearing_x -
+                      ft_metrics->vertBearingX + strong_delta.x / 2);
+        v_origin.y = (glyph->h_metrics.bearing_y +
+                      ft_metrics->vertBearingY);
+        FT_Vector_Rotate(&v_origin, rotation_angle);
+        v_bearing_rotated.x = glyph->h_metrics.bearing_rotated.x - v_origin.x;
+        v_bearing_rotated.y = v_origin.y - glyph->h_metrics.bearing_rotated.y;
+    }
+    fill_metrics(&glyph->v_metrics,
+                 ft_metrics->vertBearingX,
+                 ft_metrics->vertBearingY,
+                 &v_bearing_rotated, &v_advance_rotated);
+
+    return 0;
+
+    /*
+     * Cleanup on error
+     */
+cleanup:
+    if (image) {
+        FT_Done_Glyph(image);
+    }
+
+    return -1;
+}
+
+static void
+fill_context(TextContext *context,
+             const FreeTypeInstance *ft,
+             const PgFaceObject *faceobj,
+             const FaceRenderMode *mode,
+             const FT_Face face)
+{
+    context->lib = ft->library;
+    context->id = (FTC_FaceID)&(faceobj->id);
+    context->face = face;
+    context->charmap = ft->cache_charmap;
+    context->do_transform = 0;
+
+    if (mode->style & FT_STYLE_OBLIQUE) {
+        context->transform = slant_matrix;
+        context->do_transform = 1;
+    }
+    else {
+        context->transform = unit_matrix;
+    }
+
+    if (mode->render_flags & FT_RFLAG_TRANSFORM) {
+        FT_Matrix_Multiply(&mode->transform, &context->transform);
+        context->do_transform = 1;
+    }
+
+    if (mode->rotation_angle != 0) {
+        FT_Vector unit;
+        FT_Matrix rotate;
+
+        FT_Vector_Unit(&unit, mode->rotation_angle);
+        rotate.xx = unit.x;  /*  cos(angle) */
+        rotate.xy = -unit.y; /* -sin(angle) */
+        rotate.yx = unit.y;  /*  sin(angle) */
+        rotate.yy = unit.x;  /*  cos(angle) */
+        FT_Matrix_Multiply(&rotate, &context->transform);
+        context->do_transform = 1;
+    }
+}
+
+static void
+fill_metrics(FaceMetrics *metrics,
+             FT_Pos bearing_x, FT_Pos bearing_y,
+             FT_Vector *bearing_rotated,
+             FT_Vector *advance_rotated)
+{
+    metrics->bearing_x = bearing_x;
+    metrics->bearing_y = bearing_y;
+    metrics->bearing_rotated.x = bearing_rotated->x;
+    metrics->bearing_rotated.y = bearing_rotated->y;
+    metrics->advance_rotated.x = advance_rotated->x;
+    metrics->advance_rotated.y = advance_rotated->y;
+}
+
+static FT_UInt32
+get_load_flags(const FaceRenderMode *mode)
+{
+    FT_UInt32 load_flags = FT_LOAD_DEFAULT;
+
+    load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+
+    if (mode->render_flags & FT_RFLAG_AUTOHINT) {
+        load_flags |= FT_LOAD_FORCE_AUTOHINT;
+    }
+
+    if (mode->render_flags & FT_RFLAG_HINTED) {
+        load_flags |= FT_LOAD_TARGET_NORMAL;
+    }
+    else {
+        load_flags |= FT_LOAD_NO_HINTING;
+    }
+
+    return load_flags;
+}

File src/freetype/ft_render.c

 #include FT_OUTLINE_H
 
 static void render(FreeTypeInstance *, FaceText *, const FaceRenderMode *,
-                   FaceColor *, FaceSurface *);
+                   FaceColor *, FaceSurface *, unsigned, unsigned, FT_Vector *,
+                   FT_Pos, FT_Fixed);
 
 int
 _PGFT_CheckStyle(FT_UInt32 style)
         mode->style = (FT_UInt16)style;
     }
     mode->strength = DBL_TO_FX16(faceobj->strength);
-
+    mode->underline_adjustment = DBL_TO_FX16(faceobj->underline_adjustment);
     mode->render_flags = faceobj->render_flags;
     angle = rotation % 360;
     while (angle < 0) angle += 360;
     return 0;
 }
 
+void
+_PGFT_GetRenderMetrics(const FaceRenderMode *mode, FaceText *text,
+                       unsigned *w, unsigned *h, FT_Vector *offset,
+                       FT_Pos *underline_top, FT_Fixed *underline_size)
+{
+    FT_Pos min_x = text->min_x;
+    FT_Pos max_x = text->max_x;
+    FT_Pos min_y = text->min_y;
+    FT_Pos max_y = text->max_y;
+
+    *underline_size = 0;
+    if (mode->style & FT_STYLE_UNDERLINE) {
+        FT_Fixed half_size = (text->underline_size + 1) / 2;
+        FT_Fixed adjusted_pos;
+
+        if (mode->underline_adjustment < 0) {
+            adjusted_pos = FT_MulFix(text->ascender,
+                                     mode->underline_adjustment);
+        }
+        else {
+            adjusted_pos = FT_MulFix(text->underline_pos,
+                                     mode->underline_adjustment);
+        }
+        if (adjusted_pos + half_size > max_y) {
+            max_y = adjusted_pos + half_size;
+        }
+        if (adjusted_pos - half_size < min_y) {
+            min_y = adjusted_pos - half_size;
+        }
+        *underline_size = text->underline_size;
+        *underline_top = adjusted_pos - half_size;
+    }
+
+    offset->x = -min_x;
+    offset->y = -min_y;
+    *w = (unsigned)FX6_TRUNC(FX6_CEIL(max_x) - FX6_FLOOR(min_x));
+    *h = (unsigned)FX6_TRUNC(FX6_CEIL(max_y) - FX6_FLOOR(min_y));
+}
+
 
 /*********************************************************
  *
     };
 
     int locked = 0;
-    int width, height;
+    unsigned width;
+    unsigned height;
+    FT_Vector offset;
+    FT_Vector surf_offset;
+    FT_Pos underline_top;
+    FT_Fixed underline_size;
 
     FaceSurface font_surf;
     FaceText *font_text;
         locked = 1;
     }
 
-    /* build font text */
+    /* build face text */
     font_text = _PGFT_LoadFaceText(ft, faceobj, mode, text);
-
     if (!font_text) {
         if (locked) {
             SDL_UnlockSurface(surface);
         return -1;
     }
 
-    width = font_text->width;
-    height = font_text->height;
-    if (_PGFT_GetSurfaceSize(ft, faceobj, mode, font_text, &width, &height)) {
-        if (locked) {
-            SDL_UnlockSurface(surface);
-        }
-        return -1;
-    }
-
-    if (width <= 0 || height <= 0) {
+    _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset,
+                           &underline_top, &underline_size);
+    if (width == 0 || height == 0) {
         /* Nothing more to do. */
         if (locked) {
             SDL_UnlockSurface(surface);
         r->h = _PGFT_Face_GetHeightSized(ft, faceobj, mode->pt_size);
         return 0;
     }
+    surf_offset.x = INT_TO_FX6(x);
+    surf_offset.y = INT_TO_FX6(y);
+    if (mode->render_flags & FT_RFLAG_ORIGIN) {
+        x -= FX6_TRUNC(FX6_CEIL(offset.x));
+        y -= FX6_TRUNC(FX6_CEIL(offset.y));
+    }
+    else {
+        surf_offset.x += offset.x;
+        surf_offset.y += offset.y;
+    }
 
     /*
      * Setup target surface struct
      */
     font_surf.buffer = surface->pixels;
-    font_surf.offset.x = INT_TO_FX6(x);
-    font_surf.offset.y = INT_TO_FX6(y);
-    if (mode->render_flags & FT_RFLAG_ORIGIN) {
-        x -= FX6_TRUNC(FX6_CEIL(font_text->offset.x));
-        y -= FX6_TRUNC(FX6_CEIL(font_text->offset.y));
-    }
-    else {
-        font_surf.offset.x += font_text->offset.x;
-        font_surf.offset.y += font_text->offset.y;
-    }
-
     font_surf.width = surface->w;
     font_surf.height = surface->h;
     font_surf.pitch = surface->pitch;
-
     font_surf.format = surface->format;
-
     font_surf.render_gray = __SDLrenderFuncs[surface->format->BytesPerPixel];
     font_surf.render_mono = __MONOrenderFuncs[surface->format->BytesPerPixel];
     font_surf.fill = __RGBfillFuncs[surface->format->BytesPerPixel];
     /*
      * Render!
      */
-    render(ft, font_text, mode, fgcolor, &font_surf);
+    render(ft, font_text, mode, fgcolor, &font_surf,
+           width, height, &surf_offset, underline_top, underline_size);
 
-    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(font_text->offset.x));
-    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(font_text->offset.y));
+    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x));
+    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y));
     r->w = (Uint16)width;
     r->h = (Uint16)height;
 
 
     FaceSurface font_surf;
     FaceText *font_text;
-    int width, height;
+    unsigned width;
+    unsigned height;
+    FT_Vector offset;
+    FT_Pos underline_top;
+    FT_Fixed underline_size;
     FaceColor mono_fgcolor = {0, 0, 0, 1};
     FaceColor mono_bgcolor = {0, 0, 0, 0};
 
     /* build font text */
     font_text = _PGFT_LoadFaceText(ft, faceobj, mode, text);
-
     if (!font_text) {
         return 0;
     }
 
     if (font_text->length > 0) {
-        width = font_text->width;
-        height = font_text->height;
+        _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset,
+                               &underline_top, &underline_size);
     }
     else {
         width = 1;
         height = _PGFT_Face_GetHeightSized(ft, faceobj, mode->pt_size);
+        offset.x = -font_text->min_x;
+        offset.y = -font_text->min_y;
     }
 
     surface = SDL_CreateRGBSurface(surface_flags, width, height,
     }
 
     font_surf.buffer = surface->pixels;
-    font_surf.offset.x = font_text->offset.x;
-    font_surf.offset.y = font_text->offset.y;
-
     font_surf.width = surface->w;
     font_surf.height = surface->h;
     font_surf.pitch = surface->pitch;
-
     font_surf.format = surface->format;
     if (bits_per_pixel == 32) {
         font_surf.render_gray = __render_glyph_RGB4;
     /*
      * Render the text!
      */
-    render(ft, font_text, mode, fgcolor, &font_surf);
+    render(ft, font_text, mode, fgcolor, &font_surf,
+           width, height, &offset, underline_top, underline_size);
 
-    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(font_text->offset.x));
-    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(font_text->offset.y));
+    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x));
+    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y));
     r->w = (Uint16)width;
     r->h = (Uint16)height;
 
     FT_Byte *buffer = 0;
     PyObject *array = 0;
     FaceSurface surf;
-    int width, height;
 
     FaceText *font_text;
+    unsigned width;
+    unsigned height;
+    FT_Vector offset;
+    FT_Pos underline_top;
+    FT_Fixed underline_size;
     int array_size;
     FaceColor mono_opaque = {0, 0, 0, SDL_ALPHA_OPAQUE};
 
         return 0;
     }
 
-    if (_PGFT_GetSurfaceSize(ft, faceobj, mode, font_text, &width, &height)) {
-        return 0;
-    }
+    _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset,
+                           &underline_size, &underline_top);
 
     array_size = width * height;
-
-    if (array_size <= 0) {
+    if (array_size == 0) {
         /* Empty array */
         *_width = 0;
-        *_height = _PGFT_Face_GetHeight(ft, faceobj);
+        *_height = height;
         return Bytes_FromStringAndSize("", 0);
     }
 
         return 0;
     }
     buffer = (FT_Byte *)Bytes_AS_STRING(array);
-
     memset(buffer, 0x00, (size_t)array_size);
-
     surf.buffer = buffer;
-    surf.offset.x = font_text->offset.x;
-    surf.offset.y = font_text->offset.y;
-    surf.width = surf.pitch = width;
+    surf.width = width;
     surf.height = height;
-
+    surf.pitch = (int)surf.width;
     surf.format = 0;
     surf.render_gray = __render_glyph_GRAY1;
     surf.render_mono = __render_glyph_MONO_as_GRAY1;
     surf.fill = __fill_glyph_GRAY1;
 
-    render(ft, font_text, mode, &mono_opaque, &surf);
+    render(ft, font_text, mode, &mono_opaque, &surf,
+           width, height, &offset, underline_top, underline_size);
 
     *_width = width;
     *_height = height;
 
 /*********************************************************
  *
- * New rendering algorithm (rotation + veritical drawing)
+ * New rendering algorithm (full thickness underlines)
  *
  *********************************************************/
 static void
 render(FreeTypeInstance *ft, FaceText *text, const FaceRenderMode *mode,
-       FaceColor *fg_color, FaceSurface *surface)
+       FaceColor *fg_color, FaceSurface *surface,
+       unsigned width, unsigned height, FT_Vector *offset,
+       FT_Pos underline_top, FT_Fixed underline_size)
 {
     FT_Pos top;
     FT_Pos left;
     if (length <= 0) {
         return;
     }
-    left = surface->offset.x;
-    top = surface->offset.y;
+    left = offset->x;
+    top = offset->y;
     for (n = 0; n < length; ++n) {
         image = glyphs[n]->image;
         x = FX6_TRUNC(FX6_CEIL(left + posns[n].x));
         }
     }
 
-    if (mode->style & FT_STYLE_UNDERLINE) {
+    if (underline_size > 0) {
         surface->fill(
-            FX6_TRUNC(FX6_CEIL(left - text->offset.x)),
-            FX6_TRUNC(FX6_CEIL(top + text->underline_pos)),
-            text->width, FX6_TRUNC(FX6_CEIL(text->underline_size)),
+            FX6_TRUNC(FX6_CEIL(left - text->min_x)),
+            FX6_TRUNC(FX6_CEIL(top + underline_top)),
+            width, FX6_TRUNC(FX6_CEIL(underline_size)),
             surface, fg_color);
     }
 }

File src/freetype/ft_text.c

-/*
-  pygame - Python Game Library
-  Copyright (C) 2009 Vicent Marti
-
-  This library is free software; you can redistribute it and/or
-  modify it under the terms of the GNU Library General Public
-  License as published by the Free Software Foundation; either
-  version 2 of the License, or (at your option) any later version.
-
-  This library is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  Library General Public License for more details.
-
-  You should have received a copy of the GNU Library General Public
-  License along with this library; if not, write to the Free
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-*/
-
-#define PYGAME_FREETYPE_INTERNAL
-
-#include "ft_wrap.h"
-#include FT_MODULE_H
-#include FT_TRIGONOMETRY_H
-#include FT_OUTLINE_H
-#include FT_BITMAP_H
-#include FT_CACHE_H
-
-/* Multiply the face's x ppem by this factor to get the x strength
- * factor in 16.16 Fixed.
- */
-#define FX16_WIDE_FACTOR (FX16_ONE / 12)
-
-#define SLANT_FACTOR    0.22
-static FT_Matrix slant_matrix = {
-    FX16_ONE,  (FT_Fixed)(SLANT_FACTOR * FX16_ONE),
-    0,         FX16_ONE
-};
-
-static FT_Matrix unit_matrix = {
-    FX16_ONE,  0,
-    0,         FX16_ONE
-};
-
-typedef struct textcontext_ {
-    FT_Library lib;
-    FTC_FaceID id;
-    FT_Face face;
-    FTC_CMapCache charmap;
-    int do_transform;
-    FT_Matrix transform;
-} TextContext;
-
-#if 0
-#define BOLD_STRENGTH_D (0.65)
-#define PIXEL_SIZE ((FT_Fixed)64)
-#define BOLD_STRENGTH ((FT_Fixed)(BOLD_STRENGTH_D * PIXEL_SIZE))
-#define BOLD_ADVANCE (BOLD_STRENGTH * (FT_Fixed)4)
-#endif
-#define FX16_BOLD_FACTOR (FX16_ONE / 36)
-#define UNICODE_SPACE ((PGFT_char)' ')
-
-static FT_UInt32 get_load_flags(const FaceRenderMode *);
-static void fill_metrics(FaceMetrics *, FT_Pos, FT_Pos,
-                         FT_Vector *, FT_Vector *);
-static void fill_context(TextContext *,
-                         const FreeTypeInstance *,
-                         const PgFaceObject *,
-                         const FaceRenderMode *,
-                         const FT_Face);
-
-int
-_PGFT_FaceTextInit(FreeTypeInstance *ft, PgFaceObject *faceobj)
-{
-    FaceText *ftext = &(PGFT_INTERNALS(faceobj)->active_text);
-
-    ftext->buffer_size = 0;
-    ftext->glyphs = 0;
-    ftext->posns = 0;
-
-    if (_PGFT_Cache_Init(ft, &ftext->glyph_cache)) {
-        PyErr_NoMemory();
-        return -1;
-    }
-
-    return 0;
-}
-
-void
-_PGFT_FaceTextFree(PgFaceObject *faceobj)
-{
-    FaceText *ftext = &(PGFT_INTERNALS(faceobj)->active_text);
-
-    if (ftext->buffer_size > 0) {
-        _PGFT_free(ftext->glyphs);
-        _PGFT_free(ftext->posns);
-    }
-    _PGFT_Cache_Destroy(&ftext->glyph_cache);
-}
-
-FaceText *
-_PGFT_LoadFaceText(FreeTypeInstance *ft, PgFaceObject *faceobj,
-                  const FaceRenderMode *mode, PGFT_String *text)
-{
-    Py_ssize_t  string_length = PGFT_String_GET_LENGTH(text);
-
-    PGFT_char * buffer = PGFT_String_GET_DATA(text);
-    PGFT_char * buffer_end;
-    PGFT_char * ch;
-
-    FaceText    *ftext = &(PGFT_INTERNALS(faceobj)->active_text);
-    FaceGlyph   *glyph = 0;
-    FaceGlyph   **glyph_array = 0;
-    FaceMetrics *metrics;
-    FT_BitmapGlyph image;
-    TextContext context;
-
-    FT_Face     face;
-
-    FT_Vector   pen = {0, 0};                /* untransformed origin  */
-    FT_Vector   pen1 = {0, 0};
-    FT_Vector   pen2;
-
-    FT_Vector   *next_pos;
-
-    int         vertical = mode->render_flags & FT_RFLAG_VERTICAL;
-    int         use_kerning = mode->render_flags & FT_RFLAG_KERNING;
-    int         pad = mode->render_flags & FT_RFLAG_PAD;
-    FT_UInt     prev_glyph_index = 0;
-
-    /* All these are 16.16 precision */
-    FT_Angle    rotation_angle = mode->rotation_angle;
-
-    /* All these are 26.6 precision */
-    FT_Vector   kerning;
-    FT_Pos      min_x = FX6_MAX;
-    FT_Pos      max_x = FX6_MIN;
-    FT_Pos      min_y = FX6_MAX;
-    FT_Pos      max_y = FX6_MIN;
-    FT_Pos      glyph_width;
-    FT_Pos      glyph_height;
-    FT_Pos      text_width;
-    FT_Pos      text_height;
-    FT_Pos      top = FX6_MIN;
-    FT_Fixed    y_scale;
-
-    FT_Error    error = 0;
-
-    /* load our sized face */
-    face = _PGFT_GetFaceSized(ft, faceobj, mode->pt_size);
-    if (!face) {
-        PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft));
-        return 0;
-    }
-    y_scale = face->size->metrics.y_scale;
-
-    /* cleanup the cache */
-    _PGFT_Cache_Cleanup(&ftext->glyph_cache);
-
-    /* create the text struct */
-    if (string_length > ftext->buffer_size) {
-        _PGFT_free(ftext->glyphs);
-        ftext->glyphs = (FaceGlyph **)
-            _PGFT_malloc((size_t)string_length * sizeof(FaceGlyph *));
-        if (!ftext->glyphs) {
-            PyErr_NoMemory();
-            return 0;
-        }
-
-        _PGFT_free(ftext->posns);
-        ftext->posns = (FT_Vector *)
-        _PGFT_malloc((size_t)string_length * sizeof(FT_Vector));
-        if (!ftext->posns) {
-            PyErr_NoMemory();
-            return 0;
-        }
-        ftext->buffer_size = string_length;
-    }
-    ftext->length = string_length;
-    ftext->underline_pos = ftext->underline_size = 0;
-    ftext->descender = face->size->metrics.descender;
-
-    /* fill it with the glyphs */
-    fill_context(&context, ft, faceobj, mode, face);
-    glyph_array = ftext->glyphs;
-    next_pos = ftext->posns;
-
-    for (ch = buffer, buffer_end = ch + string_length; ch < buffer_end; ++ch) {
-        pen2.x = pen1.x;
-        pen2.y = pen1.y;
-        pen1.x = pen.x;
-        pen1.y = pen.y;
-        /*
-         * Load the corresponding glyph from the cache
-         */
-        glyph = _PGFT_Cache_FindGlyph(*((FT_UInt32 *)ch), mode,
-                                     &ftext->glyph_cache, &context);
-
-        if (!glyph) {
-            --ftext->length;
-            continue;
-        }
-        image = glyph->image;
-        glyph_width = glyph->width;
-        glyph_height = glyph->height;
-
-        /*
-         * Do size calculations for all the glyphs in the text
-         */
-        if (use_kerning && prev_glyph_index) {
-            error = FT_Get_Kerning(face, prev_glyph_index,
-                                   glyph->glyph_index,
-                                   FT_KERNING_UNFITTED, &kerning);
-            if (error) {
-                _PGFT_SetError(ft, "Loading glyphs", error);
-                PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft));
-                return 0;
-            }
-            if (rotation_angle != 0) {
-                FT_Vector_Rotate(&kerning, rotation_angle);
-            }
-            pen.x += FX6_ROUND(kerning.x);
-            pen.y += FX6_ROUND(kerning.y);
-            if (FT_Vector_Length(&pen2) > FT_Vector_Length(&pen)) {
-                pen.x = pen2.x;
-                pen.y = pen2.y;
-            }
-        }
-
-        prev_glyph_index = glyph->glyph_index;
-	metrics = vertical ? &glyph->v_metrics : &glyph->h_metrics;
-	if (metrics->bearing_rotated.y > top) {
-            top = metrics->bearing_rotated.y;
-        }
-        if (pen.x + metrics->bearing_rotated.x < min_x) {
-            min_x = pen.x + metrics->bearing_rotated.x;
-        }
-        if (pen.x + metrics->bearing_rotated.x + glyph_width > max_x) {
-            max_x = pen.x + metrics->bearing_rotated.x + glyph_width;
-        }
-        next_pos->x = pen.x + metrics->bearing_rotated.x;
-        pen.x += metrics->advance_rotated.x;
-        if (vertical) {
-            if (pen.y + metrics->bearing_rotated.y < min_y) {
-                min_y = pen.y + metrics->bearing_rotated.y;
-            }
-            if (pen.y + metrics->bearing_rotated.y + glyph_height > max_y) {
-                max_y = pen.y + metrics->bearing_rotated.y + glyph_height;
-            }
-            next_pos->y = pen.y + metrics->bearing_rotated.y;
-            pen.y += metrics->advance_rotated.y;
-        }
-        else {
-            if (pen.y - metrics->bearing_rotated.y < min_y) {
-                min_y = pen.y - metrics->bearing_rotated.y;
-            }
-            if (pen.y - metrics->bearing_rotated.y + glyph_height > max_y) {
-                max_y = pen.y - metrics->bearing_rotated.y + glyph_height;
-            }
-            next_pos->y = pen.y - metrics->bearing_rotated.y;
-            pen.y -= metrics->advance_rotated.y;
-        }
-        *glyph_array++ = glyph;
-        ++next_pos;
-    }
-
-    if (pad) {
-        FT_Size_Metrics *sz_metrics = &face->size->metrics;
-
-        if (pen.x > max_x) {
-            max_x = pen.x;
-        }
-        else if (pen.x < min_x) {
-            min_x = pen.x;
-        }
-        if (pen.y > max_y) {
-            max_y = pen.y;
-        }
-        else if (pen.y < min_y) {
-            min_y = pen.y;
-        }
-        if (vertical) {
-            FT_Fixed right = sz_metrics->max_advance / 2;
-
-            if (max_x < right) {
-                max_x = right;
-            }
-            if (min_x > -right) {
-                min_x = -right;
-            }
-            if (min_y > 0) {
-                min_y = 0;
-            }
-            else if (max_y < pen.y) {
-                max_y = pen.y;
-            }
-        }
-        else {
-            FT_Fixed ascender = sz_metrics->ascender;
-            FT_Fixed descender = sz_metrics->descender;
-
-            if (min_x > 0) {
-                min_x = 0;
-            }
-            if (max_x < pen.x) {
-                max_x = pen.x;
-            }
-            if (min_y > -ascender) {
-                min_y = -ascender;
-            }
-            if (max_y <= -descender) {
-                max_y = -descender + /* baseline */ FX6_ONE;
-            }
-        }
-    }
-
-    if (mode->style & FT_STYLE_UNDERLINE) {
-        FT_Fixed scale = face->size->metrics.y_scale;
-        FT_Fixed adjustment = DBL_TO_FX16(faceobj->underline_adjustment);
-        FT_Fixed pos;
-        FT_Fixed size;
-        FT_Fixed adjusted_pos;
-        FT_Fixed max_y_underline;
-
-        pos = -FT_MulFix(face->underline_position, scale);
-        adjusted_pos = FT_MulFix(pos, adjustment);
-        size = FT_MulFix(face->underline_thickness, scale);
-        max_y_underline = adjusted_pos + size / 2;
-        if (max_y_underline > max_y) {
-            max_y = max_y_underline;
-        }
-        ftext->underline_pos = max_y_underline - size;
-        ftext->underline_size = size;
-    }
-
-    text_width = FX6_CEIL(max_x) - FX6_FLOOR(min_x);
-    ftext->width = FX6_TRUNC(text_width);
-    ftext->offset.x = -min_x;
-    ftext->advance.x = pen.x;
-    ftext->left = FX6_TRUNC(FX6_FLOOR(min_x));
-    text_height = FX6_CEIL(max_y) - FX6_FLOOR(min_y);
-    ftext->height = FX6_TRUNC(text_height);
-    ftext->offset.y = -min_y;
-    ftext->advance.y = pen.y;
-    ftext->top = FX6_TRUNC(FX6_CEIL(top));
-
-    return ftext;
-}
-
-int _PGFT_GetMetrics(FreeTypeInstance *ft, PgFaceObject *faceobj,
-                    PGFT_char character, const FaceRenderMode *mode,
-                    FT_UInt *gindex, long *minx, long *maxx,
-                    long *miny, long *maxy,
-                    double *advance_x, double *advance_y)
-{
-    FaceText    *ftext = &(PGFT_INTERNALS(faceobj)->active_text);
-    FaceGlyph *glyph = 0;
-    TextContext context;
-    FT_Face     face;
-
-    /* load our sized face */
-    face = _PGFT_GetFaceSized(ft, faceobj, mode->pt_size);
-    if (!face) {
-        return -1;
-    }
-
-    /* cleanup the cache */
-    _PGFT_Cache_Cleanup(&ftext->glyph_cache);
-
-    fill_context(&context, ft, faceobj, mode, face);
-    glyph = _PGFT_Cache_FindGlyph(character, mode,
-                                 &PGFT_INTERNALS(faceobj)->active_text.glyph_cache,
-                                 &context);
-
-    if (!glyph) {
-        return -1;
-    }
-
-    *gindex = glyph->glyph_index;
-    *minx = (long)glyph->image->left;
-    *maxx = (long)(glyph->image->left + glyph->image->bitmap.width);
-    *maxy = (long)glyph->image->top;
-    *miny = (long)(glyph->image->top - glyph->image->bitmap.rows);
-    *advance_x = (double)(glyph->h_metrics.advance_rotated.x / 64.0);
-    *advance_y = (double)(glyph->h_metrics.advance_rotated.y / 64.0);
-
-    return 0;
-}
-
-int
-_PGFT_GetSurfaceSize(FreeTypeInstance *ft, PgFaceObject *faceobj,
-        const FaceRenderMode *mode, FaceText *text,
-        int *width, int *height)
-{
-    *width = text->width;
-    *height = text->height;
-    return 0;
-}
-
-int
-_PGFT_GetTopLeft(FaceText *text, int *top, int *left)
-{
-    *top = text->top;
-    *left = text->left;
-    return 0;
-}
-
-int
-_PGFT_GetTextRect(FreeTypeInstance *ft, PgFaceObject *faceobj,
-    const FaceRenderMode *mode, PGFT_String *text, SDL_Rect *r)
-{
-    FaceText *face_text;
-
-    face_text = _PGFT_LoadFaceText(ft, faceobj, mode, text);
-
-    if (!face_text)
-        return -1;
-
-    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(face_text->offset.x));
-    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(face_text->offset.y));
-    r->w = (Uint16)face_text->width;
-    r->h = (Uint16)face_text->height;
-    return 0;
-}
-
-int
-_PGFT_LoadGlyph(FaceGlyph *glyph, PGFT_char character,
-                const FaceRenderMode *mode, void *internal)
-{
-    static FT_Vector delta = {0, 0};
-
-    FT_Render_Mode rmode = (mode->render_flags & FT_RFLAG_ANTIALIAS ?
-                            FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
-    FT_Vector strong_delta = {0, 0};
-    FT_Glyph image = 0;
-
-    FT_Glyph_Metrics *ft_metrics;
-    TextContext *context = (TextContext *)internal;
-
-    FT_UInt32 load_flags;
-    FT_UInt gindex;
-
-    FT_Fixed rotation_angle = mode->rotation_angle;
-    /* FT_Matrix transform; */
-    FT_Vector h_bearing_rotated;
-    FT_Vector v_bearing_rotated;
-    FT_Vector h_advance_rotated;
-    FT_Vector v_advance_rotated;
-
-    FT_Error error = 0;
-
-    /*
-     * Calculate the corresponding glyph index for the char
-     */
-    gindex = FTC_CMapCache_Lookup(context->charmap, context->id,
-                                  -1, (FT_UInt32)character);
-
-    glyph->glyph_index = gindex;
-
-    /*
-     * Get loading information
-     */
-    load_flags = get_load_flags(mode);
-
-    /*
-     * Load the glyph into the glyph slot
-     */
-    if (FT_Load_Glyph(context->face, glyph->glyph_index, (FT_Int)load_flags) ||
-        FT_Get_Glyph(context->face->glyph, &image))
-        goto cleanup;
-
-    /*
-     * Perform any outline transformations
-     */
-    if (mode->style & FT_STYLE_STRONG) {
-        FT_UShort x_ppem = context->face->size->metrics.x_ppem;
-        FT_Fixed bold_str;
-        FT_BBox before;
-        FT_BBox after;
-
-        bold_str = FX16_CEIL_TO_FX6(mode->strength * x_ppem);
-        FT_Outline_Get_CBox(&((FT_OutlineGlyph)image)->outline, &before);
-        if (FT_Outline_Embolden(&((FT_OutlineGlyph)image)->outline, bold_str))
-            goto cleanup;
-        FT_Outline_Get_CBox(&((FT_OutlineGlyph)image)->outline, &after);
-        strong_delta.x += ((after.xMax - after.xMin) -
-                           (before.xMax - before.xMin));
-        strong_delta.y += ((after.yMax - after.yMin) -
-                           (before.yMax - before.yMin));
-    }
-
-    if (context->do_transform) {
-        if (FT_Glyph_Transform(image, &context->transform, &delta)) {
-            goto cleanup;
-        }
-    }
-
-    /*
-     * Finished with outline transformations, now replace with a bitmap
-     */
-    error = FT_Glyph_To_Bitmap(&image, rmode, 0, 1);
-    if (error) {
-        goto cleanup;
-    }
-
-    if (mode->style & FT_STYLE_WIDE) {
-        FT_Bitmap *bitmap = &((FT_BitmapGlyph)image)->bitmap;
-        int w = bitmap->width;
-        FT_UShort x_ppem = context->face->size->metrics.x_ppem;
-        FT_Pos x_strength;
-
-        x_strength = FX16_CEIL_TO_FX6(mode->strength * x_ppem);
-
-        /* FT_Bitmap_Embolden returns an error for a zero width bitmap */
-        if (w > 0) {
-            error = FT_Bitmap_Embolden(context->lib, bitmap,
-                                       x_strength, (FT_Pos)0);
-            if (error) {
-                goto cleanup;
-            }
-            strong_delta.x += INT_TO_FX6(bitmap->width - w);
-        }
-        else {
-            strong_delta.x += x_strength;
-        }
-    }
-
-    /* Fill the glyph */
-    ft_metrics = &context->face->glyph->metrics;
-
-    h_advance_rotated.x = ft_metrics->horiAdvance + strong_delta.x;
-    h_advance_rotated.y = 0;
-    v_advance_rotated.x = 0;
-    v_advance_rotated.y = ft_metrics->vertAdvance + strong_delta.y;
-    if (rotation_angle != 0) {
-        FT_Angle counter_rotation = INT_TO_FX6(360) - rotation_angle;
-
-        FT_Vector_Rotate(&h_advance_rotated, rotation_angle);
-        FT_Vector_Rotate(&v_advance_rotated, counter_rotation);
-    }
-
-    glyph->image = (FT_BitmapGlyph)image;
-    glyph->width = INT_TO_FX6(glyph->image->bitmap.width);
-    glyph->height = INT_TO_FX6(glyph->image->bitmap.rows);
-    h_bearing_rotated.x = INT_TO_FX6(glyph->image->left);
-    h_bearing_rotated.y = INT_TO_FX6(glyph->image->top);
-    fill_metrics(&glyph->h_metrics,
-                 ft_metrics->horiBearingX,
-                 ft_metrics->horiBearingY,
-                 &h_bearing_rotated, &h_advance_rotated);
-
-    if (rotation_angle == 0) {
-        v_bearing_rotated.x = ft_metrics->vertBearingX - strong_delta.x / 2;
-        v_bearing_rotated.y = ft_metrics->vertBearingY;
-    }
-    else {
-        /*
-         * Adjust the vertical metrics.
-         */
-        FT_Vector v_origin;
-
-        v_origin.x = (glyph->h_metrics.bearing_x -
-                      ft_metrics->vertBearingX + strong_delta.x / 2);
-        v_origin.y = (glyph->h_metrics.bearing_y +
-                      ft_metrics->vertBearingY);
-        FT_Vector_Rotate(&v_origin, rotation_angle);
-        v_bearing_rotated.x = glyph->h_metrics.bearing_rotated.x - v_origin.x;
-        v_bearing_rotated.y = v_origin.y - glyph->h_metrics.bearing_rotated.y;
-    }
-    fill_metrics(&glyph->v_metrics,
-                 ft_metrics->vertBearingX,
-                 ft_metrics->vertBearingY,
-                 &v_bearing_rotated, &v_advance_rotated);
-
-    return 0;
-
-    /*
-     * Cleanup on error
-     */
-cleanup:
-    if (image) {
-        FT_Done_Glyph(image);
-    }
-
-    return -1;
-}
-
-static void
-fill_context(TextContext *context,
-             const FreeTypeInstance *ft,
-             const PgFaceObject *faceobj,
-             const FaceRenderMode *mode,
-             const FT_Face face)
-{
-    context->lib = ft->library;
-    context->id = (FTC_FaceID)&(faceobj->id);
-    context->face = face;
-    context->charmap = ft->cache_charmap;
-    context->do_transform = 0;
-
-    if (mode->style & FT_STYLE_OBLIQUE) {
-        context->transform = slant_matrix;
-        context->do_transform = 1;
-    }
-    else {
-        context->transform = unit_matrix;
-    }
-
-    if (mode->render_flags & FT_RFLAG_TRANSFORM) {
-        FT_Matrix_Multiply(&mode->transform, &context->transform);
-        context->do_transform = 1;
-    }
-
-    if (mode->rotation_angle != 0) {
-        FT_Vector unit;
-        FT_Matrix rotate;
-
-        FT_Vector_Unit(&unit, mode->rotation_angle);
-        rotate.xx = unit.x;  /*  cos(angle) */
-        rotate.xy = -unit.y; /* -sin(angle) */
-        rotate.yx = unit.y;  /*  sin(angle) */
-        rotate.yy = unit.x;  /*  cos(angle) */
-        FT_Matrix_Multiply(&rotate, &context->transform);
-        context->do_transform = 1;
-    }
-}
-
-static void
-fill_metrics(FaceMetrics *metrics,
-             FT_Pos bearing_x, FT_Pos bearing_y,
-             FT_Vector *bearing_rotated,
-             FT_Vector *advance_rotated)
-{
-    metrics->bearing_x = bearing_x;
-    metrics->bearing_y = bearing_y;
-    metrics->bearing_rotated.x = bearing_rotated->x;
-    metrics->bearing_rotated.y = bearing_rotated->y;
-    metrics->advance_rotated.x = advance_rotated->x;
-    metrics->advance_rotated.y = advance_rotated->y;
-}
-
-static FT_UInt32
-get_load_flags(const FaceRenderMode *mode)
-{
-    FT_UInt32 load_flags = FT_LOAD_DEFAULT;
-
-    load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
-
-    if (mode->render_flags & FT_RFLAG_AUTOHINT) {
-        load_flags |= FT_LOAD_FORCE_AUTOHINT;
-    }
-
-    if (mode->render_flags & FT_RFLAG_HINTED) {
-        load_flags |= FT_LOAD_TARGET_NORMAL;
-    }
-    else {
-        load_flags |= FT_LOAD_NO_HINTING;
-    }
-
-    return load_flags;
-}

File src/freetype/ft_wrap.c

                            FX6_FLOOR(metrics->descender)) + /* baseline */ 1;
 }
 
+int
+_PGFT_GetTextRect(FreeTypeInstance *ft, PgFaceObject *faceobj,
+                  const FaceRenderMode *mode, PGFT_String *text, SDL_Rect *r)
+{
+    FaceText *face_text;
+    unsigned width;
+    unsigned height;
+    FT_Vector offset;
+    FT_Pos underline_size;
+    FT_Pos underline_top;
+
+    face_text = _PGFT_LoadFaceText(ft, faceobj, mode, text);
+    if (!face_text) {
+        return -1;
+    }
+    _PGFT_GetRenderMetrics(mode, face_text, &width, &height, &offset,
+                           &underline_size, &underline_top);
+    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x));
+    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y));
+    r->w = (Uint16)width;
+    r->h = (Uint16)height;
+    return 0;
+}
 
 
 /*********************************************************

File src/freetype/ft_wrap.h

     FT_Angle rotation_angle;
     FT_UInt16 render_flags;
     FT_UInt16 style;
+
+    /* All these are Fixed 16.16 */
     FT_Fixed strength;
+    FT_Fixed underline_adjustment;
     FT_Matrix transform;
 } FaceRenderMode;
 
 
 typedef struct facetext_ {
     int length;
-    int width;     /* In pixels */
-    int height;    /* In pixels */
+
     int top;       /* In pixels */
     int left;      /* In pixels */
 
+    FT_Pos min_x;
+    FT_Pos max_x;
+    FT_Pos min_y;
+    FT_Pos max_y;
     FT_Vector offset;
     FT_Vector advance;
-    FT_Pos underline_size;
+    FT_Pos ascender;
+    FT_Fixed underline_size;
     FT_Pos underline_pos;
-    FT_Pos descender;
 
     int buffer_size;
     FaceGlyph **glyphs;
 typedef struct facesurface_ {
     void *buffer;
 
-    FT_Vector offset;
-
-    int width;
-    int height;
+    unsigned width;
+    unsigned height;
     int pitch;
 
     SDL_PixelFormat *format;
                      PGFT_char, const FaceRenderMode *,
                      FT_UInt *, long *, long *, long *, long *,
                      double *, double *);
-int _PGFT_GetSurfaceSize(FreeTypeInstance *, PgFaceObject *,
-                         const FaceRenderMode *, FaceText *, int *, int *);
-int _PGFT_GetTopLeft(FaceText *, int *, int *);
+void _PGFT_GetRenderMetrics(const FaceRenderMode *, FaceText *,
+                            unsigned *, unsigned *, FT_Vector *,
+                            FT_Pos *, FT_Fixed *);
 
 
 /**************************************** Rendering **************************/