Edit

kc3-lang/freetype/src/sfnt/ttsbit.c

Branch :

  • Show log

    Commit

  • Author : Werner Lemberg
    Date : 2010-06-24 10:34:29
    Hash : f765e440
    Message : */*: Use module specific error names where appropriate.

  • src/sfnt/ttsbit.c
  • /***************************************************************************/
    /*                                                                         */
    /*  ttsbit.c                                                               */
    /*                                                                         */
    /*    TrueType and OpenType embedded bitmap support (body).                */
    /*                                                                         */
    /*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,   */
    /*            2010 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_INTERNAL_DEBUG_H
    #include FT_INTERNAL_STREAM_H
    #include FT_TRUETYPE_TAGS_H
    
      /*
       *  Alas, the memory-optimized sbit loader can't be used when implementing
       *  the `old internals' hack
       */
    #ifndef FT_CONFIG_OPTION_OLD_INTERNALS
    
    #include "ttsbit0.c"
    
    #else /* FT_CONFIG_OPTION_OLD_INTERNALS */
    
    #include <ft2build.h>
    #include FT_INTERNAL_DEBUG_H
    #include FT_INTERNAL_STREAM_H
    #include FT_TRUETYPE_TAGS_H
    #include "ttsbit.h"
    
    #include "sferrors.h"
    
    
      /*************************************************************************/
      /*                                                                       */
      /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
      /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
      /* messages during execution.                                            */
      /*                                                                       */
    #undef  FT_COMPONENT
    #define FT_COMPONENT  trace_ttsbit
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    blit_sbit                                                          */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Blits a bitmap from an input stream into a given target.  Supports */
      /*    x and y offsets as well as byte padded lines.                      */
      /*                                                                       */
      /* <Input>                                                               */
      /*    target      :: The target bitmap/pixmap.                           */
      /*                                                                       */
      /*    source      :: The input packed bitmap data.                       */
      /*                                                                       */
      /*    line_bits   :: The number of bits per line.                        */
      /*                                                                       */
      /*    byte_padded :: A flag which is true if lines are byte-padded.      */
      /*                                                                       */
      /*    x_offset    :: The horizontal offset.                              */
      /*                                                                       */
      /*    y_offset    :: The vertical offset.                                */
      /*                                                                       */
      /* <Note>                                                                */
      /*    IMPORTANT: The x and y offsets are relative to the top corner of   */
      /*               the target bitmap (unlike the normal TrueType           */
      /*               convention).  A positive y offset indicates a downwards */
      /*               direction!                                              */
      /*                                                                       */
      static void
      blit_sbit( FT_Bitmap*  target,
                 FT_Byte*    source,
                 FT_Int      line_bits,
                 FT_Bool     byte_padded,
                 FT_Int      x_offset,
                 FT_Int      y_offset,
                 FT_Int      source_height )
      {
        FT_Byte*   line_buff;
        FT_Int     line_incr;
        FT_Int     height;
    
        FT_UShort  acc;
        FT_UInt    loaded;
    
    
        /* first of all, compute starting write position */
        line_incr = target->pitch;
        line_buff = target->buffer;
    
        if ( line_incr < 0 )
          line_buff -= line_incr * ( target->rows - 1 );
    
        line_buff += ( x_offset >> 3 ) + y_offset * line_incr;
    
        /***********************************************************************/
        /*                                                                     */
        /* We use the extra-classic `accumulator' trick to extract the bits    */
        /* from the source byte stream.                                        */
        /*                                                                     */
        /* Namely, the variable `acc' is a 16-bit accumulator containing the   */
        /* last `loaded' bits from the input stream.  The bits are shifted to  */
        /* the upmost position in `acc'.                                       */
        /*                                                                     */
        /***********************************************************************/
    
        acc    = 0;  /* clear accumulator   */
        loaded = 0;  /* no bits were loaded */
    
        for ( height = source_height; height > 0; height-- )
        {
          FT_Byte*  cur   = line_buff;        /* current write cursor          */
          FT_Int    count = line_bits;        /* # of bits to extract per line */
          FT_Byte   shift = (FT_Byte)( x_offset & 7 ); /* current write shift  */
          FT_Byte   space = (FT_Byte)( 8 - shift );
    
    
          /* first of all, read individual source bytes */
          if ( count >= 8 )
          {
            count -= 8;
            {
              do
              {
                FT_Byte  val;
    
    
                /* ensure that there are at least 8 bits in the accumulator */
                if ( loaded < 8 )
                {
                  acc    |= (FT_UShort)((FT_UShort)*source++ << ( 8 - loaded ));
                  loaded += 8;
                }
    
                /* now write one byte */
                val = (FT_Byte)( acc >> 8 );
                if ( shift )
                {
                  cur[0] |= (FT_Byte)( val >> shift );
                  cur[1] |= (FT_Byte)( val << space );
                }
                else
                  cur[0] |= val;
    
                cur++;
                acc   <<= 8;  /* remove bits from accumulator */
                loaded -= 8;
                count  -= 8;
    
              } while ( count >= 0 );
            }
    
            /* restore `count' to correct value */
            count += 8;
          }
    
          /* now write remaining bits (count < 8) */
          if ( count > 0 )
          {
            FT_Byte  val;
    
    
            /* ensure that there are at least `count' bits in the accumulator */
            if ( (FT_Int)loaded < count )
            {
              acc    |= (FT_UShort)((FT_UShort)*source++ << ( 8 - loaded ));
              loaded += 8;
            }
    
            /* now write remaining bits */
            val     = (FT_Byte)( ( (FT_Byte)( acc >> 8 ) ) & ~( 0xFF >> count ) );
            cur[0] |= (FT_Byte)( val >> shift );
    
            if ( count > space )
              cur[1] |= (FT_Byte)( val << space );
    
            acc   <<= count;
            loaded -= count;
          }
    
          /* now, skip to next line */
          if ( byte_padded )
          {
            acc    = 0;
            loaded = 0;   /* clear accumulator on byte-padded lines */
          }
    
          line_buff += line_incr;
        }
      }
    
    
      static const FT_Frame_Field  sbit_metrics_fields[] =
      {
    #undef  FT_STRUCTURE
    #define FT_STRUCTURE  TT_SBit_MetricsRec
    
        FT_FRAME_START( 8 ),
          FT_FRAME_BYTE( height ),
          FT_FRAME_BYTE( width ),
    
          FT_FRAME_CHAR( horiBearingX ),
          FT_FRAME_CHAR( horiBearingY ),
          FT_FRAME_BYTE( horiAdvance ),
    
          FT_FRAME_CHAR( vertBearingX ),
          FT_FRAME_CHAR( vertBearingY ),
          FT_FRAME_BYTE( vertAdvance ),
        FT_FRAME_END
      };
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    Load_SBit_Const_Metrics                                            */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Loads the metrics for `EBLC' index tables format 2 and 5.          */
      /*                                                                       */
      /* <Input>                                                               */
      /*    range  :: The target range.                                        */
      /*                                                                       */
      /*    stream :: The input stream.                                        */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.                             */
      /*                                                                       */
      static FT_Error
      Load_SBit_Const_Metrics( TT_SBit_Range  range,
                               FT_Stream      stream )
      {
        FT_Error  error;
    
    
        if ( FT_READ_ULONG( range->image_size ) )
          return error;
    
        return FT_STREAM_READ_FIELDS( sbit_metrics_fields, &range->metrics );
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    Load_SBit_Range_Codes                                              */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Loads the range codes for `EBLC' index tables format 4 and 5.      */
      /*                                                                       */
      /* <Input>                                                               */
      /*    range        :: The target range.                                  */
      /*                                                                       */
      /*    stream       :: The input stream.                                  */
      /*                                                                       */
      /*    load_offsets :: A flag whether to load the glyph offset table.     */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.                             */
      /*                                                                       */
      static FT_Error
      Load_SBit_Range_Codes( TT_SBit_Range  range,
                             FT_Stream      stream,
                             FT_Bool        load_offsets )
      {
        FT_Error   error;
        FT_ULong   count, n, size;
        FT_Memory  memory = stream->memory;
    
    
        if ( FT_READ_ULONG( count ) )
          goto Exit;
    
        range->num_glyphs = count;
    
        /* Allocate glyph offsets table if needed */
        if ( load_offsets )
        {
          if ( FT_NEW_ARRAY( range->glyph_offsets, count ) )
            goto Exit;
    
          size = count * 4L;
        }
        else
          size = count * 2L;
    
        /* Allocate glyph codes table and access frame */
        if ( FT_NEW_ARRAY ( range->glyph_codes, count ) ||
             FT_FRAME_ENTER( size )                     )
          goto Exit;
    
        for ( n = 0; n < count; n++ )
        {
          range->glyph_codes[n] = FT_GET_USHORT();
    
          if ( load_offsets )
            range->glyph_offsets[n] = (FT_ULong)range->image_offset +
                                      FT_GET_USHORT();
        }
    
        FT_FRAME_EXIT();
    
      Exit:
        return error;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    Load_SBit_Range                                                    */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Loads a given `EBLC' index/range table.                            */
      /*                                                                       */
      /* <Input>                                                               */
      /*    range  :: The target range.                                        */
      /*                                                                       */
      /*    stream :: The input stream.                                        */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.                             */
      /*                                                                       */
      static FT_Error
      Load_SBit_Range( TT_SBit_Range  range,
                       FT_Stream      stream )
      {
        FT_Error   error;
        FT_Memory  memory = stream->memory;
    
    
        switch( range->index_format )
        {
        case 1:   /* variable metrics with 4-byte offsets */
        case 3:   /* variable metrics with 2-byte offsets */
          {
            FT_ULong  num_glyphs, n;
            FT_Int    size_elem;
            FT_Bool   large = FT_BOOL( range->index_format == 1 );
    
    
    
            if ( range->last_glyph < range->first_glyph )
            {
              error = SFNT_Err_Invalid_File_Format;
              goto Exit;
            }
    
            num_glyphs        = range->last_glyph - range->first_glyph + 1L;
            range->num_glyphs = num_glyphs;
            num_glyphs++;                       /* XXX: BEWARE - see spec */
    
            size_elem = large ? 4 : 2;
    
            if ( FT_NEW_ARRAY( range->glyph_offsets, num_glyphs ) ||
                 FT_FRAME_ENTER( num_glyphs * size_elem )         )
              goto Exit;
    
            for ( n = 0; n < num_glyphs; n++ )
              range->glyph_offsets[n] = (FT_ULong)( range->image_offset +
                                                    ( large ? FT_GET_ULONG()
                                                            : FT_GET_USHORT() ) );
            FT_FRAME_EXIT();
          }
          break;
    
        case 2:   /* all glyphs have identical metrics */
          error = Load_SBit_Const_Metrics( range, stream );
          break;
    
        case 4:
          error = Load_SBit_Range_Codes( range, stream, 1 );
          break;
    
        case 5:
          error = Load_SBit_Const_Metrics( range, stream );
          if ( !error )
            error = Load_SBit_Range_Codes( range, stream, 0 );
          break;
    
        default:
          error = SFNT_Err_Invalid_File_Format;
        }
    
      Exit:
        return error;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    tt_face_load_eblc                                                  */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Loads the table of embedded bitmap sizes for this face.            */
      /*                                                                       */
      /* <Input>                                                               */
      /*    face   :: The target face object.                                  */
      /*                                                                       */
      /*    stream :: The input stream.                                        */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.                             */
      /*                                                                       */
      FT_LOCAL_DEF( FT_Error )
      tt_face_load_eblc( TT_Face    face,
                         FT_Stream  stream )
      {
        FT_Error   error  = SFNT_Err_Ok;
        FT_Memory  memory = stream->memory;
        FT_Fixed   version;
        FT_ULong   num_strikes;
        FT_ULong   table_base;
    
        static const FT_Frame_Field  sbit_line_metrics_fields[] =
        {
    #undef  FT_STRUCTURE
    #define FT_STRUCTURE  TT_SBit_LineMetricsRec
    
          /* no FT_FRAME_START */
            FT_FRAME_CHAR( ascender ),
            FT_FRAME_CHAR( descender ),
            FT_FRAME_BYTE( max_width ),
    
            FT_FRAME_CHAR( caret_slope_numerator ),
            FT_FRAME_CHAR( caret_slope_denominator ),
            FT_FRAME_CHAR( caret_offset ),
    
            FT_FRAME_CHAR( min_origin_SB ),
            FT_FRAME_CHAR( min_advance_SB ),
            FT_FRAME_CHAR( max_before_BL ),
            FT_FRAME_CHAR( min_after_BL ),
            FT_FRAME_CHAR( pads[0] ),
            FT_FRAME_CHAR( pads[1] ),
          FT_FRAME_END
        };
    
        static const FT_Frame_Field  strike_start_fields[] =
        {
    #undef  FT_STRUCTURE
    #define FT_STRUCTURE  TT_SBit_StrikeRec
    
          /* no FT_FRAME_START */
            FT_FRAME_ULONG( ranges_offset ),
            FT_FRAME_SKIP_LONG,
            FT_FRAME_ULONG( num_ranges ),
            FT_FRAME_ULONG( color_ref ),
          FT_FRAME_END
        };
    
        static const FT_Frame_Field  strike_end_fields[] =
        {
          /* no FT_FRAME_START */
            FT_FRAME_USHORT( start_glyph ),
            FT_FRAME_USHORT( end_glyph ),
            FT_FRAME_BYTE  ( x_ppem ),
            FT_FRAME_BYTE  ( y_ppem ),
            FT_FRAME_BYTE  ( bit_depth ),
            FT_FRAME_CHAR  ( flags ),
          FT_FRAME_END
        };
    
    
        face->num_sbit_strikes = 0;
    
        /* this table is optional */
        error = face->goto_table( face, TTAG_EBLC, stream, 0 );
        if ( error )
          error = face->goto_table( face, TTAG_bloc, stream, 0 );
        if ( error )
          goto Exit;
    
        table_base = FT_STREAM_POS();
        if ( FT_FRAME_ENTER( 8L ) )
          goto Exit;
    
        version     = FT_GET_LONG();
        num_strikes = FT_GET_ULONG();
    
        FT_FRAME_EXIT();
    
        /* check version number and strike count */
        if ( version     != 0x00020000L ||
             num_strikes >= 0x10000L    )
        {
          FT_ERROR(( "tt_face_load_sbit_strikes: invalid table version\n" ));
          error = SFNT_Err_Invalid_File_Format;
    
          goto Exit;
        }
    
        /* allocate the strikes table */
        if ( FT_NEW_ARRAY( face->sbit_strikes, num_strikes ) )
          goto Exit;
    
        face->num_sbit_strikes = num_strikes;
    
        /* now read each strike table separately */
        {
          TT_SBit_Strike  strike = face->sbit_strikes;
          FT_ULong        count  = num_strikes;
    
    
          if ( FT_FRAME_ENTER( 48L * num_strikes ) )
            goto Exit;
    
          while ( count > 0 )
          {
            if ( FT_STREAM_READ_FIELDS( strike_start_fields, strike )             ||
                 FT_STREAM_READ_FIELDS( sbit_line_metrics_fields, &strike->hori ) ||
                 FT_STREAM_READ_FIELDS( sbit_line_metrics_fields, &strike->vert ) ||
                 FT_STREAM_READ_FIELDS( strike_end_fields, strike )               )
              break;
    
            count--;
            strike++;
          }
    
          FT_FRAME_EXIT();
        }
    
        /* allocate the index ranges for each strike table */
        {
          TT_SBit_Strike  strike = face->sbit_strikes;
          FT_ULong        count  = num_strikes;
    
    
          while ( count > 0 )
          {
            TT_SBit_Range  range;
            FT_ULong       count2 = strike->num_ranges;
    
    
            /* read each range */
            if ( FT_STREAM_SEEK( table_base + strike->ranges_offset ) ||
                 FT_FRAME_ENTER( strike->num_ranges * 8L )            )
              goto Exit;
    
            if ( FT_NEW_ARRAY( strike->sbit_ranges, strike->num_ranges ) )
              goto Exit;
    
            range = strike->sbit_ranges;
            while ( count2 > 0 )
            {
              range->first_glyph  = FT_GET_USHORT();
              range->last_glyph   = FT_GET_USHORT();
              range->table_offset = table_base + strike->ranges_offset +
                                      FT_GET_ULONG();
              count2--;
              range++;
            }
    
            FT_FRAME_EXIT();
    
            /* Now, read each index table */
            count2 = strike->num_ranges;
            range  = strike->sbit_ranges;
            while ( count2 > 0 )
            {
              /* Read the header */
              if ( FT_STREAM_SEEK( range->table_offset ) ||
                   FT_FRAME_ENTER( 8L )                  )
                goto Exit;
    
              range->index_format = FT_GET_USHORT();
              range->image_format = FT_GET_USHORT();
              range->image_offset = FT_GET_ULONG();
    
              FT_FRAME_EXIT();
    
              error = Load_SBit_Range( range, stream );
              if ( error )
                goto Exit;
    
              count2--;
              range++;
            }
    
            count--;
            strike++;
          }
        }
    
      Exit:
        return error;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    tt_face_free_eblc                                                  */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Releases the embedded bitmap tables.                               */
      /*                                                                       */
      /* <Input>                                                               */
      /*    face :: The target face object.                                    */
      /*                                                                       */
      FT_LOCAL_DEF( void )
      tt_face_free_eblc( TT_Face  face )
      {
        FT_Memory       memory       = face->root.memory;
        TT_SBit_Strike  strike       = face->sbit_strikes;
        TT_SBit_Strike  strike_limit = strike + face->num_sbit_strikes;
    
    
        if ( strike )
        {
          for ( ; strike < strike_limit; strike++ )
          {
            TT_SBit_Range  range       = strike->sbit_ranges;
            TT_SBit_Range  range_limit = range + strike->num_ranges;
    
    
            if ( range )
            {
              for ( ; range < range_limit; range++ )
              {
                /* release the glyph offsets and codes tables */
                /* where appropriate                          */
                FT_FREE( range->glyph_offsets );
                FT_FREE( range->glyph_codes );
              }
            }
            FT_FREE( strike->sbit_ranges );
            strike->num_ranges = 0;
          }
          FT_FREE( face->sbit_strikes );
        }
        face->num_sbit_strikes = 0;
      }
    
    
      FT_LOCAL_DEF( FT_Error )
      tt_face_set_sbit_strike( TT_Face          face,
                               FT_Size_Request  req,
                               FT_ULong*        astrike_index )
      {
        return FT_Match_Size( (FT_Face)face, req, 0, astrike_index );
      }
    
    
      FT_LOCAL_DEF( FT_Error )
      tt_face_load_strike_metrics( TT_Face           face,
                                   FT_ULong          strike_index,
                                   FT_Size_Metrics*  metrics )
      {
        TT_SBit_Strike  strike;
    
    
        if ( strike_index >= face->num_sbit_strikes )
          return SFNT_Err_Invalid_Argument;
    
        strike = face->sbit_strikes + strike_index;
    
        metrics->x_ppem = strike->x_ppem;
        metrics->y_ppem = strike->y_ppem;
    
        metrics->ascender  = strike->hori.ascender << 6;
        metrics->descender = strike->hori.descender << 6;
    
        /* XXX: Is this correct? */
        metrics->max_advance = ( strike->hori.min_origin_SB  +
                                 strike->hori.max_width      +
                                 strike->hori.min_advance_SB ) << 6;
    
        metrics->height = metrics->ascender - metrics->descender;
    
        return SFNT_Err_Ok;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    find_sbit_range                                                    */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Scans a given strike's ranges and return, for a given glyph        */
      /*    index, the corresponding sbit range, and `EBDT' offset.            */
      /*                                                                       */
      /* <Input>                                                               */
      /*    glyph_index   :: The glyph index.                                  */
      /*                                                                       */
      /*    strike        :: The source/current sbit strike.                   */
      /*                                                                       */
      /* <Output>                                                              */
      /*    arange        :: The sbit range containing the glyph index.        */
      /*                                                                       */
      /*    aglyph_offset :: The offset of the glyph data in `EBDT' table.     */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means the glyph index was found.           */
      /*                                                                       */
      static FT_Error
      find_sbit_range( FT_UInt          glyph_index,
                       TT_SBit_Strike   strike,
                       TT_SBit_Range   *arange,
                       FT_ULong        *aglyph_offset )
      {
        TT_SBit_RangeRec  *range, *range_limit;
    
    
        /* check whether the glyph index is within this strike's */
        /* glyph range                                           */
        if ( glyph_index < (FT_UInt)strike->start_glyph ||
             glyph_index > (FT_UInt)strike->end_glyph   )
          goto Fail;
    
        /* scan all ranges in strike */
        range       = strike->sbit_ranges;
        range_limit = range + strike->num_ranges;
        if ( !range )
          goto Fail;
    
        for ( ; range < range_limit; range++ )
        {
          if ( glyph_index >= (FT_UInt)range->first_glyph &&
               glyph_index <= (FT_UInt)range->last_glyph  )
          {
            FT_UShort  delta = (FT_UShort)( glyph_index - range->first_glyph );
    
    
            switch ( range->index_format )
            {
            case 1:
            case 3:
              *aglyph_offset = range->glyph_offsets[delta];
              break;
    
            case 2:
              *aglyph_offset = range->image_offset +
                               range->image_size * delta;
              break;
    
            case 4:
            case 5:
              {
                FT_ULong  n;
    
    
                for ( n = 0; n < range->num_glyphs; n++ )
                {
                  if ( (FT_UInt)range->glyph_codes[n] == glyph_index )
                  {
                    if ( range->index_format == 4 )
                      *aglyph_offset = range->glyph_offsets[n];
                    else
                      *aglyph_offset = range->image_offset +
                                       n * range->image_size;
                    goto Found;
                  }
                }
              }
    
            /* fall-through */
            default:
              goto Fail;
            }
    
          Found:
            /* return successfully! */
            *arange  = range;
            return SFNT_Err_Ok;
          }
        }
    
      Fail:
        *arange        = 0;
        *aglyph_offset = 0;
    
        return SFNT_Err_Invalid_Argument;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    tt_find_sbit_image                                                 */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Checks whether an embedded bitmap (an `sbit') exists for a given   */
      /*    glyph, at a given strike.                                          */
      /*                                                                       */
      /* <Input>                                                               */
      /*    face          :: The target face object.                           */
      /*                                                                       */
      /*    glyph_index   :: The glyph index.                                  */
      /*                                                                       */
      /*    strike_index  :: The current strike index.                         */
      /*                                                                       */
      /* <Output>                                                              */
      /*    arange        :: The SBit range containing the glyph index.        */
      /*                                                                       */
      /*    astrike       :: The SBit strike containing the glyph index.       */
      /*                                                                       */
      /*    aglyph_offset :: The offset of the glyph data in `EBDT' table.     */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.  Returns                    */
      /*    SFNT_Err_Invalid_Argument if no sbit exists for the requested      */
      /*    glyph.                                                             */
      /*                                                                       */
      FT_LOCAL( FT_Error )
      tt_find_sbit_image( TT_Face          face,
                          FT_UInt          glyph_index,
                          FT_ULong         strike_index,
                          TT_SBit_Range   *arange,
                          TT_SBit_Strike  *astrike,
                          FT_ULong        *aglyph_offset )
      {
        FT_Error        error;
        TT_SBit_Strike  strike;
    
    
        if ( !face->sbit_strikes                        ||
             ( face->num_sbit_strikes <= strike_index ) )
          goto Fail;
    
        strike = &face->sbit_strikes[strike_index];
    
        error = find_sbit_range( glyph_index, strike,
                                 arange, aglyph_offset );
        if ( error )
          goto Fail;
    
        *astrike = strike;
    
        return SFNT_Err_Ok;
    
      Fail:
        /* no embedded bitmap for this glyph in face */
        *arange        = 0;
        *astrike       = 0;
        *aglyph_offset = 0;
    
        return SFNT_Err_Invalid_Argument;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    tt_load_sbit_metrics                                               */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Gets the big metrics for a given SBit.                             */
      /*                                                                       */
      /* <Input>                                                               */
      /*    stream      :: The input stream.                                   */
      /*                                                                       */
      /*    range       :: The SBit range containing the glyph.                */
      /*                                                                       */
      /* <Output>                                                              */
      /*    big_metrics :: A big SBit metrics structure for the glyph.         */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.                             */
      /*                                                                       */
      /* <Note>                                                                */
      /*    The stream cursor must be positioned at the glyph's offset within  */
      /*    the `EBDT' table before the call.                                  */
      /*                                                                       */
      /*    If the image format uses variable metrics, the stream cursor is    */
      /*    positioned just after the metrics header in the `EBDT' table on    */
      /*    function exit.                                                     */
      /*                                                                       */
      FT_LOCAL( FT_Error )
      tt_load_sbit_metrics( FT_Stream        stream,
                            TT_SBit_Range    range,
                            TT_SBit_Metrics  metrics )
      {
        FT_Error  error = SFNT_Err_Ok;
    
    
        switch ( range->image_format )
        {
        case 1:
        case 2:
        case 8:
          /* variable small metrics */
          {
            TT_SBit_SmallMetricsRec  smetrics;
    
            static const FT_Frame_Field  sbit_small_metrics_fields[] =
            {
    #undef  FT_STRUCTURE
    #define FT_STRUCTURE  TT_SBit_SmallMetricsRec
    
              FT_FRAME_START( 5 ),
                FT_FRAME_BYTE( height ),
                FT_FRAME_BYTE( width ),
                FT_FRAME_CHAR( bearingX ),
                FT_FRAME_CHAR( bearingY ),
                FT_FRAME_BYTE( advance ),
              FT_FRAME_END
            };
    
    
            /* read small metrics */
            if ( FT_STREAM_READ_FIELDS( sbit_small_metrics_fields, &smetrics ) )
              goto Exit;
    
            /* convert it to a big metrics */
            metrics->height       = smetrics.height;
            metrics->width        = smetrics.width;
            metrics->horiBearingX = smetrics.bearingX;
            metrics->horiBearingY = smetrics.bearingY;
            metrics->horiAdvance  = smetrics.advance;
    
            /* these metrics are made up at a higher level when */
            /* needed.                                          */
            metrics->vertBearingX = 0;
            metrics->vertBearingY = 0;
            metrics->vertAdvance  = 0;
          }
          break;
    
        case 6:
        case 7:
        case 9:
          /* variable big metrics */
          if ( FT_STREAM_READ_FIELDS( sbit_metrics_fields, metrics ) )
            goto Exit;
          break;
    
        case 5:
        default:  /* constant metrics */
          if ( range->index_format == 2 || range->index_format == 5 )
            *metrics = range->metrics;
          else
            return SFNT_Err_Invalid_File_Format;
       }
    
      Exit:
        return error;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    crop_bitmap                                                        */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Crops a bitmap to its tightest bounding box, and adjusts its       */
      /*    metrics.                                                           */
      /*                                                                       */
      /* <InOut>                                                               */
      /*    map     :: The bitmap.                                             */
      /*                                                                       */
      /*    metrics :: The corresponding metrics structure.                    */
      /*                                                                       */
      static void
      crop_bitmap( FT_Bitmap*       map,
                   TT_SBit_Metrics  metrics )
      {
        /***********************************************************************/
        /*                                                                     */
        /* In this situation, some bounding boxes of embedded bitmaps are too  */
        /* large.  We need to crop it to a reasonable size.                    */
        /*                                                                     */
        /*      ---------                                                      */
        /*      |       |                -----                                 */
        /*      |  ***  |                |***|                                 */
        /*      |   *   |                | * |                                 */
        /*      |   *   |    ------>     | * |                                 */
        /*      |   *   |                | * |                                 */
        /*      |   *   |                | * |                                 */
        /*      |  ***  |                |***|                                 */
        /*      ---------                -----                                 */
        /*                                                                     */
        /***********************************************************************/
    
        FT_Int    rows, count;
        FT_Long   line_len;
        FT_Byte*  line;
    
    
        /***********************************************************************/
        /*                                                                     */
        /* first of all, check the top-most lines of the bitmap, and remove    */
        /* them if they're empty.                                              */
        /*                                                                     */
        {
          line     = (FT_Byte*)map->buffer;
          rows     = map->rows;
          line_len = map->pitch;
    
    
          for ( count = 0; count < rows; count++ )
          {
            FT_Byte*  cur   = line;
            FT_Byte*  limit = line + line_len;
    
    
            for ( ; cur < limit; cur++ )
              if ( cur[0] )
                goto Found_Top;
    
            /* the current line was empty - skip to next one */
            line  = limit;
          }
    
        Found_Top:
          /* check that we have at least one filled line */
          if ( count >= rows )
            goto Empty_Bitmap;
    
          /* now, crop the empty upper lines */
          if ( count > 0 )
          {
            line = (FT_Byte*)map->buffer;
    
            FT_MEM_MOVE( line, line + count * line_len,
                         ( rows - count ) * line_len );
    
            metrics->height       = (FT_Byte)( metrics->height - count );
            metrics->horiBearingY = (FT_Char)( metrics->horiBearingY - count );
            metrics->vertBearingY = (FT_Char)( metrics->vertBearingY - count );
    
            map->rows -= count;
            rows      -= count;
          }
        }
    
        /***********************************************************************/
        /*                                                                     */
        /* second, crop the lower lines                                        */
        /*                                                                     */
        {
          line = (FT_Byte*)map->buffer + ( rows - 1 ) * line_len;
    
          for ( count = 0; count < rows; count++ )
          {
            FT_Byte*  cur   = line;
            FT_Byte*  limit = line + line_len;
    
    
            for ( ; cur < limit; cur++ )
              if ( cur[0] )
                goto Found_Bottom;
    
            /* the current line was empty - skip to previous one */
            line -= line_len;
          }
    
        Found_Bottom:
          if ( count > 0 )
          {
            metrics->height  = (FT_Byte)( metrics->height - count );
            rows            -= count;
            map->rows       -= count;
          }
        }
    
        /***********************************************************************/
        /*                                                                     */
        /* third, get rid of the space on the left side of the glyph           */
        /*                                                                     */
        do
        {
          FT_Byte*  limit;
    
    
          line  = (FT_Byte*)map->buffer;
          limit = line + rows * line_len;
    
          for ( ; line < limit; line += line_len )
            if ( line[0] & 0x80 )
              goto Found_Left;
    
          /* shift the whole glyph one pixel to the left */
          line  = (FT_Byte*)map->buffer;
          limit = line + rows * line_len;
    
          for ( ; line < limit; line += line_len )
          {
            FT_Int    n, width = map->width;
            FT_Byte   old;
            FT_Byte*  cur = line;
    
    
            old = (FT_Byte)(cur[0] << 1);
            for ( n = 8; n < width; n += 8 )
            {
              FT_Byte  val;
    
    
              val    = cur[1];
              cur[0] = (FT_Byte)( old | ( val >> 7 ) );
              old    = (FT_Byte)( val << 1 );
              cur++;
            }
            cur[0] = old;
          }
    
          map->width--;
          metrics->horiBearingX++;
          metrics->vertBearingX++;
          metrics->width--;
    
        } while ( map->width > 0 );
    
      Found_Left:
    
        /***********************************************************************/
        /*                                                                     */
        /* finally, crop the bitmap width to get rid of the space on the right */
        /* side of the glyph.                                                  */
        /*                                                                     */
        do
        {
          FT_Int    right = map->width - 1;
          FT_Byte*  limit;
          FT_Byte   mask;
    
    
          line  = (FT_Byte*)map->buffer + ( right >> 3 );
          limit = line + rows * line_len;
          mask  = (FT_Byte)( 0x80 >> ( right & 7 ) );
    
          for ( ; line < limit; line += line_len )
            if ( line[0] & mask )
              goto Found_Right;
    
          /* crop the whole glyph to the right */
          map->width--;
          metrics->width--;
    
        } while ( map->width > 0 );
    
      Found_Right:
        /* all right, the bitmap was cropped */
        return;
    
      Empty_Bitmap:
        map->width      = 0;
        map->rows       = 0;
        map->pitch      = 0;
        map->pixel_mode = FT_PIXEL_MODE_MONO;
      }
    
    
      static FT_Error
      Load_SBit_Single( FT_Bitmap*       map,
                        FT_Int           x_offset,
                        FT_Int           y_offset,
                        FT_Int           pix_bits,
                        FT_UShort        image_format,
                        TT_SBit_Metrics  metrics,
                        FT_Stream        stream )
      {
        FT_Error  error;
    
    
        /* check that the source bitmap fits into the target pixmap */
        if ( x_offset < 0 || x_offset + metrics->width  > map->width ||
             y_offset < 0 || y_offset + metrics->height > map->rows  )
        {
          error = SFNT_Err_Invalid_Argument;
    
          goto Exit;
        }
    
        {
          FT_Int   glyph_width  = metrics->width;
          FT_Int   glyph_height = metrics->height;
          FT_Int   glyph_size;
          FT_Int   line_bits    = pix_bits * glyph_width;
          FT_Bool  pad_bytes    = 0;
    
    
          /* compute size of glyph image */
          switch ( image_format )
          {
          case 1:  /* byte-padded formats */
          case 6:
            {
              FT_Int  line_length;
    
    
              switch ( pix_bits )
              {
              case 1:
                line_length = ( glyph_width + 7 ) >> 3;
                break;
              case 2:
                line_length = ( glyph_width + 3 ) >> 2;
                break;
              case 4:
                line_length = ( glyph_width + 1 ) >> 1;
                break;
              default:
                line_length =   glyph_width;
              }
    
              glyph_size = glyph_height * line_length;
              pad_bytes  = 1;
            }
            break;
    
          case 2:
          case 5:
          case 7:
            line_bits  =   glyph_width  * pix_bits;
            glyph_size = ( glyph_height * line_bits + 7 ) >> 3;
            break;
    
          default:  /* invalid format */
            return SFNT_Err_Invalid_File_Format;
          }
    
          /* Now read data and draw glyph into target pixmap       */
          if ( FT_FRAME_ENTER( glyph_size ) )
            goto Exit;
    
          /* don't forget to multiply `x_offset' by `map->pix_bits' as */
          /* the sbit blitter doesn't make a difference between pixmap */
          /* depths.                                                   */
          blit_sbit( map, (FT_Byte*)stream->cursor, line_bits, pad_bytes,
                     x_offset * pix_bits, y_offset, metrics->height );
    
          FT_FRAME_EXIT();
        }
    
      Exit:
        return error;
      }
    
    
      static FT_Error
      Load_SBit_Image( TT_SBit_Strike   strike,
                       TT_SBit_Range    range,
                       FT_ULong         ebdt_pos,
                       FT_ULong         glyph_offset,
                       FT_GlyphSlot     slot,
                       FT_Int           x_offset,
                       FT_Int           y_offset,
                       FT_Stream        stream,
                       TT_SBit_Metrics  metrics,
                       FT_Int           depth )
      {
        FT_Memory   memory = stream->memory;
        FT_Bitmap*  map    = &slot->bitmap;
        FT_Error    error;
    
    
        /* place stream at beginning of glyph data and read metrics */
        if ( FT_STREAM_SEEK( ebdt_pos + glyph_offset ) )
          goto Exit;
    
        error = tt_load_sbit_metrics( stream, range, metrics );
        if ( error )
          goto Exit;
    
        /* This function is recursive.  At the top-level call, we  */
        /* compute the dimensions of the higher-level glyph to     */
        /* allocate the final pixmap buffer.                       */
        if ( depth == 0 )
        {
          FT_Long  size;
    
    
          map->width = metrics->width;
          map->rows  = metrics->height;
    
          switch ( strike->bit_depth )
          {
          case 1:
            map->pixel_mode = FT_PIXEL_MODE_MONO;
            map->pitch      = ( map->width + 7 ) >> 3;
            break;
    
          case 2:
            map->pixel_mode = FT_PIXEL_MODE_GRAY2;
            map->pitch      = ( map->width + 3 ) >> 2;
            break;
    
          case 4:
            map->pixel_mode = FT_PIXEL_MODE_GRAY4;
            map->pitch      = ( map->width + 1 ) >> 1;
            break;
    
          case 8:
            map->pixel_mode = FT_PIXEL_MODE_GRAY;
            map->pitch      = map->width;
            break;
    
          default:
            return SFNT_Err_Invalid_File_Format;
          }
    
          size = map->rows * map->pitch;
    
          /* check that there is no empty image */
          if ( size == 0 )
            goto Exit;     /* exit successfully! */
    
          error = ft_glyphslot_alloc_bitmap( slot, size );
          if (error)
            goto Exit;
        }
    
        switch ( range->image_format )
        {
        case 1:  /* single sbit image - load it */
        case 2:
        case 5:
        case 6:
        case 7:
          return Load_SBit_Single( map, x_offset, y_offset, strike->bit_depth,
                                   range->image_format, metrics, stream );
    
        case 8:  /* compound format */
          if ( FT_STREAM_SKIP( 1L ) )
          {
            error = SFNT_Err_Invalid_Stream_Skip;
            goto Exit;
          }
          /* fallthrough */
    
        case 9:
          break;
    
        default: /* invalid image format */
          return SFNT_Err_Invalid_File_Format;
        }
    
        /* All right, we have a compound format.  First of all, read */
        /* the array of elements.                                    */
        {
          TT_SBit_Component  components;
          TT_SBit_Component  comp;
          FT_UShort          num_components, count;
    
    
          if ( FT_READ_USHORT( num_components )           ||
               FT_NEW_ARRAY( components, num_components ) )
            goto Exit;
    
          count = num_components;
    
          if ( FT_FRAME_ENTER( 4L * num_components ) )
            goto Fail_Memory;
    
          for ( comp = components; count > 0; count--, comp++ )
          {
            comp->glyph_code = FT_GET_USHORT();
            comp->x_offset   = FT_GET_CHAR();
            comp->y_offset   = FT_GET_CHAR();
          }
    
          FT_FRAME_EXIT();
    
          /* Now recursively load each element glyph */
          count = num_components;
          comp  = components;
          for ( ; count > 0; count--, comp++ )
          {
            TT_SBit_Range       elem_range;
            TT_SBit_MetricsRec  elem_metrics;
            FT_ULong            elem_offset;
    
    
            /* find the range for this element */
            error = find_sbit_range( comp->glyph_code,
                                     strike,
                                     &elem_range,
                                     &elem_offset );
            if ( error )
              goto Fail_Memory;
    
            /* now load the element, recursively */
            error = Load_SBit_Image( strike,
                                     elem_range,
                                     ebdt_pos,
                                     elem_offset,
                                     slot,
                                     x_offset + comp->x_offset,
                                     y_offset + comp->y_offset,
                                     stream,
                                     &elem_metrics,
                                     depth + 1 );
            if ( error )
              goto Fail_Memory;
          }
    
        Fail_Memory:
          FT_FREE( components );
        }
    
      Exit:
        return error;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    tt_face_load_sbit_image                                            */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Loads a given glyph sbit image from the font resource.  This also  */
      /*    returns its metrics.                                               */
      /*                                                                       */
      /* <Input>                                                               */
      /*    face         :: The target face object.                            */
      /*                                                                       */
      /*    strike_index :: The current strike index.                          */
      /*                                                                       */
      /*    glyph_index  :: The current glyph index.                           */
      /*                                                                       */
      /*    load_flags   :: The glyph load flags (the code checks for the flag */
      /*                    FT_LOAD_CROP_BITMAP).                              */
      /*                                                                       */
      /*    stream       :: The input stream.                                  */
      /*                                                                       */
      /* <Output>                                                              */
      /*    map          :: The target pixmap.                                 */
      /*                                                                       */
      /*    metrics      :: A big sbit metrics structure for the glyph image.  */
      /*                                                                       */
      /* <Return>                                                              */
      /*    FreeType error code.  0 means success.  Returns an error if no     */
      /*    glyph sbit exists for the index.                                   */
      /*                                                                       */
      /*  <Note>                                                               */
      /*    The `map.buffer' field is always freed before the glyph is loaded. */
      /*                                                                       */
      FT_LOCAL_DEF( FT_Error )
      tt_face_load_sbit_image( TT_Face              face,
                               FT_ULong             strike_index,
                               FT_UInt              glyph_index,
                               FT_UInt              load_flags,
                               FT_Stream            stream,
                               FT_Bitmap           *map,
                               TT_SBit_MetricsRec  *metrics )
      {
        FT_Error        error;
        FT_ULong        ebdt_pos, glyph_offset;
    
        TT_SBit_Strike  strike;
        TT_SBit_Range   range;
    
    
        /* Check whether there is a glyph sbit for the current index */
        error = tt_find_sbit_image( face, glyph_index, strike_index,
                                    &range, &strike, &glyph_offset );
        if ( error )
          goto Exit;
    
        /* now, find the location of the `EBDT' table in */
        /* the font file                                 */
        error = face->goto_table( face, TTAG_EBDT, stream, 0 );
        if ( error )
          error = face->goto_table( face, TTAG_bdat, stream, 0 );
        if ( error )
          goto Exit;
    
        ebdt_pos = FT_STREAM_POS();
    
        error = Load_SBit_Image( strike, range, ebdt_pos, glyph_offset,
                                 face->root.glyph, 0, 0, stream, metrics, 0 );
        if ( error )
          goto Exit;
    
        /* setup vertical metrics if needed */
        if ( strike->flags & 1 )
        {
          /* in case of a horizontal strike only */
          FT_Int  advance;
    
    
          advance = strike->hori.ascender - strike->hori.descender;
    
          /* some heuristic values */
    
          metrics->vertBearingX = (FT_Char)(-metrics->width / 2 );
          metrics->vertBearingY = (FT_Char)( ( advance - metrics->height ) / 2 );
          metrics->vertAdvance  = (FT_Char)( advance * 12 / 10 );
        }
    
        /* Crop the bitmap now, unless specified otherwise */
        if ( load_flags & FT_LOAD_CROP_BITMAP )
          crop_bitmap( map, metrics );
    
      Exit:
        return error;
      }
    
    #endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
    
    
    /* END */