Commits

Anonymous committed 0605ece

Glyph caching system is in place.

Comments (0)

Files changed (6)

src/freetype/ft_cache.c

 
 #include FT_MODULE_H
 
-FT_UInt32 _PGFT_Cache_Hash(const FontRenderMode *, FT_UInt, int);
+#define PGFT_DEBUG_CACHE
+
+FT_UInt32 _PGFT_Cache_Hash(const FontRenderMode *, FT_UInt);
 FT_UInt32 _PGFT_GetLoadFlags(const FontRenderMode *);
 
-FontGlyph *_PGFT_Cache_AllocateGlyph(PGFT_Cache *, const FontRenderMode *, FT_UInt);
+FontGlyph *_PGFT_Cache_AllocateGlyph(FreeTypeInstance *, 
+        PGFT_Cache *, const FontRenderMode *, FT_UInt);
 void _PGFT_Cache_FreeGlyph(FontGlyph *);
 
 
     return load_flags;
 }
 
-FT_UInt32 _PGFT_Cache_Hash(const FontRenderMode *render, FT_UInt glyph_index, int hash_mode)
+FT_UInt32 _PGFT_Cache_Hash(const FontRenderMode *render, FT_UInt glyph_index)
 {
-    static const FT_UInt32 _CUCKOO_HASHES[] =
-    {
-        0xDEADC0DE,
-        0xBEEF1234
-    };
-
 	const FT_UInt32 m = 0x5bd1e995;
 	const int r = 24;
 
      * Assumes sizeof(FontRenderMode) == 8
      */
 
-    h = (glyph_index << 8) | _CUCKOO_HASHES[hash_mode];
+    h = (glyph_index << 12) ^ glyph_index;
 
     k = *(const FT_UInt32 *)render;
     k *= m; k ^= k >> r; 
 	return h;
 } 
 
-PGFT_Cache *PGFT_Cache_Create(FT_Face parent)
+void PGFT_Cache_Init(PGFT_Cache *cache, PyFreeTypeFont *parent)
 {
     /* 
      * TODO: Let user specify the desired
      * size for the cache?
      */
     const int cache_size = 64;
-    PGFT_Cache *cache = NULL;
 
-    cache = malloc(sizeof(PGFT_Cache));
-
-    if (!cache)
-        return NULL;
-
-    cache->face = parent;
+    cache->font = parent;
     cache->nodes = calloc(cache_size, sizeof(FontGlyph *));
     cache->size_mask = (cache_size - 1);
-
-    return cache;
+    cache->lru_counter = 0;
 }
 
 void PGFT_Cache_Destroy(PGFT_Cache *cache)
         _PGFT_Cache_FreeGlyph(cache->nodes[i]);
 
     free(cache->nodes);
-    free(cache);
 }
 
