shithub: freetype+ttf2subf

ref: 4e4a43631c91cceeae2f90e190ca2cef67910374
dir: /src/cache/ftcglyph.c/

View raw version
/***************************************************************************/
/*                                                                         */
/*  ftcglyph.c                                                             */
/*                                                                         */
/*    FreeType Glyph Image (FT_Glyph) cache (body).                        */
/*                                                                         */
/*  Copyright 2000 by                                                      */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <freetype/cache/ftcglyph.h>
#include <freetype/fterrors.h>
#include <freetype/internal/ftobjs.h>
#include <freetype/internal/ftlist.h>
#include <freetype/fterrors.h>


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      GLYPH NODES                              *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/


  /* create a new glyph node, setting its cache index and ref count */
  FT_EXPORT_FUNC( void )  FTC_GlyphNode_Init( FTC_GlyphNode  node,
                                              FTC_GlyphSet   gset,
                                              FT_UInt        gindex )
  {
    FTC_Glyph_Cache      cache = gset->cache;
    FTC_CacheNode_Data*  data  = FTC_CACHENODE_TO_DATA_P( &node->root );


    data->cache_index = (FT_UShort)cache->root.cache_index;
    data->ref_count   = (FT_Short) 0;
    node->gset_index  = (FT_UShort)gset->gset_index;
    node->glyph_index = (FT_UShort)gindex;
  }


  /* Important: This function is called from the cache manager to */
  /* destroy a given cache node during `cache compression'.  The  */
  /* second argument is always `cache.user_data'.  Thus be        */
  /* certain that the function FTC_Glyph_Cache_New() does indeed  */
  /* set its `user_data' field correctly, otherwise bad things    */
  /* will happen!                                                 */

  FT_EXPORT_FUNC( void )  FTC_GlyphNode_Destroy( FTC_GlyphNode    node,
                                                 FTC_Glyph_Cache  cache )
  {
    FT_LruNode    gset_lru = cache->gsets_lru->nodes + node->gset_index;
    FTC_GlyphSet  gset     = (FTC_GlyphSet)gset_lru->root.data;
    FT_UInt       hash     = node->glyph_index % gset->hash_size;
    
    /* remove the node from its gset's bucket list */
    {
      FTC_GlyphNode*  pnode = gset->buckets + hash;
      FTC_GlyphNode   cur;
      
      for (;;)
      {
        cur = *pnode;
        if (!cur)
        {
          /* that's very strange, this should not happen !! */
          FT_ERROR(( "FTC_GlyphNode_Destroy:"
                     " trying to delete an unlisted node !!!!" ));
          return;
        }
          
        if (cur == node)
        {
          *pnode = cur->gset_next;
          break;
        }
        pnode = &cur->gset_next;
      }
    }

    /* destroy the node */
    gset->clazz->destroy_node( node, gset );
  }


  /* Important: This function is called from the cache manager to */
  /* size a given cache node during `cache compression'.  The     */
  /* second argument is always `cache.user_data'.  Thus be        */
  /* certain that the function FTC_Glyph_Cache_New() does indeed  */
  /* set its `user_data' field correctly, otherwise bad things    */
  /* will happen!                                                 */

  FT_EXPORT_FUNC( FT_ULong )  FTC_GlyphNode_Size( FTC_GlyphNode    node,
                                                  FTC_Glyph_Cache  cache )
  {
    FT_LruNode    gset_lru = cache->gsets_lru->nodes + node->gset_index;
    FTC_GlyphSet  gset     = (FTC_GlyphSet)gset_lru->root.data;


    return gset->clazz->size_node( node, gset );
  }


  FT_CPLUSPLUS( const FTC_CacheNode_Class )  ftc_glyph_cache_node_class =
  {
    (FTC_CacheNode_SizeFunc)    FTC_GlyphNode_Size,
    (FTC_CacheNode_DestroyFunc) FTC_GlyphNode_Destroy
  };


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                      GLYPH QUEUES                             *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/


  FT_EXPORT_FUNC( FT_Error )  FTC_GlyphSet_New( FTC_Glyph_Cache  cache,
                                                FT_Pointer       type,
                                                FTC_GlyphSet    *aset )
  {
    FT_Error        error;
    FT_Memory       memory  = cache->root.memory;
    FTC_Manager     manager = cache->root.manager;
    FTC_GlyphSet    gset   = 0;

    FTC_Glyph_Cache_Class*  gcache_class;
    FTC_GlyphSet_Class*     clazz;


    gcache_class = (FTC_Glyph_Cache_Class*)cache->root.clazz;
    clazz        = gcache_class->gset_class;

    *aset = 0;

    if ( ALLOC( gset, clazz->gset_byte_size ) )
      goto Exit;

    gset->cache     = cache;
    gset->manager   = manager;
    gset->memory    = memory;
    gset->hash_size = FTC_GSET_HASH_SIZE_DEFAULT;
    gset->clazz     = clazz;

    /* allocate buckets table */
    if ( ALLOC_ARRAY( gset->buckets, gset->hash_size, FTC_GlyphNode ) )
      goto Exit;

    /* initialize gset by type if needed */
    if ( clazz->init )
    {
      error = clazz->init( gset, type );
      if ( error )
        goto Exit;
    }

    *aset = gset;

  Exit:
    if ( error && gset )
    {
      FREE( gset->buckets );
      FREE( gset );
    }

    return error;
  }


  FT_EXPORT_FUNC( void )  FTC_GlyphSet_Destroy( FTC_GlyphSet  gset )
  {
    FTC_Glyph_Cache         cache        = gset->cache;
    FTC_Manager             manager      = cache->root.manager;
    FT_List                 glyphs_lru   = &manager->global_lru;
    FTC_GlyphNode*          bucket       = gset->buckets;
    FTC_GlyphNode*          bucket_limit = bucket + gset->hash_size;
    FT_Memory               memory       = cache->root.memory;

    FTC_GlyphSet_Class*  clazz = gset->clazz;


    /* for each bucket, free the list of glyph nodes */
    for ( ; bucket < bucket_limit; bucket++ )
    {
      FTC_GlyphNode   node = bucket[0];
      FTC_GlyphNode   next = 0;
      FT_ListNode     lrunode;


      for ( ; node; node = next )
      {
        next    = node->gset_next;
        lrunode = FTC_GLYPHNODE_TO_LRUNODE( node );

        manager->num_bytes -= clazz->size_node( node, gset );

        FT_List_Remove( glyphs_lru, lrunode );

        clazz->destroy_node( node, gset );
      }

      bucket[0] = 0;
    }

    if ( clazz->done )
      clazz->done( gset );

    FREE( gset->buckets );
    FREE( gset );
  }


  FT_EXPORT_FUNC( FT_Error )
  FTC_GlyphSet_Lookup_Node( FTC_GlyphSet    gset,
                            FT_UInt         glyph_index,
                            FTC_GlyphNode  *anode )
  {
    FTC_Glyph_Cache         cache      = gset->cache;
    FTC_Manager             manager    = cache->root.manager;
    FT_UInt                 hash_index = glyph_index % gset->hash_size;
    FTC_GlyphNode*          bucket     = gset->buckets + hash_index;
    FTC_GlyphNode*          pnode      = bucket;
    FTC_GlyphNode           node;
    FT_Error                error;

    FTC_GlyphSet_Class*  clazz = gset->clazz;


    *anode = 0;
    
    for ( ;; )
    {
      node = *pnode;
      if (!node)
        break;
        
      if ( node->glyph_index == glyph_index )
      {
        /* we found it! -- move glyph to start of the lists */
        *pnode          = node->gset_next;
        node->gset_next = bucket[0];
        bucket[0]       = node;
        
        FT_List_Up( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) );
        *anode = node;
        return 0;
      }
      /* go to next node in bucket */
      pnode = &node->gset_next;
    }

    /* we didn't found the glyph image, we will now create a new one */
    error = clazz->new_node( gset, glyph_index, &node );
    if ( error )
      goto Exit;

    /* insert the node at the start of our bucket list */
    node->gset_next = bucket[0];
    bucket[0]       = node;

    /* insert the node at the start the global LRU glyph list */
    FT_List_Insert( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) );

    manager->num_bytes += clazz->size_node( node, gset );

    if (manager->num_bytes > manager->max_bytes)
    {
      FTC_GlyphNode_Ref   ( node );
      FTC_Manager_Compress( manager );
      FTC_GlyphNode_Unref ( node );
    }

    *anode = node;

  Exit:
    return error;
  }


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                   GLYPH SETS LRU CALLBACKS                    *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/


