(root)/
freetype-2.13.2/
src/
cache/
ftcsbits.c
       1  /****************************************************************************
       2   *
       3   * ftcsbits.c
       4   *
       5   *   FreeType sbits 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 "ftcsbits.h"
      21  #include <freetype/internal/ftobjs.h>
      22  #include <freetype/internal/ftdebug.h>
      23  #include <freetype/fterrors.h>
      24  
      25  #include "ftccback.h"
      26  #include "ftcerror.h"
      27  
      28  #undef  FT_COMPONENT
      29  #define FT_COMPONENT  cache
      30  
      31  
      32    /*************************************************************************/
      33    /*************************************************************************/
      34    /*****                                                               *****/
      35    /*****                     SBIT CACHE NODES                          *****/
      36    /*****                                                               *****/
      37    /*************************************************************************/
      38    /*************************************************************************/
      39  
      40  
      41    static FT_Error
      42    ftc_sbit_copy_bitmap( FTC_SBit    sbit,
      43                          FT_Bitmap*  bitmap,
      44                          FT_Memory   memory )
      45    {
      46      FT_Error  error;
      47      FT_Int    pitch = bitmap->pitch;
      48      FT_ULong  size;
      49  
      50  
      51      if ( pitch < 0 )
      52        pitch = -pitch;
      53  
      54      size = (FT_ULong)pitch * bitmap->rows;
      55  
      56      if ( !FT_QALLOC( sbit->buffer, size ) )
      57        FT_MEM_COPY( sbit->buffer, bitmap->buffer, size );
      58  
      59      return error;
      60    }
      61  
      62  
      63    FT_LOCAL_DEF( void )
      64    ftc_snode_free( FTC_Node   ftcsnode,
      65                    FTC_Cache  cache )
      66    {
      67      FTC_SNode  snode  = (FTC_SNode)ftcsnode;
      68      FTC_SBit   sbit   = snode->sbits;
      69      FT_UInt    count  = snode->count;
      70      FT_Memory  memory = cache->memory;
      71  
      72  
      73      for ( ; count > 0; sbit++, count-- )
      74        FT_FREE( sbit->buffer );
      75  
      76      FTC_GNode_Done( FTC_GNODE( snode ), cache );
      77  
      78      FT_FREE( snode );
      79    }
      80  
      81  
      82    FT_LOCAL_DEF( void )
      83    FTC_SNode_Free( FTC_SNode  snode,
      84                    FTC_Cache  cache )
      85    {
      86      ftc_snode_free( FTC_NODE( snode ), cache );
      87    }
      88  
      89  
      90    /*
      91     * This function tries to load a small bitmap within a given FTC_SNode.
      92     * Note that it returns a non-zero error code _only_ in the case of
      93     * out-of-memory condition.  For all other errors (e.g., corresponding
      94     * to a bad font file), this function will mark the sbit as `unavailable'
      95     * and return a value of 0.
      96     *
      97     * You should also read the comment within the @ftc_snode_compare
      98     * function below to see how out-of-memory is handled during a lookup.
      99     */
     100    static FT_Error
     101    ftc_snode_load( FTC_SNode    snode,
     102                    FTC_Manager  manager,
     103                    FT_UInt      gindex,
     104                    FT_ULong    *asize )
     105    {
     106      FT_Error          error;
     107      FTC_GNode         gnode  = FTC_GNODE( snode );
     108      FTC_Family        family = gnode->family;
     109      FT_Face           face;
     110      FTC_SBit          sbit;
     111      FTC_SFamilyClass  clazz;
     112  
     113  
     114      if ( gindex - gnode->gindex >= snode->count )
     115      {
     116        FT_ERROR(( "ftc_snode_load: invalid glyph index" ));
     117        return FT_THROW( Invalid_Argument );
     118      }
     119  
     120      sbit  = snode->sbits + ( gindex - gnode->gindex );
     121      clazz = (FTC_SFamilyClass)family->clazz;
     122  
     123      error = clazz->family_load_glyph( family, gindex, manager, &face );
     124      if ( error )
     125        goto BadGlyph;
     126  
     127      {
     128        FT_Int        temp;
     129        FT_GlyphSlot  slot   = face->glyph;
     130        FT_Bitmap*    bitmap = &slot->bitmap;
     131        FT_Pos        xadvance, yadvance; /* FT_GlyphSlot->advance.{x|y} */
     132  
     133  
     134        if ( slot->format != FT_GLYPH_FORMAT_BITMAP )
     135        {
     136          FT_TRACE0(( "ftc_snode_load:"
     137                      " glyph loaded didn't return a bitmap\n" ));
     138          goto BadGlyph;
     139        }
     140  
     141        /* Check whether our values fit into 8/16-bit containers! */
     142        /* If this is not the case, our bitmap is too large       */
     143        /* and we will leave it as `missing' with sbit.buffer = 0 */
     144  
     145  #define CHECK_CHAR( d )  ( temp = (FT_Char)d, (FT_Int) temp == (FT_Int) d )
     146  #define CHECK_BYTE( d )  ( temp = (FT_Byte)d, (FT_UInt)temp == (FT_UInt)d )
     147  #define CHECK_SHRT( d )  ( temp = (FT_Short)d, (FT_Int)temp == (FT_Int) d )
     148  
     149        /* horizontal advance in pixels */
     150        xadvance = ( slot->advance.x + 32 ) >> 6;
     151        yadvance = ( slot->advance.y + 32 ) >> 6;
     152  
     153        if ( !CHECK_BYTE( bitmap->rows  )     ||
     154             !CHECK_BYTE( bitmap->width )     ||
     155             !CHECK_SHRT( bitmap->pitch )     ||
     156             !CHECK_CHAR( slot->bitmap_left ) ||
     157             !CHECK_CHAR( slot->bitmap_top  ) ||
     158             !CHECK_CHAR( xadvance )          ||
     159             !CHECK_CHAR( yadvance )          )
     160        {
     161          FT_TRACE2(( "ftc_snode_load:"
     162                      " glyph too large for small bitmap cache\n"));
     163          goto BadGlyph;
     164        }
     165  
     166        sbit->width     = (FT_Byte)bitmap->width;
     167        sbit->height    = (FT_Byte)bitmap->rows;
     168        sbit->pitch     = (FT_Short)bitmap->pitch;
     169        sbit->left      = (FT_Char)slot->bitmap_left;
     170        sbit->top       = (FT_Char)slot->bitmap_top;
     171        sbit->xadvance  = (FT_Char)xadvance;
     172        sbit->yadvance  = (FT_Char)yadvance;
     173        sbit->format    = (FT_Byte)bitmap->pixel_mode;
     174        sbit->max_grays = (FT_Byte)( bitmap->num_grays - 1 );
     175  
     176        if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
     177        {
     178          /* take the bitmap ownership */
     179          sbit->buffer = bitmap->buffer;
     180          slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
     181        }
     182        else
     183        {
     184          /* copy the bitmap into a new buffer -- ignore error */
     185          error = ftc_sbit_copy_bitmap( sbit, bitmap, manager->memory );
     186        }
     187  
     188        /* now, compute size */
     189        if ( asize )
     190          *asize = (FT_ULong)FT_ABS( sbit->pitch ) * sbit->height;
     191  
     192      } /* glyph loading successful */
     193  
     194      /* ignore the errors that might have occurred --   */
     195      /* we mark unloaded glyphs with `sbit.buffer == 0' */
     196      /* and `width == 255', `height == 0'               */
     197      /*                                                 */
     198      if ( error && FT_ERR_NEQ( error, Out_Of_Memory ) )
     199      {
     200      BadGlyph:
     201        sbit->width  = 255;
     202        sbit->height = 0;
     203        sbit->buffer = NULL;
     204        error        = FT_Err_Ok;
     205        if ( asize )
     206          *asize = 0;
     207      }
     208  
     209      return error;
     210    }
     211  
     212  
     213    FT_LOCAL_DEF( FT_Error )
     214    FTC_SNode_New( FTC_SNode  *psnode,
     215                   FTC_GQuery  gquery,
     216                   FTC_Cache   cache )
     217    {
     218      FT_Memory   memory = cache->memory;
     219      FT_Error    error;
     220      FTC_SNode   snode  = NULL;
     221      FT_UInt     gindex = gquery->gindex;
     222      FTC_Family  family = gquery->family;
     223  
     224      FTC_SFamilyClass  clazz = FTC_CACHE_SFAMILY_CLASS( cache );
     225      FT_UInt           total;
     226      FT_UInt           node_count;
     227  
     228  
     229      total = clazz->family_get_count( family, cache->manager );
     230      if ( total == 0 || gindex >= total )
     231      {
     232        error = FT_THROW( Invalid_Argument );
     233        goto Exit;
     234      }
     235  
     236      if ( !FT_QNEW( snode ) )
     237      {
     238        FT_UInt  count, start;
     239  
     240  
     241        start = gindex - ( gindex % FTC_SBIT_ITEMS_PER_NODE );
     242        count = total - start;
     243        if ( count > FTC_SBIT_ITEMS_PER_NODE )
     244          count = FTC_SBIT_ITEMS_PER_NODE;
     245  
     246        FTC_GNode_Init( FTC_GNODE( snode ), start, family );
     247  
     248        snode->count = count;
     249        for ( node_count = 0; node_count < count; node_count++ )
     250        {
     251          snode->sbits[node_count].width  = 255;
     252          snode->sbits[node_count].height = 0;
     253          snode->sbits[node_count].buffer = NULL;
     254        }
     255  
     256        error = ftc_snode_load( snode,
     257                                cache->manager,
     258                                gindex,
     259                                NULL );
     260        if ( error )
     261        {
     262          FTC_SNode_Free( snode, cache );
     263          snode = NULL;
     264        }
     265      }
     266  
     267    Exit:
     268      *psnode = snode;
     269      return error;
     270    }
     271  
     272  
     273    FT_LOCAL_DEF( FT_Error )
     274    ftc_snode_new( FTC_Node   *ftcpsnode,
     275                   FT_Pointer  ftcgquery,
     276                   FTC_Cache   cache )
     277    {
     278      FTC_SNode  *psnode = (FTC_SNode*)ftcpsnode;
     279      FTC_GQuery  gquery = (FTC_GQuery)ftcgquery;
     280  
     281  
     282      return FTC_SNode_New( psnode, gquery, cache );
     283    }
     284  
     285  
     286    FT_LOCAL_DEF( FT_Offset )
     287    ftc_snode_weight( FTC_Node   ftcsnode,
     288                      FTC_Cache  cache )
     289    {
     290      FTC_SNode  snode = (FTC_SNode)ftcsnode;
     291      FT_UInt    count = snode->count;
     292      FTC_SBit   sbit  = snode->sbits;
     293      FT_Int     pitch;
     294      FT_Offset  size;
     295  
     296      FT_UNUSED( cache );
     297  
     298  
     299      FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE );
     300  
     301      /* the node itself */
     302      size = sizeof ( *snode );
     303  
     304      for ( ; count > 0; count--, sbit++ )
     305      {
     306        if ( sbit->buffer )
     307        {
     308          pitch = sbit->pitch;
     309          if ( pitch < 0 )
     310            pitch = -pitch;
     311  
     312          /* add the size of a given glyph image */
     313          size += (FT_Offset)pitch * sbit->height;
     314        }
     315      }
     316  
     317      return size;
     318    }
     319  
     320  
     321  #if 0
     322  
     323    FT_LOCAL_DEF( FT_Offset )
     324    FTC_SNode_Weight( FTC_SNode  snode )
     325    {
     326      return ftc_snode_weight( FTC_NODE( snode ), NULL );
     327    }
     328  
     329  #endif /* 0 */
     330  
     331  
     332    FT_LOCAL_DEF( FT_Bool )
     333    ftc_snode_compare( FTC_Node    ftcsnode,
     334                       FT_Pointer  ftcgquery,
     335                       FTC_Cache   cache,
     336                       FT_Bool*    list_changed )
     337    {
     338      FTC_SNode   snode  = (FTC_SNode)ftcsnode;
     339      FTC_GQuery  gquery = (FTC_GQuery)ftcgquery;
     340      FTC_GNode   gnode  = FTC_GNODE( snode );
     341      FT_UInt     gindex = gquery->gindex;
     342      FT_Bool     result;
     343  
     344  
     345      if ( list_changed )
     346        *list_changed = FALSE;
     347      result = FT_BOOL( gnode->family == gquery->family       &&
     348                        gindex - gnode->gindex < snode->count );
     349      if ( result )
     350      {
     351        /* check if we need to load the glyph bitmap now */
     352        FTC_SBit  sbit = snode->sbits + ( gindex - gnode->gindex );
     353  
     354  
     355        /*
     356         * The following code illustrates what to do when you want to
     357         * perform operations that may fail within a lookup function.
     358         *
     359         * Here, we want to load a small bitmap on-demand; we thus
     360         * need to call the `ftc_snode_load' function which may return
     361         * a non-zero error code only when we are out of memory (OOM).
     362         *
     363         * The correct thing to do is to use @FTC_CACHE_TRYLOOP and
     364         * @FTC_CACHE_TRYLOOP_END in order to implement a retry loop
     365         * that is capable of flushing the cache incrementally when
     366         * an OOM errors occur.
     367         *
     368         * However, we need to `lock' the node before this operation to
     369         * prevent it from being flushed within the loop.
     370         *
     371         * When we exit the loop, we unlock the node, then check the `error'
     372         * variable.  If it is non-zero, this means that the cache was
     373         * completely flushed and that no usable memory was found to load
     374         * the bitmap.
     375         *
     376         * We then prefer to return a value of 0 (i.e., NO MATCH).  This
     377         * ensures that the caller will try to allocate a new node.
     378         * This operation consequently _fail_ and the lookup function
     379         * returns the appropriate OOM error code.
     380         *
     381         * Note that `buffer == NULL && width == 255' is a hack used to
     382         * tag `unavailable' bitmaps in the array.  We should never try
     383         * to load these.
     384         *
     385         */
     386  
     387        if ( !sbit->buffer && sbit->width == 255 )
     388        {
     389          FT_ULong  size;
     390          FT_Error  error;
     391  
     392  
     393          ftcsnode->ref_count++;  /* lock node to prevent flushing */
     394                                  /* in retry loop                 */
     395  
     396          FTC_CACHE_TRYLOOP( cache )
     397          {
     398            error = ftc_snode_load( snode, cache->manager, gindex, &size );
     399          }
     400          FTC_CACHE_TRYLOOP_END( list_changed )
     401  
     402          ftcsnode->ref_count--;  /* unlock the node */
     403  
     404          if ( error )
     405            result = 0;
     406          else
     407            cache->manager->cur_weight += size;
     408        }
     409      }
     410  
     411      return result;
     412    }
     413  
     414  /* END */