-FontGlyph *PGFT_Cache_FindGlyph(PGFT_Cache *cache, 
-        FT_UInt glyph_index, const FontRenderMode *render)
+FontGlyph *PGFT_Cache_FindGlyph(FreeTypeInstance *ft, PGFT_Cache *cache, 
+        FT_UInt character, const FontRenderMode *render)
 {
-    const int MAX_CUCKOO_ITER = 6;
-    FontGlyph **glyph = NULL, *next_glyph, *aux, *alloc;
-    int cuckoo_hash = 0;
-    FT_UInt32 hashes[2];
+    FontGlyph **nodes = cache->nodes;
+    FT_UInt32 lowest, current, first, i;
 
+    const FT_UInt32 hash = _PGFT_Cache_Hash(render, character);
+    FT_UInt32 perturb;
+    
+    i = hash;
+    current = first = lowest = hash & cache->size_mask;
+    perturb = hash;
+
+    /*
+     * Browse the whole cache with linear probing until either:
+     *
+     *  a) we find an empty spot for the glyph
+     *      => we load our glyph and store it there
+     *
+     *  b) we find the glyph already on the cache
+     *      => we return the reference to that glyph
+     *
+     *  c) we find no glyphs, and no empty spots
+     *      => we kick the LRU glyph from the cache,
+     *      and store the new one there
+     */
     do
     {
-        FT_UInt32 hash = _PGFT_Cache_Hash(render, glyph_index, cuckoo_hash & 1);
-        glyph = &cache->nodes[hash & cache->size_mask];
-
-        if (*glyph == NULL)
+        if (nodes[current] == NULL)
         {
-           *glyph = _PGFT_Cache_AllocateGlyph(cache, render, glyph_index);
-           return *glyph;
+            /* A: found empty spot */
+            return (nodes[current] = 
+                   _PGFT_Cache_AllocateGlyph(ft, cache, render, character));
         }
         
-        if ((*glyph)->glyph_index == glyph_index)
+        if (nodes[current]->hash == hash)
         {
-            return *glyph;
+            /* B: found glyph on cache */
+            nodes[current]->lru = ++cache->lru_counter;
+            return nodes[current];
         }
 
-    } while (cuckoo_hash++ < 2);
+        if (nodes[current]->lru < nodes[lowest]->lru)
+            lowest = current;
 
-    /*
-     * Glyph is not cached.
-     * Place it on the cache!
-     */
-    cuckoo_hash = 0;
+        i = (5 * i) + 1 + perturb;
+        perturb <<= 5;
 
-    next_glyph = *glyph;
-    *glyph = _PGFT_Cache_AllocateGlyph(cache, render, glyph_index);
-    alloc = *glyph;
+        current = i & cache->size_mask;
 
-    while (next_glyph != NULL && cuckoo_hash++ < MAX_CUCKOO_ITER)
-    {
-        hashes[0] = next_glyph->hashes[0];
-        hashes[1] = next_glyph->hashes[1];
+    } while (current != first);
 
-        if (glyph == &cache->nodes[hashes[0] & cache->size_mask])
-            glyph = &cache->nodes[hashes[1] & cache->size_mask];
-        else
-            glyph = &cache->nodes[hashes[0] & cache->size_mask];
+    /* C: kick glyph from cache */
+    _PGFT_Cache_FreeGlyph(nodes[lowest]);
 
-        aux = *glyph;
-        *glyph = next_glyph;
-        next_glyph = aux;
-    }
-
-    if (next_glyph)
-        _PGFT_Cache_FreeGlyph(next_glyph);
-
-    return alloc;
+    return (nodes[lowest] = 
+            _PGFT_Cache_AllocateGlyph(ft, cache, render, character));
 }
 
 void _PGFT_Cache_FreeGlyph(FontGlyph *glyph)
     free(glyph);
 }
 
-FontGlyph *_PGFT_Cache_AllocateGlyph(PGFT_Cache *cache, 
-        const FontRenderMode *render, FT_UInt glyph_index)
+FontGlyph *_PGFT_Cache_AllocateGlyph(FreeTypeInstance *ft, 
+        PGFT_Cache *cache, const FontRenderMode *render, FT_UInt character)
 {
     FT_Glyph_Metrics *metrics;
     FontGlyph *glyph = NULL;
     FT_UInt32 load_flags;
     FT_Fixed bold_str = 0;
+    int gindex;
+
+    FT_Face face;
+
+    /*
+     * Grab face reference
+     */
+    face = _PGFT_GetFaceSized(ft, cache->font, render->pt_size);
+
+    if (!face)
+    {
+        _PGFT_SetError(ft, "Failed to resize face", 0);
+        goto cleanup;
+    }
 
     /* 
      * Allocate cache node 
      */
     glyph = malloc(sizeof(FontGlyph));
 
-    glyph->glyph_index = glyph_index;
-    glyph->hashes[0] = _PGFT_Cache_Hash(render, glyph_index, 0);
-    glyph->hashes[1] = _PGFT_Cache_Hash(render, glyph_index, 1);
 
     /*
-     * Loading information
+     * Calculate the corresponding glyph index for the char
+     */
+    gindex = FTC_CMapCache_Lookup(ft->cache_charmap, 
+            (FTC_FaceID)&(cache->font->id), -1, character);
+
+    if (gindex < 0)
+    {
+        _PGFT_SetError(ft, "Glyph character not found in font", 0);
+        goto cleanup;
+    }
+
+    glyph->glyph_index = (FT_UInt)gindex;
+
+
+    /*
+     * Get loading information
      */
     load_flags = _PGFT_GetLoadFlags(render);
 
     if (render->style & FT_STYLE_BOLD)
-        bold_str = PGFT_GetBoldStrength(cache->face);
+        bold_str = PGFT_GetBoldStrength(face);
 
     /*
      * Load the glyph into the glyph slot
      * TODO: error handling
      */
-    if (FT_Load_Glyph(cache->face, glyph_index, (FT_Int)load_flags) != 0 ||
-        FT_Get_Glyph(cache->face->glyph, &(glyph->image)) != 0)
-        return NULL;
+    if (FT_Load_Glyph(face, glyph->glyph_index, (FT_Int)load_flags) != 0 ||
+        FT_Get_Glyph(face->glyph, &(glyph->image)) != 0)
+        goto cleanup;
 
     /*
      * Precalculate useful metric values
      */
-    metrics = &cache->face->glyph->metrics;
+    metrics = &face->glyph->metrics;
 
     glyph->vvector.x  = (metrics->vertBearingX - bold_str / 2) - metrics->horiBearingX;
     glyph->vvector.y  = -(metrics->vertBearingY + bold_str) - (metrics->horiBearingY + bold_str);
     glyph->size.x = metrics->width + bold_str;
     glyph->size.y = metrics->height + bold_str;
 
-    glyph->lsb_delta = cache->face->glyph->lsb_delta;
+
+    /*
+     * Update cache internals
+     */
+    glyph->lru = ++cache->lru_counter;
+    glyph->hash = _PGFT_Cache_Hash(render, character);
 
     return glyph;
+
+    /*
+     * Cleanup on error
+     */
+cleanup:
+    if (glyph && glyph->image)
+        FT_Done_Glyph(glyph->image);
+
+    free(glyph);
+    return NULL;
 }

