(root)/
freetype-2.13.2/
src/
cache/
ftcmanag.c
       1  /****************************************************************************
       2   *
       3   * ftcmanag.c
       4   *
       5   *   FreeType Cache Manager (body).
       6   *
       7   * Copyright (C) 2000-2023 by
       8   * David Turner, Robert Wilhelm, and Werner Lemberg.
       9   *
      10   * This file is part of the FreeType project, and may only be used,
      11   * modified, and distributed under the terms of the FreeType project
      12   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
      13   * this file you indicate that you have read the license and
      14   * understand and accept it fully.
      15   *
      16   */
      17  
      18  
      19  #include <freetype/ftcache.h>
      20  #include "ftcmanag.h"
      21  #include <freetype/internal/ftobjs.h>
      22  #include <freetype/internal/ftdebug.h>
      23  #include <freetype/ftsizes.h>
      24  
      25  #include "ftccback.h"
      26  #include "ftcerror.h"
      27  
      28  
      29  #undef  FT_COMPONENT
      30  #define FT_COMPONENT  cache
      31  
      32  
      33    static FT_Error
      34    ftc_scaler_lookup_size( FTC_Manager  manager,
      35                            FTC_Scaler   scaler,
      36                            FT_Size     *asize )
      37    {
      38      FT_Face   face;
      39      FT_Size   size = NULL;
      40      FT_Error  error;
      41  
      42  
      43      error = FTC_Manager_LookupFace( manager, scaler->face_id, &face );
      44      if ( error )
      45        goto Exit;
      46  
      47      error = FT_New_Size( face, &size );
      48      if ( error )
      49        goto Exit;
      50  
      51      FT_Activate_Size( size );
      52  
      53      if ( scaler->pixel )
      54        error = FT_Set_Pixel_Sizes( face, scaler->width, scaler->height );
      55      else
      56        error = FT_Set_Char_Size( face,
      57                                  (FT_F26Dot6)scaler->width,
      58                                  (FT_F26Dot6)scaler->height,
      59                                  scaler->x_res,
      60                                  scaler->y_res );
      61      if ( error )
      62      {
      63        FT_Done_Size( size );
      64        size = NULL;
      65      }
      66  
      67    Exit:
      68      *asize = size;
      69      return error;
      70    }
      71  
      72  
      73    typedef struct  FTC_SizeNodeRec_
      74    {
      75      FTC_MruNodeRec  node;
      76      FT_Size         size;
      77      FTC_ScalerRec   scaler;
      78  
      79    } FTC_SizeNodeRec, *FTC_SizeNode;
      80  
      81  #define FTC_SIZE_NODE( x ) ( (FTC_SizeNode)( x ) )
      82  
      83  
      84    FT_CALLBACK_DEF( void )
      85    ftc_size_node_done( FTC_MruNode  ftcnode,
      86                        FT_Pointer   data )
      87    {
      88      FTC_SizeNode  node = (FTC_SizeNode)ftcnode;
      89      FT_Size       size = node->size;
      90      FT_UNUSED( data );
      91  
      92  
      93      if ( size )
      94        FT_Done_Size( size );
      95    }
      96  
      97  
      98    FT_CALLBACK_DEF( FT_Bool )
      99    ftc_size_node_compare( FTC_MruNode  ftcnode,
     100                           FT_Pointer   ftcscaler )
     101    {
     102      FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
     103      FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
     104      FTC_Scaler    scaler0 = &node->scaler;
     105  
     106  
     107      if ( FTC_SCALER_COMPARE( scaler0, scaler ) )
     108      {
     109        FT_Activate_Size( node->size );
     110        return 1;
     111      }
     112      return 0;
     113    }
     114  
     115  
     116    FT_CALLBACK_DEF( FT_Error )
     117    ftc_size_node_init( FTC_MruNode  ftcnode,
     118                        FT_Pointer   ftcscaler,
     119                        FT_Pointer   ftcmanager )
     120    {
     121      FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
     122      FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
     123      FTC_Manager   manager = (FTC_Manager)ftcmanager;
     124  
     125  
     126      node->scaler = scaler[0];
     127  
     128      return ftc_scaler_lookup_size( manager, scaler, &node->size );
     129    }
     130  
     131  
     132    FT_CALLBACK_DEF( FT_Error )
     133    ftc_size_node_reset( FTC_MruNode  ftcnode,
     134                         FT_Pointer   ftcscaler,
     135                         FT_Pointer   ftcmanager )
     136    {
     137      FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
     138      FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
     139      FTC_Manager   manager = (FTC_Manager)ftcmanager;
     140  
     141  
     142      FT_Done_Size( node->size );
     143  
     144      node->scaler = scaler[0];
     145  
     146      return ftc_scaler_lookup_size( manager, scaler, &node->size );
     147    }
     148  
     149  
     150    static
     151    const FTC_MruListClassRec  ftc_size_list_class =
     152    {
     153      sizeof ( FTC_SizeNodeRec ),
     154  
     155      ftc_size_node_compare,  /* FTC_MruNode_CompareFunc  node_compare */
     156      ftc_size_node_init,     /* FTC_MruNode_InitFunc     node_init    */
     157      ftc_size_node_reset,    /* FTC_MruNode_ResetFunc    node_reset   */
     158      ftc_size_node_done      /* FTC_MruNode_DoneFunc     node_done    */
     159    };
     160  
     161  
     162    /* helper function used by ftc_face_node_done */
     163    static FT_Bool
     164    ftc_size_node_compare_faceid( FTC_MruNode  ftcnode,
     165                                  FT_Pointer   ftcface_id )
     166    {
     167      FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
     168      FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;
     169  
     170  
     171      return FT_BOOL( node->scaler.face_id == face_id );
     172    }
     173  
     174  
     175    /* documentation is in ftcache.h */
     176  
     177    FT_EXPORT_DEF( FT_Error )
     178    FTC_Manager_LookupSize( FTC_Manager  manager,
     179                            FTC_Scaler   scaler,
     180                            FT_Size     *asize )
     181    {
     182      FT_Error     error;
     183      FTC_MruNode  mrunode;
     184  
     185  
     186      if ( !asize || !scaler )
     187        return FT_THROW( Invalid_Argument );
     188  
     189      *asize = NULL;
     190  
     191      if ( !manager )
     192        return FT_THROW( Invalid_Cache_Handle );
     193  
     194  #ifdef FTC_INLINE
     195  
     196      FTC_MRULIST_LOOKUP_CMP( &manager->sizes, scaler, ftc_size_node_compare,
     197                              mrunode, error );
     198  
     199  #else
     200      error = FTC_MruList_Lookup( &manager->sizes, scaler, &mrunode );
     201  #endif
     202  
     203      if ( !error )
     204        *asize = FTC_SIZE_NODE( mrunode )->size;
     205  
     206      return error;
     207    }
     208  
     209  
     210    /*************************************************************************/
     211    /*************************************************************************/
     212    /*****                                                               *****/
     213    /*****                    FACE MRU IMPLEMENTATION                    *****/
     214    /*****                                                               *****/
     215    /*************************************************************************/
     216    /*************************************************************************/
     217  
     218    typedef struct  FTC_FaceNodeRec_
     219    {
     220      FTC_MruNodeRec  node;
     221      FTC_FaceID      face_id;
     222      FT_Face         face;
     223  
     224    } FTC_FaceNodeRec, *FTC_FaceNode;
     225  
     226  #define FTC_FACE_NODE( x ) ( ( FTC_FaceNode )( x ) )
     227  
     228  
     229    FT_CALLBACK_DEF( FT_Error )
     230    ftc_face_node_init( FTC_MruNode  ftcnode,
     231                        FT_Pointer   ftcface_id,
     232                        FT_Pointer   ftcmanager )
     233    {
     234      FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
     235      FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;
     236      FTC_Manager   manager = (FTC_Manager)ftcmanager;
     237      FT_Error      error;
     238  
     239  
     240      node->face_id = face_id;
     241  
     242      error = manager->request_face( face_id,
     243                                     manager->library,
     244                                     manager->request_data,
     245                                     &node->face );
     246      if ( !error )
     247      {
     248        /* destroy initial size object; it will be re-created later */
     249        if ( node->face->size )
     250          FT_Done_Size( node->face->size );
     251      }
     252  
     253      return error;
     254    }
     255  
     256  
     257    FT_CALLBACK_DEF( void )
     258    ftc_face_node_done( FTC_MruNode  ftcnode,
     259                        FT_Pointer   ftcmanager )
     260    {
     261      FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
     262      FTC_Manager   manager = (FTC_Manager)ftcmanager;
     263  
     264  
     265      /* we must begin by removing all scalers for the target face */
     266      /* from the manager's list                                   */
     267      FTC_MruList_RemoveSelection( &manager->sizes,
     268                                   ftc_size_node_compare_faceid,
     269                                   node->face_id );
     270  
     271      /* all right, we can discard the face now */
     272      FT_Done_Face( node->face );
     273      node->face    = NULL;
     274      node->face_id = NULL;
     275    }
     276  
     277  
     278    FT_CALLBACK_DEF( FT_Bool )
     279    ftc_face_node_compare( FTC_MruNode  ftcnode,
     280                           FT_Pointer   ftcface_id )
     281    {
     282      FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
     283      FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;
     284  
     285  
     286      return FT_BOOL( node->face_id == face_id );
     287    }
     288  
     289  
     290    static
     291    const FTC_MruListClassRec  ftc_face_list_class =
     292    {
     293      sizeof ( FTC_FaceNodeRec),
     294  
     295      ftc_face_node_compare,  /* FTC_MruNode_CompareFunc  node_compare */
     296      ftc_face_node_init,     /* FTC_MruNode_InitFunc     node_init    */
     297      NULL,                   /* FTC_MruNode_ResetFunc    node_reset   */
     298      ftc_face_node_done      /* FTC_MruNode_DoneFunc     node_done    */
     299    };
     300  
     301  
     302    /* documentation is in ftcache.h */
     303  
     304    FT_EXPORT_DEF( FT_Error )
     305    FTC_Manager_LookupFace( FTC_Manager  manager,
     306                            FTC_FaceID   face_id,
     307                            FT_Face     *aface )
     308    {
     309      FT_Error     error;
     310      FTC_MruNode  mrunode;
     311  
     312  
     313      if ( !aface )
     314        return FT_THROW( Invalid_Argument );
     315  
     316      *aface = NULL;
     317  
     318      if ( !manager )
     319        return FT_THROW( Invalid_Cache_Handle );
     320  
     321      /* we break encapsulation for the sake of speed */
     322  #ifdef FTC_INLINE
     323  
     324      FTC_MRULIST_LOOKUP_CMP( &manager->faces, face_id, ftc_face_node_compare,
     325                              mrunode, error );
     326  
     327  #else
     328      error = FTC_MruList_Lookup( &manager->faces, face_id, &mrunode );
     329  #endif
     330  
     331      if ( !error )
     332        *aface = FTC_FACE_NODE( mrunode )->face;
     333  
     334      return error;
     335    }
     336  
     337  
     338    /*************************************************************************/
     339    /*************************************************************************/
     340    /*****                                                               *****/
     341    /*****                    CACHE MANAGER ROUTINES                     *****/
     342    /*****                                                               *****/
     343    /*************************************************************************/
     344    /*************************************************************************/
     345  
     346  
     347    /* documentation is in ftcache.h */
     348  
     349    FT_EXPORT_DEF( FT_Error )
     350    FTC_Manager_New( FT_Library          library,
     351                     FT_UInt             max_faces,
     352                     FT_UInt             max_sizes,
     353                     FT_ULong            max_bytes,
     354                     FTC_Face_Requester  requester,
     355                     FT_Pointer          req_data,
     356                     FTC_Manager        *amanager )
     357    {
     358      FT_Error     error;
     359      FT_Memory    memory;
     360      FTC_Manager  manager = NULL;
     361  
     362  
     363      if ( !library )
     364        return FT_THROW( Invalid_Library_Handle );
     365  
     366      if ( !amanager || !requester )
     367        return FT_THROW( Invalid_Argument );
     368  
     369      memory = library->memory;
     370  
     371      if ( FT_QNEW( manager ) )
     372        goto Exit;
     373  
     374      if ( max_faces == 0 )
     375        max_faces = FTC_MAX_FACES_DEFAULT;
     376  
     377      if ( max_sizes == 0 )
     378        max_sizes = FTC_MAX_SIZES_DEFAULT;
     379  
     380      if ( max_bytes == 0 )
     381        max_bytes = FTC_MAX_BYTES_DEFAULT;
     382  
     383      manager->library      = library;
     384      manager->memory       = memory;
     385      manager->max_weight   = max_bytes;
     386      manager->cur_weight   = 0;
     387  
     388      manager->request_face = requester;
     389      manager->request_data = req_data;
     390  
     391      FTC_MruList_Init( &manager->faces,
     392                        &ftc_face_list_class,
     393                        max_faces,
     394                        manager,
     395                        memory );
     396  
     397      FTC_MruList_Init( &manager->sizes,
     398                        &ftc_size_list_class,
     399                        max_sizes,
     400                        manager,
     401                        memory );
     402  
     403      manager->nodes_list = NULL;
     404      manager->num_nodes  = 0;
     405      manager->num_caches = 0;
     406  
     407      *amanager = manager;
     408  
     409    Exit:
     410      return error;
     411    }
     412  
     413  
     414    /* documentation is in ftcache.h */
     415  
     416    FT_EXPORT_DEF( void )
     417    FTC_Manager_Done( FTC_Manager  manager )
     418    {
     419      FT_Memory  memory;
     420      FT_UInt    idx;
     421  
     422  
     423      if ( !manager || !manager->library )
     424        return;
     425  
     426      memory = manager->memory;
     427  
     428      /* now discard all caches */
     429      for ( idx = manager->num_caches; idx-- > 0; )
     430      {
     431        FTC_Cache  cache = manager->caches[idx];
     432  
     433  
     434        if ( cache )
     435        {
     436          cache->clazz.cache_done( cache );
     437          FT_FREE( cache );
     438          manager->caches[idx] = NULL;
     439        }
     440      }
     441      manager->num_caches = 0;
     442  
     443      /* discard faces and sizes */
     444      FTC_MruList_Done( &manager->sizes );
     445      FTC_MruList_Done( &manager->faces );
     446  
     447      manager->library = NULL;
     448      manager->memory  = NULL;
     449  
     450      FT_FREE( manager );
     451    }
     452  
     453  
     454    /* documentation is in ftcache.h */
     455  
     456    FT_EXPORT_DEF( void )
     457    FTC_Manager_Reset( FTC_Manager  manager )
     458    {
     459      if ( !manager )
     460        return;
     461  
     462      FTC_MruList_Reset( &manager->sizes );
     463      FTC_MruList_Reset( &manager->faces );
     464  
     465      FTC_Manager_FlushN( manager, manager->num_nodes );
     466    }
     467  
     468  
     469  #ifdef FT_DEBUG_ERROR
     470  
     471    static void
     472    FTC_Manager_Check( FTC_Manager  manager )
     473    {
     474      FTC_Node  node, first;
     475  
     476  
     477      first = manager->nodes_list;
     478  
     479      /* check node weights */
     480      if ( first )
     481      {
     482        FT_Offset  weight = 0;
     483  
     484  
     485        node = first;
     486  
     487        do
     488        {
     489          FTC_Cache  cache = manager->caches[node->cache_index];
     490  
     491  
     492          if ( node->cache_index >= manager->num_caches )
     493            FT_TRACE0(( "FTC_Manager_Check: invalid node (cache index = %hu\n",
     494                        node->cache_index ));
     495          else
     496            weight += cache->clazz.node_weight( node, cache );
     497  
     498          node = FTC_NODE_NEXT( node );
     499  
     500        } while ( node != first );
     501  
     502        if ( weight != manager->cur_weight )
     503          FT_TRACE0(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n",
     504                      manager->cur_weight, weight ));
     505      }
     506  
     507      /* check circular list */
     508      if ( first )
     509      {
     510        FT_UFast  count = 0;
     511  
     512  
     513        node = first;
     514        do
     515        {
     516          count++;
     517          node = FTC_NODE_NEXT( node );
     518  
     519        } while ( node != first );
     520  
     521        if ( count != manager->num_nodes )
     522          FT_TRACE0(( "FTC_Manager_Check:"
     523                      " invalid cache node count %u instead of %u\n",
     524                      manager->num_nodes, count ));
     525      }
     526    }
     527  
     528  #endif /* FT_DEBUG_ERROR */
     529  
     530  
     531    /* `Compress' the manager's data, i.e., get rid of old cache nodes */
     532    /* that are not referenced anymore in order to limit the total     */
     533    /* memory used by the cache.                                       */
     534  
     535    /* documentation is in ftcmanag.h */
     536  
     537    FT_LOCAL_DEF( void )
     538    FTC_Manager_Compress( FTC_Manager  manager )
     539    {
     540      FTC_Node   node, prev, first;
     541  
     542  
     543      if ( !manager )
     544        return;
     545  
     546      first = manager->nodes_list;
     547  
     548  #ifdef FT_DEBUG_ERROR
     549      FTC_Manager_Check( manager );
     550  
     551      FT_TRACE0(( "compressing, weight = %ld, max = %ld, nodes = %u\n",
     552                  manager->cur_weight, manager->max_weight,
     553                  manager->num_nodes ));
     554  #endif
     555  
     556      if ( manager->cur_weight < manager->max_weight || !first )
     557        return;
     558  
     559      /* go to last node -- it's a circular list */
     560      prev = FTC_NODE_PREV( first );
     561      do
     562      {
     563        node = prev;
     564        prev = FTC_NODE_PREV( node );
     565  
     566        if ( node->ref_count <= 0 )
     567          ftc_node_destroy( node, manager );
     568  
     569      } while ( node != first && manager->cur_weight > manager->max_weight );
     570    }
     571  
     572  
     573    /* documentation is in ftcmanag.h */
     574  
     575    FT_LOCAL_DEF( FT_Error )
     576    FTC_Manager_RegisterCache( FTC_Manager      manager,
     577                               FTC_CacheClass   clazz,
     578                               FTC_Cache       *acache )
     579    {
     580      FT_Error   error = FT_ERR( Invalid_Argument );
     581      FTC_Cache  cache = NULL;
     582  
     583  
     584      if ( manager && clazz && acache )
     585      {
     586        FT_Memory  memory = manager->memory;
     587  
     588  
     589        if ( manager->num_caches >= FTC_MAX_CACHES )
     590        {
     591          error = FT_THROW( Too_Many_Caches );
     592          FT_ERROR(( "FTC_Manager_RegisterCache:"
     593                     " too many registered caches\n" ));
     594          goto Exit;
     595        }
     596  
     597        if ( !FT_QALLOC( cache, clazz->cache_size ) )
     598        {
     599          cache->manager   = manager;
     600          cache->memory    = memory;
     601          cache->clazz     = clazz[0];
     602          cache->org_class = clazz;
     603  
     604          /* THIS IS VERY IMPORTANT!  IT WILL WRETCH THE MANAGER */
     605          /* IF IT IS NOT SET CORRECTLY                          */
     606          cache->index = manager->num_caches;
     607  
     608          error = clazz->cache_init( cache );
     609          if ( error )
     610          {
     611            clazz->cache_done( cache );
     612            FT_FREE( cache );
     613            goto Exit;
     614          }
     615  
     616          manager->caches[manager->num_caches++] = cache;
     617        }
     618      }
     619  
     620    Exit:
     621      if ( acache )
     622        *acache = cache;
     623      return error;
     624    }
     625  
     626  
     627    FT_LOCAL_DEF( FT_UInt )
     628    FTC_Manager_FlushN( FTC_Manager  manager,
     629                        FT_UInt      count )
     630    {
     631      FTC_Node  first = manager->nodes_list;
     632      FTC_Node  prev, node;
     633      FT_UInt   result = 0;
     634  
     635  
     636      /* try to remove `count' nodes from the list */
     637      if ( !first || !count )
     638        return result;
     639  
     640      /* go to last node -- it's a circular list */
     641      prev = FTC_NODE_PREV( first );
     642      do
     643      {
     644        node = prev;
     645        prev = FTC_NODE_PREV( node );
     646  
     647        /* don't touch locked nodes */
     648        if ( node->ref_count <= 0 )
     649        {
     650          ftc_node_destroy( node, manager );
     651          result++;
     652        }
     653      } while ( node != first && result < count );
     654  
     655      return result;
     656    }
     657  
     658  
     659    /* documentation is in ftcache.h */
     660  
     661    FT_EXPORT_DEF( void )
     662    FTC_Manager_RemoveFaceID( FTC_Manager  manager,
     663                              FTC_FaceID   face_id )
     664    {
     665      FT_UInt  nn;
     666  
     667  
     668      if ( !manager )
     669        return;
     670  
     671      /* this will remove all FTC_SizeNode that correspond to
     672       * the face_id as well
     673       */
     674      FTC_MruList_RemoveSelection( &manager->faces,
     675                                   ftc_face_node_compare,
     676                                   face_id );
     677  
     678      for ( nn = 0; nn < manager->num_caches; nn++ )
     679        FTC_Cache_RemoveFaceID( manager->caches[nn], face_id );
     680    }
     681  
     682  
     683    /* documentation is in ftcache.h */
     684  
     685    FT_EXPORT_DEF( void )
     686    FTC_Node_Unref( FTC_Node     node,
     687                    FTC_Manager  manager )
     688    {
     689      if ( node                                    &&
     690           manager                                 &&
     691           node->cache_index < manager->num_caches )
     692        node->ref_count--;
     693    }
     694  
     695  
     696  /* END */