/*******************************************************************
 *
 *  ttobjs.c                                                     1.0
 *
 *    Objects manager.
 *
 *  Copyright 1996-1998 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 "ttobjs.h"
#include "ttfile.h"
#include "ttcalc.h"
#include "ttmemory.h"
#include "ttload.h"
#include "ttinterp.h"
#include "ttdebug.h"


/* Add extensions definition */
#ifdef TT_CONFIG_OPTION_EXTEND_ENGINE
#include "ttextend.h"
#endif

/* Required by tracing mode */
#undef   TT_COMPONENT
#define  TT_COMPONENT  trace_objs

/*******************************************************************
 *
 *  Function    :  New_Context
 *
 *  Description :  Creates a new execution context for a given
 *                 face object.
 *
 ******************************************************************/

  LOCAL_FUNC
  PExecution_Context  New_Context( PFace  face )
  {
    PEngine_Instance    engine;
    PExecution_Context  exec;


    if ( !face )
      return NULL;

    engine = face->engine;
    CACHE_New( engine->objs_exec_cache, exec, face );
    return exec;
  }


/*******************************************************************
 *
 *  Function    :  Done_Context
 *
 *  Description :  Discards an execution context.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Done_Context( PExecution_Context  exec )
  {
    PEngine_Instance  engine;


    if ( !exec )
      return TT_Err_Ok;

    engine = exec->face->engine;
    return CACHE_Done( engine->objs_exec_cache, exec );
  }


#if 0

/*******************************************************************
 *
 *  Function    :  New_Instance
 *
 *  Description :  Creates a new instance for a given face object.
 *
 ******************************************************************/

  LOCAL_FUNC
  PInstance  New_Instance( PFace  face )
  {
    PInstance  ins;


    if ( !face )
      return NULL;

    CACHE_New( &face->instances, ins, face );

    return ins;
  }


/*******************************************************************
 *
 *  Function    :  Done_Instance
 *
 *  Description :  Discards an instance.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Done_Instance( PInstance  instance )
  {
    return CACHE_Done( &instance->owner->instances, instance );
  }

#endif


/*******************************************************************
 *                                                                 *
 *                     GLYPH ZONE FUNCTIONS                        *
 *                                                                 *
 *                                                                 *
 *******************************************************************/