src/freetype/ft_render.c

         FT_Glyph image;
         FT_BBox bbox;
 
-        FontGlyph *glyph = &(text->glyphs[n]);
+        FontGlyph *glyph = text->glyphs[n];
 
-        if (!glyph->image)
+        if (!glyph || !glyph->image)
             continue;
 
         /* copy image */

src/freetype/ft_text.c

 PGFT_LoadFontText(FreeTypeInstance *ft, PyFreeTypeFont *font, 
         const FontRenderMode *render, PyObject *text)
 {
-    FT_Int32 load_flags = FT_LOAD_DEFAULT; 
-
     int         swapped = 0;
     int         string_length = 0;
 
     FT_UInt16 * orig_buffer;
     FT_UInt16 * ch;
 
-    FT_Pos      prev_rsb_delta = 0;
-    FT_Fixed    baseline;
+    FT_Fixed    y_scale;
     FT_Fixed    bold_str = 0;
-    FT_Fixed    y_scale;
 
-    FontText  * ftext = NULL;
-    FontGlyph * glyph = NULL;
+    FontText    *ftext = NULL;
+    FontGlyph   *glyph = NULL;
+    FontGlyph   **glyph_array = NULL;
 
     FT_Face     face;
 
-    /* compute proper load flags */
-    load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
-
-    if (render->render_flags & FT_RFLAG_AUTOHINT)
-        load_flags |= FT_LOAD_FORCE_AUTOHINT;
-
-    if (render->render_flags & FT_RFLAG_HINTED)
-    {
-        load_flags |=   (render->render_flags & FT_RFLAG_ANTIALIAS) ?
-                        FT_LOAD_TARGET_NORMAL :
-                        FT_LOAD_TARGET_MONO;
-    }
-    else
-    {
-        load_flags |= FT_LOAD_NO_HINTING;
-    }
-
     /* load our sized face */
     face = _PGFT_GetFaceSized(ft, font, render->pt_size);
 
 
     /* get the length of the text */
     for (ch = buffer; *ch; ++ch)
-        string_length++;
-
-    if (render->style & FT_STYLE_BOLD)
-        bold_str = PGFT_GetBoldStrength(face);
+    {
+        if (*ch != UNICODE_BOM_NATIVE &&
+            *ch != UNICODE_BOM_SWAPPED)
+            string_length++;
+    }
 
     /* create the text struct */
-    ftext = malloc(sizeof(FontText));
+    ftext = &(PGFT_INTERNALS(font)->active_text);
+
+    free(ftext->glyphs);
+    ftext->glyphs = calloc((size_t)string_length, sizeof(FontGlyph *));
+
     ftext->length = string_length;
-    ftext->glyphs = calloc((size_t)string_length, sizeof(FontGlyph));
     ftext->glyph_size.x = ftext->glyph_size.y = 0;
     ftext->text_size.x = ftext->text_size.y = 0;
     ftext->baseline_offset.x = ftext->baseline_offset.y = 0;
      * on most cases. We manually adjust for this by increasing the offset
      * a proportional amount to the bold strength.
      */
+    if (render->style & FT_STYLE_BOLD)
+        bold_str = PGFT_GetBoldStrength(face);
+
     ftext->underline_pos = (FT_Int16)(
             PGFT_FLOOR(FT_MulFix(face->underline_position + bold_str / 2, y_scale)) >> 6);
 
             (bold_str + PGFT_FLOOR(FT_MulFix(face->underline_thickness, y_scale))) >> 6);
 
     /* fill it with the glyphs */
-    glyph = &(ftext->glyphs[0]);
+    glyph_array = ftext->glyphs;
 
