/****************************************************************************
 *
 * ftcglyph.h
 *
 *   FreeType abstract glyph cache (specification).
 *
 * Copyright (C) 2000-2022 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.
 *
 */


  /*
   *
   * FTC_GCache is an _abstract_ cache object optimized to store glyph
   * data.  It works as follows:
   *
   * - It manages FTC_GNode objects. Each one of them can hold one or more
   *   glyph `items'.  Item types are not specified in the FTC_GCache but
   *   in classes that extend it.
   *
   * - Glyph attributes, like face ID, character size, render mode, etc.,
   *   can be grouped into abstract `glyph families'.  This avoids storing
   *   the attributes within the FTC_GCache, since it is likely that many
   *   FTC_GNodes will belong to the same family in typical uses.
   *
   * - Each FTC_GNode is thus an FTC_Node with two additional fields:
   *
   *   * gindex: A glyph index, or the first index in a glyph range.
   *   * family: A pointer to a glyph `family'.
   *
   * - Family types are not fully specific in the FTC_Family type, but
   *   by classes that extend it.
   *
   * Note that both FTC_ImageCache and FTC_SBitCache extend FTC_GCache.
   * They share an FTC_Family sub-class called FTC_BasicFamily which is
   * used to store the following data: face ID, pixel/point sizes, load
   * flags.  For more details see the file `src/cache/ftcbasic.c'.
   *
   * Client applications can extend FTC_GNode with their own FTC_GNode
   * and FTC_Family sub-classes to implement more complex caches (e.g.,
   * handling automatic synthesis, like obliquing & emboldening, colored
   * glyphs, etc.).
   *
   * See also the FTC_ICache & FTC_SCache classes in `ftcimage.h' and
   * `ftcsbits.h', which both extend FTC_GCache with additional
   * optimizations.
   *
   * A typical FTC_GCache implementation must provide at least the
   * following:
   *
   * - FTC_GNode sub-class, e.g. MyNode, with relevant methods:
   *     my_node_new            (must call FTC_GNode_Init)
   *     my_node_free           (must call FTC_GNode_Done)
   *     my_node_compare        (must call FTC_GNode_Compare)
   *     my_node_remove_faceid  (must call ftc_gnode_unselect in case
   *                             of match)
   *
   * - FTC_Family sub-class, e.g. MyFamily, with relevant methods:
   *     my_family_compare
   *     my_family_init
   *     my_family_reset (optional)
   *     my_family_done
   *
   * - FTC_GQuery sub-class, e.g. MyQuery, to hold cache-specific query
   *   data.
   *
   * - Constant structures for a FTC_GNodeClass.
   *
   * - MyCacheNew() can be implemented easily as a call to the convenience
   *   function FTC_GCache_New.
   *
   * - MyCacheLookup with a call to FTC_GCache_Lookup.  This function will
   *   automatically:
   *
   *   - Search for the corresponding family in the cache, or create
   *     a new one if necessary.  Put it in FTC_GQUERY(myquery).family
   *
   *   - Call FTC_Cache_Lookup.
   *
   *   If it returns NULL, you should create a new node, then call
   *   ftc_cache_add as usual.
   */


  /**************************************************************************
   *
   * Important: The functions defined in this file are only used to
   *            implement an abstract glyph cache class.  You need to
   *            provide additional logic to implement a complete cache.
   *
   */


  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*********                                                       *********/
  /*********             WARNING, THIS IS BETA CODE.               *********/
  /*********                                                       *********/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/


#ifndef FTCGLYPH_H_
#define FTCGLYPH_H_


#include "ftcmanag.h"


FT_BEGIN_HEADER


 /*
  * We can group glyphs into `families'.  Each family correspond to a
  * given face ID, character size, transform, etc.
  *
  * Families are implemented as MRU list nodes.  They are
  * reference-counted.
  */

  typedef struct  FTC_FamilyRec_
  {
    FTC_MruNodeRec    mrunode;
    FT_UInt           num_nodes; /* current number of nodes in this family */
    FTC_Cache         cache;
    FTC_MruListClass  clazz;

  } FTC_FamilyRec, *FTC_Family;

#define  FTC_FAMILY( x )    ( (FTC_Family)(x) )
#define  FTC_FAMILY_P( x )  ( (FTC_Family*)(x) )


  typedef struct  FTC_GNodeRec_
  {
    FTC_NodeRec      node;
    FTC_Family       family;
    FT_UInt          gindex;

  } FTC_GNodeRec, *FTC_GNode;

#define FTC_GNODE( x )    ( (FTC_GNode)(x) )
#define FTC_GNODE_P( x )  ( (FTC_GNode*)(x) )


  typedef struct  FTC_GQueryRec_
  {
    FT_UInt      gindex;
    FTC_Family   family;

  } FTC_GQueryRec, *FTC_GQuery;

#define FTC_GQUERY( x )  ( (FTC_GQuery)(x) )


  /**************************************************************************
   *
   * These functions are exported so that they can be called from
   * user-provided cache classes; otherwise, they are really part of the
   * cache sub-system internals.
   */

  /* must be called by derived FTC_Node_InitFunc routines */
  FT_LOCAL( void )
  FTC_GNode_Init( FTC_GNode   node,
                  FT_UInt     gindex,  /* glyph index for node */
                  FTC_Family  family );

