text: /***************************************************************************/
  /*                                                                         */
  /*  ftcsbits.c                                                             */
  /*                                                                         */
  /*    FreeType sbits manager (body).                                       */
  /*                                                                         */
  /*  Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2009 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 <ft2build.h>
  #include FT_CACHE_H
  #include "ftcsbits.h"
  #include FT_INTERNAL_OBJECTS_H
  #include FT_INTERNAL_DEBUG_H
  #include FT_ERRORS_H
  
  #include "ftccback.h"
  #include "ftcerror.h"
  
  #undef  FT_COMPONENT
  #define FT_COMPONENT  trace_cache
  
  
    /*************************************************************************/
    /*************************************************************************/
    /*****                                                               *****/
    /*****                     SBIT CACHE NODES                          *****/
    /*****                                                               *****/
    /*************************************************************************/
    /*************************************************************************/
  
  
    static FT_Error
    ftc_sbit_copy_bitmap( FTC_SBit    sbit,
                          FT_Bitmap*  bitmap,
                          FT_Memory   memory )
    {
      FT_Error  error;
      FT_Int    pitch = bitmap->pitch;
      FT_ULong  size;
  
  
      if ( pitch < 0 )
        pitch = -pitch;
  
      size = (FT_ULong)( pitch * bitmap->rows );
  
      if ( !FT_ALLOC( sbit->buffer, size ) )
        FT_MEM_COPY( sbit->buffer, bitmap->buffer, size );
  
      return error;
    }
  
  
    FT_LOCAL_DEF( void )
    ftc_snode_free( FTC_Node   ftcsnode,
                    FTC_Cache  cache )
    {
      FTC_SNode  snode  = (FTC_SNode)ftcsnode;
      FTC_SBit   sbit   = snode->sbits;
      FT_UInt    count  = snode->count;
      FT_Memory  memory = cache->memory;
  
  
      for ( ; count > 0; sbit++, count-- )
        FT_FREE( sbit->buffer );
  
      FTC_GNode_Done( FTC_GNODE( snode ), cache );
  
      FT_FREE( snode );
    }
  
  
    FT_LOCAL_DEF( void )
    FTC_SNode_Free( FTC_SNode  snode,
                    FTC_Cache  cache )
    {
      ftc_snode_free( FTC_NODE( snode ), cache );
    }
  
  
    /*
     *  This function tries to load a small bitmap within a given FTC_SNode.
     *  Note that it returns a non-zero error code _only_ in the case of
     *  out-of-memory condition.  For all other errors (e.g., corresponding
     *  to a bad font file), this function will mark the sbit as `unavailable'
     *  and return a value of 0.
     *
     *  You should also read the comment within the @ftc_snode_compare
     *  function below to see how out-of-memory is handled during a lookup.
     */
    static FT_Error
    ftc_snode_load( FTC_SNode    snode,
                    FTC_Manager  manager,
                    FT_UInt      gindex,
                    FT_ULong    *asize )
    {
      FT_Error          error;
      FTC_GNode         gnode  = FTC_GNODE( snode );
      FTC_Family        family = gnode->family;
      FT_Memory         memory = manager->memory;
      FT_Face           face;
      FTC_SBit          sbit;
      FTC_SFamilyClass  clazz;
  
  
      if ( (FT_UInt)(gindex - gnode->gindex) >= snode->count )
      {
        FT_ERROR(( "ftc_snode_load: invalid glyph index" ));
        return FTC_Err_Invalid_Argument;
      }
  
      sbit  = snode->sbits + ( gindex - gnode->gindex );
      clazz = (FTC_SFamilyClass)family->clazz;
  
      sbit->buffer = 0;
  
      error = clazz->family_load_glyph( family, gindex, manager, &face );
      if ( error )
        goto BadGlyph;
  
      {
        FT_Int        temp;
        FT_GlyphSlot  slot   = face->glyph;
        FT_Bitmap*    bitmap = &slot->bitmap;
        FT_Pos        xadvance, yadvance; /* FT_GlyphSlot->advance.{x|y} */
  
  
        if ( slot->format != FT_GLYPH_FORMAT_BITMAP )
        {
          FT_TRACE0(( "ftc_snode_load:"
                      " glyph loaded didn't return a bitmap\n" ));
          goto BadGlyph;
        }
  
        /* Check that our values fit into 8-bit containers!       */
        /* If this is not the case, our bitmap is too large       */
        /* and we will leave it as `missing' with sbit.buffer = 0 */
  
  #define CHECK_CHAR( d )  ( temp = (FT_Char)d, temp == d )
  #define CHECK_BYTE( d )  ( temp = (FT_Byte)d, temp == d )
  
        /* horizontal advance in pixels */
        xadvance = ( slot->advance.x + 32 ) >> 6;
        yadvance = ( slot->advance.y + 32 ) >> 6;
  
        if ( !CHECK_BYTE( bitmap->rows  )     ||
             !CHECK_BYTE( bitmap->width )     ||
             !CHECK_CHAR( bitmap->pitch )     ||
             !CHECK_CHAR( slot->bitmap_left ) ||
             !CHECK_CHAR( slot->bitmap_top  ) ||
             !CHECK_CHAR( xadvance )          ||
             !CHECK_CHAR( yadvance )          )
          goto BadGlyph;
  
        sbit->width     = (FT_Byte)bitmap->width;
        sbit->height    = (FT_Byte)bitmap->rows;
        sbit->pitch     = (FT_Char)bitmap->pitch;
        sbit->left      = (FT_Char)slot->bitmap_left;
        sbit->top       = (FT_Char)slot->bitmap_top;
        sbit->xadvance  = (FT_Char)xadvance;
        sbit->yadvance  = (FT_Char)yadvance;
        sbit->format    = (FT_Byte)bitmap->pixel_mode;
        sbit->max_grays = (FT_Byte)(bitmap->num_grays - 1);
  
        /* copy the bitmap into a new buffer -- ignore error */
        error = ftc_sbit_copy_bitmap( sbit, bitmap, memory );
  
        /* now, compute size */
        if ( asize )
          *asize = FT_ABS( sbit->pitch ) * sbit->height;
  
      } /* glyph loading successful */
  
      /* ignore the errors that might have occurred --   */
      /* we mark unloaded glyphs with `sbit.buffer == 0' */
      /* and `width == 255', `height == 0'               */
      /*                                                 */
      if ( error && error != FTC_Err_Out_Of_Memory )
      {
      BadGlyph:
        sbit->width  = 255;
        sbit->height = 0;
        sbit->buffer = NULL;
        error        = 0;
        if ( asize )
          *asize = 0;
      }
  
      return error;
    }
  
  
    FT_LOCAL_DEF( FT_Error )
    FTC_SNode_New( FTC_SNode  *psnode,
                   FTC_GQuery  gquery,
                   FTC_Cache   cache )
    {
      FT_Memory   memory = cache->memory;
      FT_Error    error;
      FTC_SNode   snode  = NULL;
      FT_UInt     gindex = gquery->gindex;
      FTC_Family  family = gquery->family;
  
      FTC_SFamilyClass  clazz = FTC_CACHE__SFAMILY_CLASS( cache );
      FT_UInt           total;
  
  
      total = clazz->family_get_count( family, cache->manager );
      if ( total == 0 || gindex >= total )
      {
        error = FT_Err_Invalid_Argument;
        goto Exit;
      }
  
      if ( !FT_NEW( snode ) )
      {
        FT_UInt  count, start;
  
  
        start = gindex - ( gindex % FTC_SBIT_ITEMS_PER_NODE );
        count = total - start;
        if ( count > FTC_SBIT_ITEMS_PER_NODE )
          count = FTC_SBIT_ITEMS_PER_NODE;
  
        FTC_GNode_Init( FTC_GNODE( snode ), start, family );
  
        snode->count = count;
  
        error = ftc_snode_load( snode,
                                cache->manager,
                                gindex,
                                NULL );
        if ( error )
        {
          FTC_SNode_Free( snode, cache );
          snode = NULL;
        }
      }
  
    Exit:
      *psnode = snode;
      return error;
    }
  
  
    FT_LOCAL_DEF( FT_Error )
    ftc_snode_new( FTC_Node   *ftcpsnode,
                   FT_Pointer  ftcgquery,
                   FTC_Cache   cache )
    {
      FTC_SNode  *psnode = (FTC_SNode*)ftcpsnode;
      FTC_GQuery  gquery = (FTC_GQuery)ftcgquery;
  
  
      return FTC_SNode_New( psnode, gquery, cache );
    }
  
  
    FT_LOCAL_DEF( FT_Offset )
    ftc_snode_weight( FTC_Node   ftcsnode,
                      FTC_Cache  cache )
    {
      FTC_SNode  snode = (FTC_SNode)ftcsnode;
      FT_UInt    count = snode->count;
      FTC_SBit   sbit  = snode->sbits;
      FT_Int     pitch;
      FT_Offset  size;
  
      FT_UNUSED( cache );
  
  
      FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE );
  
      /* the node itself */
      size = sizeof ( *snode );
  
      for ( ; count > 0; count--, sbit++ )
      {
        if ( sbit->buffer )
        {
          pitch = sbit->pitch;
          if ( pitch < 0 )
            pitch = -pitch;
  
          /* add the size of a given glyph image */
          size += pitch * sbit->height;
        }
      }
  
      return size;
    }
  
  
  #if 0
  
    FT_LOCAL_DEF( FT_Offset )
    FTC_SNode_Weight( FTC_SNode  snode )
    {
      return ftc_snode_weight( FTC_NODE( snode ), NULL );
    }
  
  #endif /* 0 */
  
  
    FT_LOCAL_DEF( FT_Bool )
    ftc_snode_compare( FTC_Node    ftcsnode,
                       FT_Pointer  ftcgquery,
                       FTC_Cache   cache )
    {
      FTC_SNode   snode  = (FTC_SNode)ftcsnode;
      FTC_GQuery  gquery = (FTC_GQuery)ftcgquery;
      FTC_GNode   gnode  = FTC_GNODE( snode );
      FT_UInt     gindex = gquery->gindex;
      FT_Bool     result;
  
  
      result = FT_BOOL( gnode->family == gquery->family                    &&
                        (FT_UInt)( gindex - gnode->gindex ) < snode->count );
      if ( result )
      {
        /* check if we need to load the glyph bitmap now */
        FTC_SBit  sbit = snode->sbits + ( gindex - gnode->gindex );
  
  
        /*
         *  The following code illustrates what to do when you want to
         *  perform operations that may fail within a lookup function.
         *
         *  Here, we want to load a small bitmap on-demand; we thus
         *  need to call the `ftc_snode_load' function which may return
         *  a non-zero error code only when we are out of memory (OOM).
         *
         *  The correct thing to do is to use @FTC_CACHE_TRYLOOP and
         *  @FTC_CACHE_TRYLOOP_END in order to implement a retry loop
         *  that is capable of flushing the cache incrementally when
         *  an OOM errors occur.
         *
         *  However, we need to `lock' the node before this operation to
         *  prevent it from being flushed within the loop.
         *
         *  When we exit the loop, we unlock the node, then check the `error'
         *  variable.  If it is non-zero, this means that the cache was
         *  completely flushed and that no usable memory was found to load
         *  the bitmap.
         *
         *  We then prefer to return a value of 0 (i.e., NO MATCH).  This
         *  ensures that the caller will try to allocate a new node.
         *  This operation consequently _fail_ and the lookup function
         *  returns the appropriate OOM error code.
         *
         *  Note that `buffer == NULL && width == 255' is a hack used to
         *  tag `unavailable' bitmaps in the array.  We should never try
         *  to load these.
         *
         */
  
        if ( sbit->buffer == NULL && sbit->width != 255 )
        {
          FT_ULong  size;
          FT_Error  error;
  
  
          ftcsnode->ref_count++;  /* lock node to prevent flushing */
                                  /* in retry loop                 */
  
          FTC_CACHE_TRYLOOP( cache )
          {
            error = ftc_snode_load( snode, cache->manager, gindex, &size );
          }
          FTC_CACHE_TRYLOOP_END();
  
          ftcsnode->ref_count--;  /* unlock the node */
  
          if ( error )
            result = 0;
          else
            cache->manager->cur_weight += size;
        }
      }
  
      return result;
    }
  
  
    FT_LOCAL_DEF( FT_Bool )
    FTC_SNode_Compare( FTC_SNode   snode,
                       FTC_GQuery  gquery,
                       FTC_Cache   cache )
    {
      return ftc_snode_compare( FTC_NODE( snode ), gquery, cache );
    }
  
  
  /* END */
  