shithub: freetype+ttf2subf

ref: d7a046838f7f1970cd77d1d8350c46edf545e81b
dir: /src/base/ftglyph.c/

View raw version
/***************************************************************************/
/*                                                                         */
/*  ftglyph.c                                                              */
/*                                                                         */
/*    FreeType convenience functions to handle glyphs (body).              */
/*                                                                         */
/*  Copyright 1996-2000 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.                                        */
/*                                                                         */
/***************************************************************************/

  /*************************************************************************/
  /*                                                                       */
  /*  This file contains the definition of several convenience functions   */
  /*  that can be used by client applications to easily retrieve glyph     */
  /*  bitmaps and outlines from a given face.                              */
  /*                                                                       */
  /*  These functions should be optional if you are writing a font server  */
  /*  or text layout engine on top of FreeType.  However, they are pretty  */
  /*  handy for many other simple uses of the library.                     */
  /*                                                                       */
  /*************************************************************************/


#include <freetype/ftglyph.h>
#include <freetype/internal/ftobjs.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_glyph


  static
  void  ft_prepare_glyph( FT_Glyph  glyph,
                          FT_Face   face,
                          FT_Bool   vertical )
  {
    FT_Glyph_Metrics*  metrics = &face->glyph->metrics;


    glyph->memory = face->memory;
    glyph->width  = metrics->width;
    glyph->height = metrics->height;

    if ( vertical )
    {
      glyph->bearingX = metrics->vertBearingX;
      glyph->bearingY = metrics->vertBearingY;
      glyph->advance  = metrics->vertAdvance;
    }
    else
    {
      glyph->bearingX = metrics->horiBearingX;
      glyph->bearingY = metrics->horiBearingY;
      glyph->advance  = metrics->horiAdvance;
    }
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Glyph_Bitmap                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to directly return a monochrome bitmap glyph image */
  /*    from a face.                                                       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face        :: A handle to source face object.                     */
  /*    glyph_index :: A glyph index into the face.                        */
  /*    load_flags  :: Load flags (see FT_LOAD_FLAG_XXXX constants).       */
  /*    grays       :: The number of gray levels for anti-aliased bitmaps. */
  /*                   Set it to 0 if you want to render a monochrome      */
  /*                   bitmap.                                             */
  /*    origin      :: A pointer to the origin's position.  Set it to 0    */
  /*                   if the current transform is the identity.           */
  /*                                                                       */
  /* <Output>                                                              */
  /*    abitglyph   :: A pointer to the new bitmap glyph.                  */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    If the font contains glyph outlines, these will be automatically   */
  /*    converted to a bitmap according to the value of `grays'.           */
  /*                                                                       */
  /*    If `grays' is set to 0, the result is a 1-bit monochrome bitmap    */
  /*    otherwise, it is an 8-bit gray-level bitmap.                       */
  /*                                                                       */
  /*    The number of gray levels in the result anti-aliased bitmap might  */
  /*    not be `grays', depending on the current scan-converter            */
  /*    implementation.                                                    */
  /*                                                                       */
  /*    Note that it is not possible to generate 8-bit monochrome bitmaps  */
  /*    with this function.  Rather, use FT_Get_Glyph_Outline(), then      */
  /*    FT_Glyph_Render_Outline(), and provide your own span callbacks.    */
  /*                                                                       */
  /*    If the face doesn't contain scalable outlines, this function will  */
  /*    fail if the current transformation is not the identity, or if the  */
  /*    glyph origin's phase to the pixel grid is not 0 in both            */
  /*    directions!                                                        */
  /*                                                                       */
  FT_EXPORT_FUNC( FT_Error )  FT_Get_Glyph_Bitmap(
                                FT_Face          face,
                                FT_UInt          glyph_index,
                                FT_UInt          load_flags,
                                FT_Int           grays,
                                FT_Vector*       origin,
                                FT_BitmapGlyph*  abitglyph )
  {
    FT_Error         error;
    FT_Memory        memory;

    FT_BitmapGlyph   bitglyph;
    FT_Glyph         glyph;
    FT_Pos           origin_x = 0;
    FT_Pos           origin_y = 0;


    if ( !face )
      return FT_Err_Invalid_Face_Handle;

    if ( !abitglyph )
      return FT_Err_Invalid_Argument;

    *abitglyph = 0;

    if ( origin )
    {
      origin_x = origin->x & 63;
      origin_y = origin->y & 63;
    }

    /* check arguments whether the face's format is not scalable */
    if ( !( face->face_flags & FT_FACE_FLAG_SCALABLE ) &&
         face->transform_flags )
    {
      /* we can't transform bitmaps, so return an error */
      error = FT_Err_Unimplemented_Feature;
      goto Exit;
    }

    /* check that NO_SCALE and NO_RECURSE are not set */
    if ( load_flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE ) )
    {
      error = FT_Err_Invalid_Argument;
      goto Exit;
    }

    /* disable embedded bitmaps for transformed images */
    if ( face->face_flags & FT_FACE_FLAG_SCALABLE && face->transform_flags )
      load_flags |= FT_LOAD_NO_BITMAP;

    error = FT_Load_Glyph( face, glyph_index, load_flags );
    if ( error )
      goto Exit;

    /* now, handle bitmap and outline glyph images */
    memory = face->memory;
    switch ( face->glyph->format )
    {
    case ft_glyph_format_bitmap:
      {
        FT_Long     size;
        FT_Bitmap*  source;


        if ( ALLOC( bitglyph, sizeof ( *bitglyph ) ) )
          goto Exit;

        glyph             = (FT_Glyph)bitglyph;
        glyph->glyph_type = ft_glyph_type_bitmap;
        ft_prepare_glyph( glyph, face, 0 );

        source = &face->glyph->bitmap;
        size   = source->rows * source->pitch;
        if ( size < 0 )
          size = -size;

        bitglyph->bitmap = *source;
        if ( ALLOC( bitglyph->bitmap.buffer, size ) )
          goto Fail;

        /* copy the content of the source glyph */
        MEM_Copy( bitglyph->bitmap.buffer, source->buffer, size );
      }
      break;

    case ft_glyph_format_outline:
      {
        FT_BBox  cbox;
        FT_Int   width, height, pitch;
        FT_Long  size;


        /* transform the outline -- note that the original metrics are NOT */
        /* transformed by this, only the outline points themselves...      */
        FT_Outline_Translate( &face->glyph->outline,
                              origin_x,
                              origin_y );

        /* compute the size in pixels of the outline */
        FT_Outline_Get_CBox( &face->glyph->outline, &cbox );
        cbox.xMin &= -64;
        cbox.yMin &= -64;
        cbox.xMax  = ( cbox.xMax + 63 ) & -64;
        cbox.yMax  = ( cbox.yMax + 63 ) & -64;

        width  = ( cbox.xMax - cbox.xMin ) >> 6;
        height = ( cbox.yMax - cbox.yMin ) >> 6;

        /* allocate the pixel buffer for the glyph bitmap */
        if ( grays )
          /* some raster implementation need this */
          pitch = ( width + 3 ) & -4;
        else
          pitch = ( width + 7 ) >> 3;

        size  = pitch * height;
        if ( ALLOC( bitglyph, sizeof ( *bitglyph ) ) )
          goto Exit;

        glyph             = (FT_Glyph)bitglyph;
        glyph->glyph_type = ft_glyph_type_bitmap;
        ft_prepare_glyph( glyph, face, 0 );

        if ( ALLOC( bitglyph->bitmap.buffer, size ) )
          goto Fail;

        bitglyph->bitmap.width      = width;
        bitglyph->bitmap.rows       = height;
        bitglyph->bitmap.pitch      = pitch;
        bitglyph->bitmap.pixel_mode = grays ? ft_pixel_mode_grays
                                            : ft_pixel_mode_mono;
        bitglyph->bitmap.num_grays  = (short)grays;

        bitglyph->left = cbox.xMin >> 6;
        bitglyph->top  = cbox.yMax >> 6;

        /* render the monochrome outline into the target buffer */
        FT_Outline_Translate( &face->glyph->outline,
                              -cbox.xMin,
                              -cbox.yMin );
        error = FT_Outline_Get_Bitmap( face->driver->root.library,
                                       &face->glyph->outline,
                                       &bitglyph->bitmap );
        if ( error )
        {
          FREE( bitglyph->bitmap.buffer );
          goto Fail;
        }
      }
      break;

    default:
      error = FT_Err_Invalid_Glyph_Index;
      goto Exit;
    }

    *abitglyph = bitglyph;

  Exit:
    return error;

  Fail:
    FREE( glyph );
    goto Exit;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Get_Glyph_Outline                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    A function used to directly return a bitmap glyph image from a     */
  /*    face.  This is faster than calling FT_Load_Glyph() +               */
  /*    FT_Get_Outline_Bitmap().                                           */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face        :: A handle to the source face object.                 */
  /*    glyph_index :: A glyph index into face                             */
  /*    load_flags  :: Load flags (see FT_LOAD_FLAG_XXXX constants).       */
  /*                                                                       */
  /* <Output>                                                              */
  /*    vecglyph    :: A pointer to the new outline glyph.                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    This function will fail if the load flags FT_LOAD_NO_OUTLINE and   */
  /*    FT_LOAD_NO_RECURSE are set.                                        */
  /*                                                                       */
  FT_EXPORT_FUNC( FT_Error )  FT_Get_Glyph_Outline(
                                FT_Face           face,
                                FT_UInt           glyph_index,
                                FT_UInt           load_flags,
                                FT_OutlineGlyph*  vecglyph )
  {
    FT_Error         error;
    FT_Memory        memory;
    FT_OutlineGlyph  glyph;


    /* test for valid face delayed to FT_Load_Glyph() */

    if ( !vecglyph )
      return FT_Err_Invalid_Argument;

    *vecglyph = 0;

    /* check that RENDER and NO_RECURSE are not set */
    if ( load_flags & ( FT_LOAD_RENDER | FT_LOAD_NO_RECURSE ) )
    {
      error = FT_Err_Invalid_Argument;
      goto Exit;
    }

    /* disable the loading of embedded bitmaps */
    load_flags |= FT_LOAD_NO_BITMAP;

    error = FT_Load_Glyph( face, glyph_index, load_flags );
    if ( error )
      goto Exit;

    /* check that we really loaded an outline */
    if ( face->glyph->format != ft_glyph_format_outline )
    {
      error = FT_Err_Invalid_Glyph_Index;
      goto Exit;
    }

    /* now, create a new outline glyph and copy everything */
    memory = face->memory;
    if ( ALLOC( glyph, sizeof ( *glyph ) ) )
      goto Exit;

    ft_prepare_glyph( (FT_Glyph)glyph, face, 0 );
    glyph->metrics.glyph_type = ft_glyph_type_outline;

    error = FT_Outline_New( face->driver->root.library,
                            face->glyph->outline.n_points,
                            face->glyph->outline.n_contours,
                            &glyph->outline );
    if ( !error )
      error = FT_Outline_Copy( &face->glyph->outline, &glyph->outline );
    if ( error )
      goto Fail;

    *vecglyph = glyph;

  Exit:
    return error;

  Fail:
    FREE( glyph );
    goto Exit;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Done_Glyph                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Destroys a given glyph.                                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    glyph :: A handle to the target glyph object.                      */
  /*                                                                       */
  FT_EXPORT_FUNC( void )  FT_Done_Glyph( FT_Glyph  glyph )
  {
    if ( glyph )
    {
      FT_Memory  memory = glyph->memory;


      if ( glyph->glyph_type == ft_glyph_type_bitmap )
      {
        FT_BitmapGlyph  bit = (FT_BitmapGlyph)glyph;


        FREE( bit->bitmap.buffer );
      }
      else if ( glyph->glyph_type == ft_glyph_type_outline )
      {
        FT_OutlineGlyph  out = (FT_OutlineGlyph)glyph;


        if ( out->outline.flags & ft_outline_owner )
        {
          FREE( out->outline.points );
          FREE( out->outline.contours );
          FREE( out->outline.tags );
        }
      }

      FREE( glyph );
    }
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    FT_Glyph_Get_Box                                                   */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Returns the glyph image's bounding box in pixels.                  */
  /*                                                                       */
  /* <Input>                                                               */
  /*    glyph :: A handle to the target glyph object.                      */
  /*                                                                       */
  /* <Output>                                                              */
  /*    box   :: The glyph bounding box.  Coordinates are expressed in     */
  /*             _integer_ pixels, with exclusive maximal bounding values. */
  /*                                                                       */
  /* <Note>                                                                */
  /*    Coordinates are relative to the glyph origin, using the Y-upwards  */
  /*    convention.                                                        */
  /*                                                                       */
  /*    The width of the box in pixels is `box.xMax-box.xMin'; the height  */
  /*    is `box.yMax-box.yMin'.                                            */
  /*                                                                       */
  FT_EXPORT_FUNC( void )  FT_Glyph_Get_Box( FT_Glyph  glyph,
                                            FT_BBox*  box )
  {
    if ( !box )
      return;

    box->xMin = box->xMax = 0;
    box->yMin = box->yMax = 0;

    if ( glyph )
      switch ( glyph->glyph_type )
      {
      case ft_glyph_type_bitmap:
        {
          FT_BitmapGlyph  bit = (FT_BitmapGlyph)glyph;


          box->xMin = bit->left;
          box->xMax = box->xMin + bit->bitmap.width;
          box->yMax = bit->top;
          box->yMin = box->yMax - bit->bitmap.rows;
        }
        break;

      case ft_glyph_type_outline:
        {
          FT_OutlineGlyph  out = (FT_OutlineGlyph)glyph;


          FT_Outline_Get_CBox( &out->outline, box );
          box->xMin >>= 6;
          box->yMin >>= 6;
          box->xMax  = ( box->xMax + 63 ) >> 6;
          box->yMax  = ( box->yMax + 63 ) >> 6;
        }
        break;

      default:
        ;
      }
  }


  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****   EXPERIMENTAL EMBOLDENING/OUTLINING SUPPORT                    ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/

#if 0

  /* Compute the norm of a vector */

#ifdef FT_CONFIG_OPTION_OLD_CALCS

  static
  FT_Pos  ft_norm( FT_Vector*  vec )
  {
    FT_Int64  t1, t2;


    MUL_64( vec->x, vec->x, t1 );
    MUL_64( vec->y, vec->y, t2 );
    ADD_64( t1, t2, t1 );

    return (FT_Pos)SQRT_64( t1 );
  }

#else /* FT_CONFIG_OPTION_OLD_CALCS */

  static
  FT_Pos  ft_norm( FT_Vector*  vec )
  {
    FT_F26Dot6  u, v, d;
    FT_Int      shift;
    FT_ULong    H, L, L2, hi, lo, med;


    u = vec->x; if ( u < 0 ) u = -u;
    v = vec->y; if ( v < 0 ) v = -v;

    if ( u < v )
    {
      d = u;
      u = v;
      v = d;
    }

    /* check that we're not trying to normalize zero! */
    if ( u == 0 )
      return 0;

    /* compute (u*u + v*v) on 64 bits with two 32-bit registers [H:L] */
    hi  = (FT_ULong)u >> 16;
    lo  = (FT_ULong)u & 0xFFFF;
    med = hi * lo;

    H     = hi * hi + ( med >> 15 );
    med <<= 17;
    L     = lo * lo + med;
    if ( L < med )
      H++;

    hi  = (FT_ULong)v >> 16;
    lo  = (FT_ULong)v & 0xFFFF;
    med = hi * lo;

    H    += hi * hi + ( med >> 15 );
    med <<= 17;
    L2    = lo * lo + med;
    if ( L2 < med )
      H++;

    L += L2;
    if ( L < L2 )
      H++;

    /* if the value is smaller than 32 bits */
    shift = 0;
    if ( H == 0 )
    {
      while ( ( L & 0xC0000000UL ) == 0 )
      {
        L <<= 2;
        shift++;
      }
      return ( FT_Sqrt32( L ) >> shift );
    }
    else
    {
      while ( H )
      {
        L   = ( L >> 2 ) | ( H << 30 );
        H >>= 2;
        shift++;
      }
      return ( FT_Sqrt32( L ) << shift );
    }
  }

#endif /* FT_CONFIG_OPTION_OLD_CALCS */


  static
  int  ft_test_extrema( FT_Outline*  outline,
                        int          n )
  {
    FT_Vector  *prev, *cur, *next;
    FT_Pos      product;
    FT_Int      first, last;


    /* we need to compute the `previous' and `next' point */
    /* for these extrema.                                 */
    cur   = outline->points + n;
    prev  = cur - 1;
    next  = cur + 1;

    first = 0;
    for ( c = 0; c < outline->n_contours; c++ )
    {
      last  = outline->contours[c];

      if ( n == first )
        prev = outline->points + last;

      if ( n == last )
        next = outline->points + first;

      first = last + 1;
    }

    product = FT_MulDiv( cur->x - prev->x,   /* in.x  */
                         next->y - cur->y,   /* out.y */
                         0x40 )
              -
              FT_MulDiv( cur->y - prev->y,   /* in.y  */
                         next->x - cur->x,   /* out.x */
                         0x40 );

    if ( product )
      product = product > 0 ? 1 : -1;

    return product;
  }


  /* Compute the orientation of path filling.  It differs between TrueType */
  /* and Type1 formats.  We could use the `ft_outline_reverse_fill' flag,  */
  /* but it's better to re-compute it directly (it seems that this flag    */
  /* isn't correctly set for some weird composite glyphs for now).         */
  /*                                                                       */
  /* We do this by computing bounding box points, and computing their      */
  /* curvature.                                                            */
  /*                                                                       */
  /* The function returns either 1 or -1.                                  */
  /*                                                                       */
  static
  int  ft_get_orientation( FT_Outline*  outline )
  {
    FT_BBox  box;
    FT_BBox  indices;
    int      n, last;

    indices.xMin = -1;
    indices.yMin = -1;
    indices.xMax = -1;
    indices.yMax = -1;

    box.xMin = box.yMin = 32767;
    box.xMax = box.yMax = -32768;

    /* is it empty ? */
    if ( outline->n_contours < 1 )
      return 1;

    last = outline->contours[outline->n_contours - 1];

    for ( n = 0; n <= last; n++ )
    {
      FT_Pos  x, y;


      x = outline->points[n].x;
      if ( x < box.xMin )
      {
        box.xMin     = x;
        indices.xMin = n;
      }
      if ( x > box.xMax )
      {
        box.xMax     = x;
        indices.xMax = n;
      }

      y = outline->points[n].y;
      if ( y < box.yMin )
      {
        box.yMin     = y;
        indices.yMin = n;
      }
      if ( y > box.yMax )
      {
        box.yMax     = y;
        indices.yMax = n;
      }
    }

    /* test orientation of the xmin */
    return ft_test_extrema( outline, indices.xMin ) ||
           ft_test_extrema( outline, indices.yMin ) ||
           ft_test_extrema( outline, indices.xMax ) ||
           ft_test_extrema( outline, indices.yMax ) ||
           1;  /* this is an empty glyph? */
  }


  static
  FT_Error  ft_embolden( FT_Face      original,
                         FT_Outline*  outline,
                         FT_Pos*      advance )
  {
    FT_Vector  u, v;
    FT_Vector* points;
    FT_Vector  cur, prev, next;
    FT_Pos     distance;
    int        c, n, first, orientation;

    UNUSED( advance );


    /* compute control distance */
    distance = FT_MulFix( original->em_size / 60,
                          original->size->metrics.y_scale );

    orientation = ft_get_orientation( &original->glyph->outline );

    points = original->glyph->outline.points;

    first = 0;
    for ( c = 0; c < outline->n_contours; c++ )
    {
      int  last = outline->contours[c];


      prev = points[last];

      for ( n = first; n <= last; n++ )
      {
        FT_Pos     norm, delta, d;
        FT_Vector  in, out;


        cur = points[n];
        if ( n < last ) next = points[n + 1];
        else            next = points[first];

        /* compute the in and out vectors */
        in.x  = cur.x - prev.x;
        in.y  = cur.y - prev.y;

        out.x = next.x - cur.x;
        out.y = next.y - cur.y;

        /* compute U and V */
        norm = ft_norm( &in );
        u.x = orientation *  FT_DivFix( in.y, norm );
        u.y = orientation * -FT_DivFix( in.x, norm );

        norm = ft_norm( &out );
        v.x = orientation *  FT_DivFix( out.y, norm );
        v.y = orientation * -FT_DivFix( out.x, norm );

        d = distance;

        if ( (outline->flags[n] & FT_Curve_Tag_On) == 0 )
          d *= 2;

        /* Check discriminant for parallel vectors */
        delta = FT_MulFix( u.x, v.y ) - FT_MulFix( u.y, v.x );
        if ( delta > FT_BOLD_THRESHOLD || delta < -FT_BOLD_THRESHOLD )
        {
          /* Move point -- compute A and B */
          FT_Pos  x, y, A, B;


          A = d + FT_MulFix( cur.x, u.x ) + FT_MulFix( cur.y, u.y );
          B = d + FT_MulFix( cur.x, v.x ) + FT_MulFix( cur.y, v.y );

          x = FT_MulFix( A, v.y ) - FT_MulFix( B, u.y );
          y = FT_MulFix( B, u.x ) - FT_MulFix( A, v.x );

          outline->points[n].x = distance + FT_DivFix( x, delta );
          outline->points[n].y = distance + FT_DivFix( y, delta );
        }
        else
        {
          /* Vectors are nearly parallel */
          FT_Pos  x, y;

          x = distance + cur.x + FT_MulFix( d, u.x + v.x ) / 2;
          y = distance + cur.y + FT_MulFix( d, u.y + v.y ) / 2;

          outline->points[n].x = x;
          outline->points[n].y = y;
        }

        prev = cur;
      }

      first = last + 1;
    }

    if ( advance )
      *advance = ( *advance + distance * 4 ) & -64;

    return 0;
  }

#endif /* 0 -- EXPERIMENTAL STUFF! */


/* END */