#ifdef FTC_INLINE

  /* returns TRUE iff the query's glyph index correspond to the node;  */
  /* this assumes that the `family' and `hash' fields of the query are */
  /* already correctly set                                             */
  FT_LOCAL( FT_Bool )
  FTC_GNode_Compare( FTC_GNode   gnode,
                     FTC_GQuery  gquery,
                     FTC_Cache   cache,
                     FT_Bool*    list_changed );

#endif

  /* call this function to clear a node's family -- this is necessary */
  /* to implement the `node_remove_faceid' cache method correctly     */
  FT_LOCAL( void )
  FTC_GNode_UnselectFamily( FTC_GNode  gnode,
                            FTC_Cache  cache );

  /* must be called by derived FTC_Node_DoneFunc routines */
  FT_LOCAL( void )
  FTC_GNode_Done( FTC_GNode  node,
                  FTC_Cache  cache );


  FT_LOCAL( void )
  FTC_Family_Init( FTC_Family  family,
                   FTC_Cache   cache );

  typedef struct FTC_GCacheRec_
  {
    FTC_CacheRec    cache;
    FTC_MruListRec  families;

  } FTC_GCacheRec, *FTC_GCache;

#define FTC_GCACHE( x )  ((FTC_GCache)(x))


#if 0
  /* can be used as @FTC_Cache_InitFunc */
  FT_LOCAL( FT_Error )
  FTC_GCache_Init( FTC_GCache  cache );
#endif


#if 0
  /* can be used as @FTC_Cache_DoneFunc */
  FT_LOCAL( void )
  FTC_GCache_Done( FTC_GCache  cache );
#endif


  /* the glyph cache class adds fields for the family implementation */
  typedef struct  FTC_GCacheClassRec_
  {
    FTC_CacheClassRec  clazz;
    FTC_MruListClass   family_class;

  } FTC_GCacheClassRec;

  typedef const FTC_GCacheClassRec*   FTC_GCacheClass;

#define FTC_GCACHE_CLASS( x )  ((FTC_GCacheClass)(x))

#define FTC_CACHE_GCACHE_CLASS( x ) \
          FTC_GCACHE_CLASS( FTC_CACHE( x )->org_class )
#define FTC_CACHE_FAMILY_CLASS( x ) \
          ( (FTC_MruListClass)FTC_CACHE_GCACHE_CLASS( x )->family_class )


  /* convenience function; use it instead of FTC_Manager_Register_Cache */
  FT_LOCAL( FT_Error )
  FTC_GCache_New( FTC_Manager       manager,
                  FTC_GCacheClass   clazz,
                  FTC_GCache       *acache );

#ifndef FTC_INLINE
  FT_LOCAL( FT_Error )
  FTC_GCache_Lookup( FTC_GCache   cache,
                     FT_Offset    hash,
                     FT_UInt      gindex,
                     FTC_GQuery   query,
                     FTC_Node    *anode );
#endif


  /* */


#define FTC_FAMILY_FREE( family, cache )                      \
          FTC_MruList_Remove( &FTC_GCACHE((cache))->families, \
                              (FTC_MruNode)(family) )


#ifdef FTC_INLINE

#define FTC_GCACHE_LOOKUP_CMP( cache, famcmp, nodecmp, hash,                \
                               gindex, query, node, error )                 \
  FT_BEGIN_STMNT                                                            \
    FTC_GCache               _gcache   = FTC_GCACHE( cache );               \
    FTC_GQuery               _gquery   = (FTC_GQuery)( query );             \
    FTC_MruNode_CompareFunc  _fcompare = (FTC_MruNode_CompareFunc)(famcmp); \
    FTC_MruNode              _mrunode;                                      \
                                                                            \
                                                                            \
    _gquery->gindex = (gindex);                                             \
                                                                            \
    FTC_MRULIST_LOOKUP_CMP( &_gcache->families, _gquery, _fcompare,         \
                            _mrunode, error );                              \
    _gquery->family = FTC_FAMILY( _mrunode );                               \
    if ( !error )                                                           \
    {                                                                       \
      FTC_Family  _gqfamily = _gquery->family;                              \
                                                                            \
                                                                            \
      _gqfamily->num_nodes++;                                               \
                                                                            \
      FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error );     \
                                                                            \
      if ( --_gqfamily->num_nodes == 0 )                                    \
        FTC_FAMILY_FREE( _gqfamily, _gcache );                              \
    }                                                                       \
  FT_END_STMNT
  /* */

#else /* !FTC_INLINE */

#define FTC_GCACHE_LOOKUP_CMP( cache, famcmp, nodecmp, hash,          \
                               gindex, query, node, error )           \
   FT_BEGIN_STMNT                                                     \
                                                                      \
     error = FTC_GCache_Lookup( FTC_GCACHE( cache ), hash, gindex,    \
                                FTC_GQUERY( query ), &node );         \
                                                                      \
   FT_END_STMNT

#endif /* !FTC_INLINE */


FT_END_HEADER


#endif /* FTCGLYPH_H_ */


/* END */