-    for (ch = buffer; *ch; ++ch, ++glyph)
+    for (ch = buffer; *ch; ++ch)
     {
         FT_UInt16 c = *ch;
 
+        /*
+         * Handle byte-order markers in the unicode string
+         */
         if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED)
         {
             swapped = (c == UNICODE_BOM_SWAPPED);
         if (swapped)
             c = (FT_UInt16)((c << 8) | (c >> 8));
 
-        glyph->glyph_index = FTC_CMapCache_Lookup(ft->cache_charmap, 
-            (FTC_FaceID)(&font->id),
-            -1, (FT_UInt32)c);
+        /*
+         * Load the corresponding glyph from the cache
+         */
+        glyph = PGFT_Cache_FindGlyph(ft, &PGFT_INTERNALS(font)->cache, 
+                (FT_UInt)c, render);
 
-        /* FIXME: leaks memory, needs to use the cache */
-        if (!FT_Load_Glyph(face, glyph->glyph_index, load_flags)  &&
-            !FT_Get_Glyph(face->glyph, &glyph->image))
-        {
-            FT_Glyph_Metrics *metrics = &face->glyph->metrics;
+        if (!glyph)
+            continue;
+           
+        /*
+         * Do size calculations for all the glyphs in the text
+         */
+        if (glyph->baseline > ftext->baseline_offset.y)
+            ftext->baseline_offset.y = glyph->baseline;
 
-            /* note that in vertical layout, y-positive goes downwards */
-            glyph->vvector.x  = (metrics->vertBearingX - bold_str / 2) - metrics->horiBearingX;
-            glyph->vvector.y  = -(metrics->vertBearingY + bold_str) - (metrics->horiBearingY + bold_str);
+        if (glyph->size.x > ftext->glyph_size.x)
+            ftext->glyph_size.x = glyph->size.x;
 
-            glyph->vadvance.x = 0;
-            glyph->vadvance.y = -(metrics->vertAdvance + bold_str);
+        if (glyph->size.y > ftext->glyph_size.y)
+            ftext->glyph_size.y = glyph->size.y;
 
-            baseline = metrics->height - metrics->horiBearingY;
-
-            if (baseline > ftext->baseline_offset.y)
-                ftext->baseline_offset.y = baseline;
-
-            if (metrics->width + bold_str > ftext->glyph_size.x)
-                ftext->glyph_size.x = metrics->width + bold_str;
-
-            if (metrics->height + bold_str > ftext->glyph_size.y)
-                ftext->glyph_size.y = metrics->height + bold_str;
-
-            if (prev_rsb_delta - face->glyph->lsb_delta >= 32)
-                glyph->delta = -1 << 6;
-            else if (prev_rsb_delta - face->glyph->lsb_delta < -32)
-                glyph->delta = 1 << 6;
-            else
-                glyph->delta = 0;
-        }
-
+        *glyph_array++ = glyph;
     }
 
     /* 
 
     for (i = 0; i < text->length; i++)
     {
-        glyph = &(text->glyphs[i]);
+        glyph = text->glyphs[i];
 
-        if (!glyph->image)
+        if (!glyph || !glyph->image)
             continue;
 
         if (render->render_flags & FT_RFLAG_VERTICAL)

src/freetype/ft_wrap.c

 
 
 
-/*********************************************************
- *
- * Glyph loading
- *
- *********************************************************/
-int
-_PGFT_LoadGlyph(FreeTypeInstance *ft, 
-    PyFreeTypeFont *font,
-    int load_flags,
-    FTC_Scaler scale, 
-    int character, 
-    FT_Glyph *glyph, 
-    FT_UInt32 *_index)
-{
-    FT_Error error = 0;
-    FT_UInt32 char_index;
-
-    char_index = FTC_CMapCache_Lookup(
-        ft->cache_charmap, 
-            (FTC_FaceID)(&font->id),
-            -1, (FT_UInt32)character);
-
-    if (_index)
-        *_index = char_index;
-
-    if (char_index == 0)
-        return -1;
-
-    if (glyph)
-    {
-        error = FTC_ImageCache_LookupScaler(
-            ft->cache_img,
-                scale,
-                load_flags,
-                char_index,
-                glyph, NULL);
-    }
-
-    return error;
-}
-
 
 
 
     font->id.open_args.flags = FT_OPEN_PATHNAME;
     font->id.open_args.pathname = filename_alloc;
 
+    font->_internals = malloc(sizeof(FontInternals));
+    memset(font->_internals, 0x0, sizeof(FontInternals));
+    
+    PGFT_Cache_Init(&PGFT_INTERNALS(font)->cache, font);
+
     return _PGFT_GetFace(ft, font) ? 0 : -1;
 }
 
     if (ft != NULL)
         FTC_Manager_RemoveFaceID(ft->cache_manager, (FTC_FaceID)(&font->id));
 