/*******************************************************************
 *
 *  Function    :  New_Glyph_Zone
 *
 *  Description :  Allocates a new glyph zone
 *
 *  Input  :  pts          pointer to the target glyph zone record
 *            maxPoints    capacity of glyph zone in points
 *            maxContours  capacity of glyph zone in contours
 *
 *  Return :  Error code.
 *
 *****************************************************************/

 static
 TT_Error  New_Glyph_Zone( PGlyph_Zone  pts,
                           UShort       maxPoints,
                           UShort       maxContours )
 {
   TT_Error  error;


   if ( ALLOC( pts->org, maxPoints * 2 * sizeof ( TT_F26Dot6 ) ) ||
        ALLOC( pts->cur, maxPoints * 2 * sizeof ( TT_F26Dot6 ) ) ||
        ALLOC( pts->touch, maxPoints * sizeof ( Byte )         ) ||
        ALLOC( pts->contours, maxContours * sizeof ( Short ) ) )
     return error;

   return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    :  Done_Glyph_Zone
 *
 *  Description :  Deallocates a glyph zone
 *
 *  Input  :  pts          pointer to the target glyph zone record
 *
 *  Return :  Error code.
 *
 *****************************************************************/

 static
 TT_Error  Done_Glyph_Zone( PGlyph_Zone  pts )
 {
   FREE( pts->contours );
   FREE( pts->touch );
   FREE( pts->cur );
   FREE( pts->org );

   return TT_Err_Ok;
 }



/*******************************************************************
 *                                                                 *
 *                     CODERANGE FUNCTIONS                         *
 *                                                                 *
 *******************************************************************/

/*******************************************************************
 *
 *  Function    :  Goto_CodeRange
 *
 *  Description :  Switch to a new code range (updates Code and IP).
 *
 *  Input  :  exec    target execution context
 *            range   new execution code range
 *            IP      new IP in new code range
 *
 *  Output :  SUCCESS on success.  FAILURE on error (no code range).
 *
 *****************************************************************/

  LOCAL_FUNC
  TT_Error  Goto_CodeRange( PExecution_Context  exec,
                            Int                 range,
                            Long                IP )
  {
    PCodeRange  cr;


    if ( range < 1 || range > 3 )
      return TT_Err_Bad_Argument;

    cr = &exec->codeRangeTable[range - 1];

    if ( cr->Base == NULL )
      return TT_Err_Invalid_CodeRange;

    /* NOTE:  Because the last instruction of a program may be a CALL */
    /*        which will return to the first byte *after* the code    */
    /*        range, we test for IP <= Size, instead of IP < Size.    */

    if ( IP > cr->Size )
      return TT_Err_Code_Overflow;

    exec->code     = cr->Base;
    exec->codeSize = cr->Size;
    exec->IP       = IP;
    exec->curRange = range;

    return TT_Err_Ok;
  }


#if 0

/*******************************************************************
 *
 *  Function    :  Get_CodeRange
 *
 *  Description :  Returns a pointer to a given code range.  Should
 *                 be used only by the debugger.  Returns NULL if
 *                 'range' is out of current bounds.
 *
 *  Input  :  exec    target execution context
 *            range   new execution code range
 *
 *  Output :  Pointer to the code range record.  NULL on failure.
 *
 *****************************************************************/

  LOCAL_FUNC
  PCodeRange  Get_CodeRange( PExecution_Context  exec, Int  range )
  {
    if ( range < 1 || range > 3 )
      return NULL;
    else                /* arrays start with 1 in Pascal, and with 0 in C */
      return &exec->codeRangeTable[range - 1];
  }

#endif


/*******************************************************************
 *
 *  Function    :  Set_CodeRange
 *
 *  Description :  Sets a code range.
 *
 *  Input  :  exec    target execution context
 *            range   code range index
 *            base    new code base
 *            length  sange size in bytes
 *
 *  Output :  SUCCESS on success.  FAILURE on error.
 *
 *****************************************************************/

  LOCAL_FUNC
  TT_Error  Set_CodeRange( PExecution_Context  exec,
                           Int                 range,
                           void*               base,
                           Long                length )
  {
    if ( range < 1 || range > 3 )
      return TT_Err_Bad_Argument;

    exec->codeRangeTable[range - 1].Base = (Byte*)base;
    exec->codeRangeTable[range - 1].Size = length;

    return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    :  Clear_CodeRange
 *
 *  Description :  Clears a code range.
 *
 *  Input  :  exec    target execution context
 *            range   code range index
 *
 *  Output :  SUCCESS on success.  FAILURE on error.
 *
 *  Note   : Does not set the Error variable.
 *
 *****************************************************************/

  LOCAL_FUNC
  TT_Error Clear_CodeRange( PExecution_Context  exec, Int  range )
  {
    if ( range < 1 || range > 3 )
      return TT_Err_Bad_Argument;

    exec->codeRangeTable[range - 1].Base = NULL;
    exec->codeRangeTable[range - 1].Size = 0;

    return TT_Err_Ok;
  }



/*******************************************************************
 *                                                                 *
 *                EXECUTION CONTEXT ROUTINES                       *
 *                                                                 *
 *******************************************************************/

/*******************************************************************
 *
 *  Function    :  Context_Destroy
 *
 *****************************************************************/

 LOCAL_FUNC
 TT_Error  Context_Destroy( void*  _context )
 {
   PExecution_Context  exec = (PExecution_Context)_context;

   if ( !exec )
     return TT_Err_Ok;

   /* free composite load stack */
   FREE( exec->loadStack );
   exec->loadSize = 0;

   /* points zone */
   Done_Glyph_Zone( &exec->pts );
   exec->maxPoints   = 0;
   exec->maxContours = 0;

   /* free stack */
   FREE( exec->stack );
   exec->stackSize = 0;

   /* free call stack */
   FREE( exec->callStack );
   exec->callSize = 0;
   exec->callTop  = 0;

   /* free glyph code range */
   FREE( exec->glyphIns );
   exec->glyphSize = 0;

   exec->instance = NULL;
   exec->face     = NULL;

   return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    :  Context_Create
 *
 *****************************************************************/

 LOCAL_FUNC
 TT_Error  Context_Create( void*  _context, void*  _face )
 {
   PExecution_Context  exec = (PExecution_Context)_context;

   PFace        face = (PFace)_face;
   TT_Error     error;


   /* XXX : We don't reserve arrays anymore, this is done automatically */
   /*       during a "Context_Load"..                                   */

   exec->callSize  = 32;
   if ( ALLOC_ARRAY( exec->callStack, exec->callSize, TCallRecord ) )
     goto Fail_Memory;

   /* all values in the context are set to 0 already, but this is */
   /* here as a remainder                                         */
   exec->maxPoints   = 0;
   exec->maxContours = 0;

   exec->stackSize = 0;
   exec->loadSize  = 0;
   exec->glyphSize = 0;

   exec->stack     = NULL;
   exec->loadStack = NULL;
   exec->glyphIns  = NULL;

   exec->face     = face;
   exec->instance = NULL;

   return TT_Err_Ok;

  Fail_Memory:
    Context_Destroy( exec );
    return error;
 }


/*******************************************************************
 *
 *  Function    :  Context_Load
 *
 *****************************************************************/

/****************************************************************/
/*                                                              */
/* Update_Max : Reallocate a buffer if it needs to              */
/*                                                              */
/* input:  size        address of buffer's current size         */
/*                     expressed in elements                    */
/*                                                              */
/*         multiplier  size in bytes of each element in the     */
/*                     buffer                                   */
/*                                                              */
/*         buff        address of the buffer base pointer       */
/*                                                              */
/*         new_max     new capacity (size) of the buffer        */

 static
 TT_Error  Update_Max( ULong*  size,
                       Long    multiplier,
                       void**  buff,
                       Long    new_max )
 {
   TT_Error  error;

   if ( *size < new_max )
   {
     FREE( *buff );
     if ( ALLOC( *buff, new_max * multiplier ) )
       return error;
     *size = new_max;
   }
   return TT_Err_Ok;
 }


/****************************************************************/
/*                                                              */
/* Update_Zone: Reallocate a zone if it needs to                */
/*                                                              */
/* input:  zone        address of the target zone               */
/*                                                              */
/*         maxPoints   address of the zone's current capacity   */
/*                     in points                                */
/*                                                              */
/*         maxContours address of the zone's current capacity   */
/*                     in contours                              */
/*                                                              */
/*         newPoints   new capacity in points                   */
/*                                                              */
/*         newContours new capacity in contours                 */
/*                                                              */

 static
 TT_Error  Update_Zone( PGlyph_Zone  zone,
                        UShort*      maxPoints,
                        UShort*      maxContours,
                        UShort       newPoints,
                        UShort       newContours )
 {
   if ( *maxPoints < newPoints || *maxContours < newContours )
   {
     TT_Error  error;


     Done_Glyph_Zone( zone );

     error = New_Glyph_Zone( zone, newPoints, newContours );
     if ( error )
       return error;

     *maxPoints   = newPoints;
     *maxContours = newContours;
   }
   return TT_Err_Ok;
 }


 LOCAL_FUNC
 TT_Error Context_Load( PExecution_Context  exec,
                        PFace               face,
                        PInstance           ins )
 {
   Int           i;
   TMaxProfile*  maxp;
   TT_Error      error;

   exec->face     = face;
   maxp           = &face->maxProfile;

   exec->instance = ins;

   if ( ins )
   {
     exec->numFDefs = ins->numFDefs;
     exec->numIDefs = ins->numIDefs;
     exec->FDefs    = ins->FDefs;
     exec->IDefs    = ins->IDefs;
     exec->metrics  = ins->metrics;

     for ( i = 0; i < MAX_CODE_RANGES; i++ )
       exec->codeRangeTable[i] = ins->codeRangeTable[i];

     /* set graphics state */
     exec->GS = ins->GS;

     exec->cvtSize = ins->cvtSize;
     exec->cvt     = ins->cvt;

     exec->storeSize = ins->storeSize;
     exec->storage   = ins->storage;

     exec->twilight  = ins->twilight;
   }

   error = Update_Max( &exec->loadSize,
                       sizeof ( TSubglyph_Record ),
                       (void**)&exec->loadStack,
                       face->maxComponents + 1 );
   if ( error )
     return error;

   error = Update_Max( &exec->stackSize,
                       sizeof ( TT_F26Dot6 ),
                       (void**)&exec->stack,
                       maxp->maxStackElements + 32 );
   /* XXX : We reserve a little more elements on the stack to deal safely */
   /*       with broken fonts like arialbs, courbs, timesbs...            */
   if ( error )
     return error;

   error = Update_Max( &exec->glyphSize,
                       sizeof ( Byte ),
                       (void**)&exec->glyphIns,
                       maxp->maxSizeOfInstructions );
   if ( error )
     return error;

   error = Update_Zone( &exec->pts,
                        &exec->maxPoints,
                        &exec->maxContours,
                        exec->face->maxPoints + 2,
                        exec->face->maxContours );
   /* XXX : We reserve two positions for the phantom points! */
   if ( error )
     return error;

   exec->pts.n_points   = 0;
   exec->pts.n_contours = 0;

   exec->instruction_trap = FALSE;

   return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    :  Context_Save
 *
 *****************************************************************/

 LOCAL_FUNC
 TT_Error  Context_Save( PExecution_Context  exec,
                         PInstance           ins )
 {
   Int  i;


   /* XXXX : Will probably disappear soon with all the coderange */
   /*        management, which is now rather obsolete.           */

   for ( i = 0; i < MAX_CODE_RANGES; i++ )
     ins->codeRangeTable[i] = exec->codeRangeTable[i];

   return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    :  Context_Run
 *
 *****************************************************************/

 LOCAL_FUNC
 TT_Error  Context_Run( PExecution_Context  exec,
                        Bool                debug )
 {
   TT_Error  error;


   if ( (error = Goto_CodeRange( exec, TT_CodeRange_Glyph, 0 )) != TT_Err_Ok )
     return error;

   exec->zp0 = exec->pts;
   exec->zp1 = exec->pts;
   exec->zp2 = exec->pts;

   exec->GS.gep0 = 1;
   exec->GS.gep1 = 1;
   exec->GS.gep2 = 1;

   exec->GS.projVector.x = 0x4000;
   exec->GS.projVector.y = 0x0000;

   exec->GS.freeVector = exec->GS.projVector;
   exec->GS.dualVector = exec->GS.projVector;

   exec->GS.round_state = 1;
   exec->GS.loop        = 1;

   /* some glyphs leave something on the stack. so we clean it */
   /* before a new execution.                                  */
   exec->top     = 0;
   exec->callTop = 0;

   if ( !debug )
     return RunIns( exec );
   else
     return TT_Err_Ok;
 }


  LOCAL_FUNC
  const TGraphicsState  Default_GraphicsState =
  {
    0, 0, 0,
    { 0x4000, 0 },
    { 0x4000, 0 },
    { 0x4000, 0 },
    1, 64, 1,
    TRUE, 68, 0, 0, 9, 3,
    0, FALSE, 2, 1, 1, 1
  };



/*******************************************************************
 *                                                                 *
 *                     INSTANCE  FUNCTIONS                         *
 *                                                                 *
 *                                                                 *
 *******************************************************************/

/*******************************************************************
 *
 *  Function    : Instance_Destroy
 *
 *  Description :
 *
 *  Input  :  _instance   the instance object to destroy
 *
 *  Output :  error code.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Instance_Destroy( void* _instance )
  {
    PInstance  ins = (PInstance)_instance;


    if ( !_instance )
      return TT_Err_Ok;

    if ( ins->debug )
    {
      /* the debug context must be deleted by the debugger itself */
      ins->context = NULL;
      ins->debug   = FALSE;
    }

    FREE( ins->cvt );
    ins->cvtSize = 0;

    /* free storage area */
    FREE( ins->storage );
    ins->storeSize = 0;

    /* twilight zone */
    Done_Glyph_Zone( &ins->twilight );

    FREE( ins->FDefs );
    FREE( ins->IDefs );
    ins->numFDefs = 0;
    ins->numIDefs = 0;

    ins->owner = NULL;
    ins->valid = FALSE;

    return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    : Instance_Create
 *
 *  Description :
 *
 *  Input  :  _instance    instance record to initialize
 *            _face        parent face object
 *
 *  Output :  Error code.  All partially built subtables are
 *            released on error.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Instance_Create( void*  _instance,
                             void*  _face )
  {
    PInstance  ins  = (PInstance)_instance;
    PFace      face = (PFace)_face;
    TT_Error   error;
    Int        i;
    UShort     n_twilight;

    PMaxProfile  maxp = &face->maxProfile;


    ins->owner = face;
    ins->valid = FALSE;

    ins->numFDefs  = maxp->maxFunctionDefs;
    ins->numIDefs  = maxp->maxInstructionDefs;
    ins->cvtSize   = face->cvtSize;
    ins->storeSize = maxp->maxStorage;

    /* Set default metrics */
    {
      PIns_Metrics   metrics = &ins->metrics;


      metrics->pointSize    = 10 * 64;     /* default pointsize  = 10pts */

      metrics->x_resolution = 96;          /* default resolution = 96dpi */
      metrics->y_resolution = 96;

      metrics->x_ppem = 0;
      metrics->y_ppem = 0;

      metrics->rotated   = FALSE;
      metrics->stretched = FALSE;

      /* set default compensation ( all 0 ) */
      for ( i = 0; i < 4; i++ )
        metrics->compensations[i] = 0;
    }

    /* allocate function defs, instruction defs, cvt and storage area */
    if ( ALLOC_ARRAY( ins->FDefs,   ins->numFDefs,  TDefRecord )  ||
         ALLOC_ARRAY( ins->IDefs,   ins->numIDefs,  TDefRecord )  ||
         ALLOC_ARRAY( ins->cvt,     ins->cvtSize,   Long       )  ||
         ALLOC_ARRAY( ins->storage, ins->storeSize, Long       )  )
      goto Fail_Memory;

    /* reserve twilight zone */
    n_twilight = maxp->maxTwilightPoints;
    error = New_Glyph_Zone( &ins->twilight, n_twilight, 0 );
    if (error)
      goto Fail_Memory;

    ins->twilight.n_points = n_twilight;

    return TT_Err_Ok;

  Fail_Memory:
    Instance_Destroy( ins );
    return error;
  }


/*******************************************************************
 *
 *  Function    : Instance_Init
 *
 *  Description : Initialize a fresh new instance.
 *                Executes the font program if any is found.
 *
 *  Input  :  _instance   the instance object to destroy
 *
 *  Output :  Error code.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Instance_Init( PInstance  ins )
  {
    PExecution_Context  exec;

    TT_Error  error;
    PFace     face = ins->owner;


    if ( ins->debug )
      exec = ins->context;
    else
      exec = New_Context( face );
    /* debugging instances have their own context */

    if ( !exec )
      return TT_Err_Could_Not_Find_Context;

    ins->GS = Default_GraphicsState;

    Context_Load( exec, face, ins );

    exec->callTop   = 0;
    exec->top       = 0;

    exec->period    = 64;
    exec->phase     = 0;
    exec->threshold = 0;

    {
      PIns_Metrics  metrics = &exec->metrics;


      metrics->x_ppem    = 0;
      metrics->y_ppem    = 0;
      metrics->pointSize = 0;
      metrics->x_scale1  = 0;
      metrics->x_scale2  = 1;
      metrics->y_scale1  = 0;
      metrics->y_scale2  = 1;

      metrics->ppem      = 0;
      metrics->scale1    = 0;
      metrics->scale2    = 1;
      metrics->ratio     = 1 << 16;
    }

    exec->instruction_trap = FALSE;

    exec->cvtSize = ins->cvtSize;
    exec->cvt     = ins->cvt;

    exec->F_dot_P = 0x10000;

    /* allow font program execution */
    Set_CodeRange( exec,
                   TT_CodeRange_Font,
                   face->fontProgram,
                   face->fontPgmSize );

    /* disable CVT and glyph programs coderange */
    Clear_CodeRange( exec, TT_CodeRange_Cvt );
    Clear_CodeRange( exec, TT_CodeRange_Glyph );

    if ( face->fontPgmSize > 0 )
    {
      error = Goto_CodeRange( exec, TT_CodeRange_Font, 0 );
      if ( error )
        goto Fin;

      error = RunIns( exec );
    }
    else
      error = TT_Err_Ok;

  Fin:
    Context_Save( exec, ins );

    if ( !ins->debug )
      Done_Context( exec );
    /* debugging instances keep their context */

    ins->valid = FALSE;

    return error;
  }


/*******************************************************************
 *
 *  Function    : Instance_Reset
 *
 *  Description : Resets an instance to a new pointsize/transform.
 *                Executes the cvt program if any is found.
 *
 *  Input  :  _instance   the instance object to destroy
 *
 *  Output :  Error code.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Instance_Reset( PInstance  ins )
  {
    PExecution_Context  exec;

    TT_Error  error;
    Long      i;
    UShort    j;
    PFace     face;


    if ( !ins )
      return TT_Err_Invalid_Instance_Handle;

    if ( ins->valid )
      return TT_Err_Ok;

    face = ins->owner;

    if ( ins->metrics.x_ppem < 1 ||
         ins->metrics.y_ppem < 1 )
      return TT_Err_Invalid_PPem;

    /* compute new transformation */
    if ( ins->metrics.x_ppem >= ins->metrics.y_ppem )
    {
      ins->metrics.scale1  = ins->metrics.x_scale1;
      ins->metrics.scale2  = ins->metrics.x_scale2;
      ins->metrics.ppem    = ins->metrics.x_ppem;
      ins->metrics.x_ratio = 1 << 16;
      ins->metrics.y_ratio = TT_MulDiv( ins->metrics.y_ppem,
                                        0x10000,
                                        ins->metrics.x_ppem );
    }
    else
    {
      ins->metrics.scale1  = ins->metrics.y_scale1;
      ins->metrics.scale2  = ins->metrics.y_scale2;
      ins->metrics.ppem    = ins->metrics.y_ppem;
      ins->metrics.x_ratio = TT_MulDiv( ins->metrics.x_ppem,
                                        0x10000,
                                        ins->metrics.y_ppem );
      ins->metrics.y_ratio = 1 << 16;
    }

    /* Scale the cvt values to the new ppem.          */
    /* We use by default the y ppem to scale the CVT. */

    for ( i = 0; i < ins->cvtSize; i++ )
      ins->cvt[i] = TT_MulDiv( face->cvt[i],
                               ins->metrics.scale1,
                               ins->metrics.scale2 );

    /* All twilight points are originally zero */
    for ( j = 0; j < ins->twilight.n_points; j++ )
    {
      ins->twilight.org[j].x = 0;
      ins->twilight.org[j].y = 0;
      ins->twilight.cur[j].x = 0;
      ins->twilight.cur[j].y = 0;
    }

    /* clear storage area */
    for ( i = 0; i < ins->storeSize; i++ )
      ins->storage[i] = 0;

    ins->GS = Default_GraphicsState;

    /* get execution context and run prep program */

    if ( ins->debug )
      exec = ins->context;
    else
      exec = New_Context(face);
    /* debugging instances have their own context */

    if ( !exec )
      return TT_Err_Could_Not_Find_Context;

    Context_Load( exec, face, ins );

    Set_CodeRange( exec,
                   TT_CodeRange_Cvt,
                   face->cvtProgram,
                   face->cvtPgmSize );

    Clear_CodeRange( exec, TT_CodeRange_Glyph );

    exec->instruction_trap = FALSE;

    exec->top     = 0;
    exec->callTop = 0;

    if ( face->cvtPgmSize > 0 )
    {
      error = Goto_CodeRange( exec, TT_CodeRange_Cvt, 0 );
      if ( error )
        goto Fin;

      if ( !ins->debug )
        error = RunIns( exec );
    }
    else
      error = TT_Err_Ok;

    ins->GS = exec->GS;
    /* save default graphics state */

  Fin:
    Context_Save( exec, ins );

    if ( !ins->debug )
      Done_Context( exec );
    /* debugging instances keep their context */

    if ( !error )
      ins->valid = TRUE;

    return error;
  }



/*******************************************************************
 *                                                                 *
 *                         FACE  FUNCTIONS                         *
 *                                                                 *
 *                                                                 *
 *******************************************************************/

/*******************************************************************
 *
 *  Function    :  Face_Destroy
 *
 *  Description :  The face object destructor.
 *
 *  Input  :  _face   typeless pointer to the face object to destroy
 *
 *  Output :  Error code.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Face_Destroy( void*  _face )
  {
    PFace   face = (PFace)_face;
    UShort  n;


    if ( !face )
      return TT_Err_Ok;

    /* well, we assume that no other thread is using the face */
    /* at this moment, but one is never sure enough.          */
    MUTEX_Lock( face->lock );

    /* first of all, destroys the cached sub-objects */
    Cache_Destroy( &face->instances );
    Cache_Destroy( &face->glyphs );

    /* destroy the extensions */
#ifdef TT_CONFIG_OPTION_EXTEND_ENGINE
    Extension_Destroy( face );
#endif

    /* freeing the collection table */
    FREE( face->ttcHeader.TableDirectory );
    face->ttcHeader.DirCount = 0;

    /* freeing table directory */
    FREE( face->dirTables );
    face->numTables = 0;

    /* freeing the locations table */
    FREE( face->glyphLocations );
    face->numLocations = 0;

    /* freeing the character mapping tables */
    for ( n = 0; n < face->numCMaps; n++ )
      CharMap_Free( face->cMaps + n );

    FREE( face->cMaps );
    face->numCMaps = 0;

    /* freeing the CVT */
    FREE( face->cvt );
    face->cvtSize = 0;

    /* freeing the horizontal metrics */
    FREE( face->horizontalHeader.long_metrics );
    FREE( face->horizontalHeader.short_metrics );

    /* freeing the vertical ones, if any */
    if (face->verticalInfo)
    {
      FREE( face->verticalHeader.long_metrics  );
      FREE( face->verticalHeader.short_metrics );
      face->verticalInfo = 0;
    }

    /* freeing the programs */
    FREE( face->fontProgram );
    FREE( face->cvtProgram );
    face->fontPgmSize = 0;
    face->cvtPgmSize  = 0;

    /* freeing the gasp table */
    FREE( face->gasp.gaspRanges );
    face->gasp.numRanges = 0;

    /* freeing the name table */
    Free_TrueType_Names( face );

    /* freeing the hdmx table */
    Free_TrueType_Hdmx( face );

    /* TT_Close_Stream( &face->stream ); -- this is performed by the API */

    /* destroy the mutex */
    MUTEX_Destroy(face->lock);

    return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    :  Face_Create
 *
 *  Description :  The face object constructor.
 *
 *  Input  :  _face    face record to build
 *            _input   input stream where to load font data
 *
 *  Output :  Error code.
 *
 *  NOTE : The input stream is kept in the face object.  The
 *         caller shouldn't destroy it after calling Face_Create().
 *
 ******************************************************************/

#undef  LOAD_
#define LOAD_( table ) \
          (error = Load_TrueType_##table (face)) != TT_Err_Ok


  LOCAL_FUNC
  TT_Error  Face_Create( void*  _face,
                         void*  _input )
  {
    PEngine_Instance  engine;

    TFont_Input*  input = (TFont_Input*)_input;
    PFace         face  = (PFace)_face;
    TT_Error      error;


    face->stream = input->stream;
    face->engine = input->engine;

    engine = face->engine;

    MUTEX_Create( face->lock );

    Cache_Create( engine,
                  engine->objs_instance_class,
                  &face->instances,
                  &face->lock );

    Cache_Create( engine,
                  engine->objs_glyph_class,
                  &face->glyphs,
                  &face->lock );

    /* Load collection directory if present, then font directory */

    error = Load_TrueType_Directory( face, input->fontIndex );
    if ( error )
      goto Fail;

    /* Load tables */

    if ( LOAD_( Header )        ||
         LOAD_( MaxProfile )    ||
         LOAD_( Locations )     ||

         (error = Load_TrueType_Metrics_Header( face, 0 )) != TT_Err_Ok  ||
         /* load the 'hhea' & 'hmtx' tables at once */

         LOAD_( CMap )          ||
         LOAD_( CVT )           ||
         LOAD_( Programs )      ||
         LOAD_( Gasp )          ||
         LOAD_( Names )         ||
         LOAD_( OS2 )           ||
         LOAD_( PostScript )    ||

         (error = Load_TrueType_Metrics_Header( face, 1 )) != TT_Err_Ok ||
         /* try to load the 'vhea' & 'vmtx' at once if present */

         LOAD_( Hdmx )          )

      goto Fail;

#ifdef TT_CONFIG_OPTION_EXTEND_ENGINE
    if ( ( error = Extension_Create( face ) ) != TT_Err_Ok )
      return error;
#endif

    return TT_Err_Ok;

  Fail :
    Face_Destroy( face );
    return error;
  }

#undef LOAD_


/*******************************************************************
 *
 *  Function    :  Glyph_Destroy
 *
 *  Description :  The glyph object destructor.
 *
 *  Input  :  _glyph  typeless pointer to the glyph record to destroy
 *
 *  Output :  Error code.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Glyph_Destroy( void*  _glyph )
  {
    PGlyph  glyph = (PGlyph)_glyph;


    if ( !glyph )
      return TT_Err_Ok;

    glyph->outline.owner = TRUE;
    return TT_Done_Outline( &glyph->outline );
  }


/*******************************************************************
 *
 *  Function    :  Glyph_Create
 *
 *  Description :  The glyph object constructor.
 *
 *  Input  :  _glyph   glyph record to build.
 *            _face    the glyph's parent face.
 *
 *  Output :  Error code.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  Glyph_Create( void*  _glyph,
                          void*  _face )
  {
    PFace     face  = (PFace)_face;
    PGlyph    glyph = (PGlyph)_glyph;


    if ( !face )
      return TT_Err_Invalid_Face_Handle;

    if ( !glyph )
      return TT_Err_Invalid_Glyph_Handle;

    glyph->face = face;

    /* XXX: Don't forget the space for the 2 phantom points */
    return TT_New_Outline( glyph->face->maxPoints + 2,
                           glyph->face->maxContours,
                           &glyph->outline );
  }


/*******************************************************************
 *
 *  Function    :  Scale_X
 *
 *  Description :  scale an horizontal distance from font
 *                 units to 26.6 pixels
 *
 *  Input  :  metrics  pointer to metrics
 *            x        value to scale
 *
 *  Output :  scaled value
 *
 ******************************************************************/

 LOCAL_FUNC
 TT_Pos  Scale_X( PIns_Metrics  metrics, TT_Pos  x )
 {
   return TT_MulDiv( x, metrics->x_scale1, metrics->x_scale2 );
 }


/*******************************************************************
 *
 *  Function    :  Scale_Y
 *
 *  Description :  scale a vertical distance from font
 *                 units to 26.6 pixels
 *
 *  Input  :  metrics  pointer to metrics
 *            y        value to scale
 *
 *  Output :  scaled value
 *
 ******************************************************************/

 LOCAL_FUNC
 TT_Pos  Scale_Y( PIns_Metrics  metrics, TT_Pos  y )
 {
   return TT_MulDiv( y, metrics->y_scale1, metrics->y_scale2 );
 }


/*******************************************************************
 *
 *  Function    :  TTObjs_Init
 *
 *  Description :  The TTObjs component initializer.  Creates the
 *                 object cache classes, as well as the face record
 *                 cache.
 *
 *  Input  :  engine    engine instance
 *
 *  Output :  Error code.
 *
 ******************************************************************/

  static
  const TCache_Class   objs_face_class =
        {
          sizeof ( TFace ),
          -1,
          Face_Create,
          Face_Destroy,
          NULL,
          NULL
        };

  static
  const TCache_Class  objs_instance_class =
        {
          sizeof ( TInstance ),
          -1,
          Instance_Create,
          Instance_Destroy,
          NULL,
          NULL
        };

  static
  const TCache_Class  objs_exec_class =
        {
          sizeof ( TExecution_Context ),
          1,
          Context_Create,
          Context_Destroy,
          NULL,
          NULL
        };

  static
  const TCache_Class  objs_glyph_class =
        {
          sizeof ( TGlyph ),
          -1,
          Glyph_Create,
          Glyph_Destroy,
          NULL,
          NULL
        };


  LOCAL_FUNC
  TT_Error  TTObjs_Init( PEngine_Instance  engine )
  {
    PCache        face_cache, exec_cache;
    TT_Error      error;


    face_cache = 0;
    exec_cache = 0;

    if ( ALLOC( face_cache, sizeof ( TCache ) ) ||
         ALLOC( exec_cache, sizeof ( TCache ) ) )
      goto Fail;

      /* create face cache */
    error = Cache_Create( engine, (PCache_Class)&objs_face_class,
                          face_cache, &engine->lock );
    if ( error )
      goto Fail;

    engine->objs_face_cache = face_cache;

    error = Cache_Create( engine, (PCache_Class)&objs_exec_class,
                          exec_cache, &engine->lock );
    if ( error )
      goto Fail;

    engine->objs_exec_cache = exec_cache;

    engine->objs_face_class      = (PCache_Class)&objs_face_class;
    engine->objs_instance_class  = (PCache_Class)&objs_instance_class;
    engine->objs_execution_class = (PCache_Class)&objs_exec_class;
    engine->objs_glyph_class     = (PCache_Class)&objs_glyph_class;

    goto Exit;

  Fail:
    FREE( face_cache );
    FREE( exec_cache );

  Exit:
    return error;
  }


/*******************************************************************
 *
 *  Function    :  TTObjs_Done
 *
 *  Description :  The TTObjs component finalizer.
 *
 *  Input  :  engine    engine instance
 *
 *  Output :  Error code.
 *
 ******************************************************************/

  LOCAL_FUNC
  TT_Error  TTObjs_Done( PEngine_Instance  engine )
  {
    /* destroy all active faces and contexts before releasing the */
    /* caches                                                     */
    Cache_Destroy( (TCache*)engine->objs_exec_cache );
    Cache_Destroy( (TCache*)engine->objs_face_cache );

    /* Now frees caches and cache classes */
    FREE( engine->objs_exec_cache );
    FREE( engine->objs_face_cache );

    return TT_Err_Ok;
  }


/* END */