#define FTC_GSET_LRU_GET_CACHE( lru )   \
          ( (FTC_Glyph_Cache)(lru)->user_data )

#define FTC_GSET_LRU_GET_MANAGER( lru ) \
          FTC_GSET_LRU_GET_CACHE( lru )->manager

#define FTC_LRUNODE_GSET( node )        \
          ( (FTC_GlyphSet)(node)->root.data )


  LOCAL_FUNC_X
  FT_Error  ftc_glyph_set_lru_init( FT_Lru      lru,
                                    FT_LruNode  node )
  {
    FTC_Glyph_Cache  cache = FTC_GSET_LRU_GET_CACHE( lru );
    FT_Error         error;
    FTC_GlyphSet     gset;


    error = FTC_GlyphSet_New( cache, (FT_Pointer)node->key, &gset );
    if ( !error )
    {
      /* good, now set the gset index within the gset object */
      gset->gset_index = node - lru->nodes;
      node->root.data  = gset;
    }

    return error;
  }


  LOCAL_FUNC_X
  void  ftc_glyph_set_lru_done( FT_Lru      lru,
                                FT_LruNode  node )
  {
    FTC_GlyphSet  gset = FTC_LRUNODE_GSET( node );

    FT_UNUSED( lru );


    FTC_GlyphSet_Destroy( gset );
  }


  LOCAL_FUNC_X
  FT_Bool  ftc_glyph_set_lru_compare( FT_LruNode  node,
                                      FT_LruKey   key )
  {
    FTC_GlyphSet  gset = FTC_LRUNODE_GSET( node );


    return gset->clazz->compare( gset, (FT_Pointer)key );
  }


  FT_CPLUSPLUS( const FT_Lru_Class )  ftc_glyph_set_lru_class =
  {
    sizeof( FT_LruRec ),
    ftc_glyph_set_lru_init,
    ftc_glyph_set_lru_done,
    0,  /* no flush */
    ftc_glyph_set_lru_compare
  };


  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                GLYPH IMAGE CACHE OBJECTS                      *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/


  FT_EXPORT_FUNC( FT_Error )  FTC_Glyph_Cache_Init( FTC_Glyph_Cache  cache )
  {
    FT_Memory  memory = cache->root.memory;
    FT_Error   error;


    /* set up root node_class to be used by manager */
    cache->root.node_clazz =
      (FTC_CacheNode_Class*)&ftc_glyph_cache_node_class;

    /* The following is extremely important for ftc_destroy_glyph_image() */
    /* to work properly, as the second parameter that is sent to it       */
    /* through the cache manager is `user_data' and must be set to        */
    /* `cache' here.                                                      */
    /*                                                                    */
    cache->root.cache_user = cache;

    error = FT_Lru_New( &ftc_glyph_set_lru_class,
                        FTC_MAX_GLYPH_SETS,
                        cache,
                        memory,
                        1, /* pre_alloc == TRUE */
                        &cache->gsets_lru );
    return error;
  }


  FT_EXPORT_FUNC( void )  FTC_Glyph_Cache_Done( FTC_Glyph_Cache  cache )
  {
    /* discard glyph sets */
    FT_Lru_Done( cache->gsets_lru );
  }


/* END */