+    PGFT_Cache_Destroy(&PGFT_INTERNALS(font)->cache);
+    free(PGFT_INTERNALS(font)->active_text.glyphs);
+    free(PGFT_INTERNALS(font));
+
     free(font->id.open_args.pathname);
 }
 
             &inst->cache_charmap) != 0)
         goto error_cleanup;
 
-    if (FTC_SBitCache_New(inst->cache_manager,
-            &inst->cache_sbit) != 0)
-        goto error_cleanup;
-
-    if (FTC_ImageCache_New(inst->cache_manager,
-            &inst->cache_img) != 0)
-        goto error_cleanup;
-
     *_instance = inst;
     return 0;
 

src/freetype/ft_wrap.h

 #define PGFT_ROUND(x)  ( ( (x) + 32 ) & -64 )
 #define PGFT_TRUNC(x)  (   (x) >> 6 )
 
-
-#define PGFT_CHECK_PTSIZE()                             \
-    if (ptsize == -1)                                   \
-    {                                                   \
-        if (font->default_ptsize == -1)                 \
-        {                                               \
-            PyErr_SetString(PyExc_ValueError,           \
-                    "Missing font size argument "       \
-                    "and no default size specified");   \
-            return NULL;                                \
-        }                                               \
-                                                        \
-        ptsize = font->default_ptsize;                  \
-    }
-
 #define PGFT_CHECK_BOOL(_pyobj, _var)               \
     if (_pyobj)                                     \
     {                                               \
 {
     FT_Library library;
     FTC_Manager cache_manager;
-    FTC_SBitCache cache_sbit;
-    FTC_ImageCache cache_img;
     FTC_CMapCache cache_charmap;
 
     char *_error_msg;
 
     FT_Fixed    baseline;
     FT_Vector   size;
-    FT_Int      lsb_delta;
 
-    FT_UInt32   hashes[2];
-
+    FT_UInt32   lru;
+    FT_UInt32   hash;
 } FontGlyph;
 
 typedef struct FontText_
 {
-    FontGlyph *glyphs;
+    FontGlyph **glyphs;
     int length;
-    FT_UInt32 _hash;
 
     FT_Vector glyph_size;       /* 26.6 */
     FT_Vector text_size;        /* 26.6 */
 
     FT_Int16 underline_pos;
     FT_Int16 underline_h;
-
 } FontText;
 
 typedef struct __glyphcache
     FontGlyph   **nodes;
     FT_UInt32   size_mask;
 
-    FT_Face     face;
+    FT_UInt32   lru_counter;
+
+    PyFreeTypeFont  *font;
 } PGFT_Cache;
 
+typedef struct FontInternals_
+{
+    PGFT_Cache  cache;
+    FontText    active_text;
+
+    /* TODO */
+} FontInternals;
+
+#define PGFT_INTERNALS(f) ((FontInternals *)(f->_internals))
+
 
 typedef struct {
     FreeTypeInstance *freetype;
 
 
 /******************************************************** Glyph cache management ****/
-PGFT_Cache* PGFT_Cache_Create(FT_Face parent);
+void        PGFT_Cache_Init(PGFT_Cache *cache, PyFreeTypeFont *parent);
 void        PGFT_Cache_Destroy(PGFT_Cache *cache);
-FontGlyph * PGFT_Cache_FindGlyph(PGFT_Cache *cache, FT_UInt glyph_index, 
+FontGlyph * PGFT_Cache_FindGlyph(FreeTypeInstance *ft, PGFT_Cache *cache, FT_UInt character, 
                 const FontRenderMode *render);
 
 
 FT_Face     _PGFT_GetFace(FreeTypeInstance *, PyFreeTypeFont *);
 FT_Face     _PGFT_GetFaceSized(FreeTypeInstance *, PyFreeTypeFont *, int);
 void        _PGFT_BuildScaler(PyFreeTypeFont *, FTC_Scaler, int);
-int         _PGFT_LoadGlyph(FreeTypeInstance *, PyFreeTypeFont *, int,
-                FTC_Scaler, int, FT_Glyph *, FT_UInt32 *);
 
 
 #endif

src/freetype/pgfreetype.h

 {
     PyFont pyfont;
     FontId id;
+
+    void *_internals;
     int default_ptsize;
+
 } PyFreeTypeFont;
 
 #define PyFreeTypeFont_AsFont(x) (((PyFreeTypeFont *)x)->font)