blob: 9748e9a4ed4b3ed294f9c5875ee19c5779eb8831 [file] [log] [blame]
/***************************************************************************/
/* */
/* ttinterp.c */
/* */
/* TrueType bytecode interpreter (body). */
/* */
/* Copyright 1996-2001 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_CALC_H
#include FT_TRIGONOMETRY_H
#include FT_SYSTEM_H
#include "ttinterp.h"
#include "tterrors.h"
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
#define TT_MULFIX FT_MulFix
#define TT_MULDIV FT_MulDiv
#define TT_INT64 FT_Int64
/*************************************************************************/
/* */
/* 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_ttinterp
#undef NO_APPLE_PATENT
#define APPLE_THRESHOLD 0x4000000L
/*************************************************************************/
/* */
/* In order to detect infinite loops in the code, we set up a counter */
/* within the run loop. A single stroke of interpretation is now */
/* limitet to a maximal number of opcodes defined below. */
/* */
#define MAX_RUNNABLE_OPCODES 1000000L
/*************************************************************************/
/* */
/* There are two kinds of implementations: */
/* */
/* a. static implementation */
/* */
/* The current execution context is a static variable, which fields */
/* are accessed directly by the interpreter during execution. The */
/* context is named `cur'. */
/* */
/* This version is non-reentrant, of course. */
/* */
/* b. indirect implementation */
/* */
/* The current execution context is passed to _each_ function as its */
/* first argument, and each field is thus accessed indirectly. */
/* */
/* This version is fully re-entrant. */
/* */
/* The idea is that an indirect implementation may be slower to execute */
/* on low-end processors that are used in some systems (like 386s or */
/* even 486s). */
/* */
/* As a consequence, the indirect implementation is now the default, as */
/* its performance costs can be considered negligible in our context. */
/* Note, however, that we kept the same source with macros because: */
/* */
/* - The code is kept very close in design to the Pascal code used for */
/* development. */
/* */
/* - It's much more readable that way! */
/* */
/* - It's still open to experimentation and tuning. */
/* */
/*************************************************************************/
#ifndef TT_CONFIG_OPTION_STATIC_INTERPRETER /* indirect implementation */
#define CUR (*exc) /* see ttobjs.h */
#else /* static implementation */
#define CUR cur
static
TT_ExecContextRec cur; /* static exec. context variable */
/* apparently, we have a _lot_ of direct indexing when accessing */
/* the static `cur', which makes the code bigger (due to all the */
/* four bytes addresses). */
#endif /* TT_CONFIG_OPTION_STATIC_INTERPRETER */
/*************************************************************************/
/* */
/* The instruction argument stack. */
/* */
#define INS_ARG EXEC_OP_ FT_Long* args /* see ttobjs.h for EXEC_OP_ */
/*************************************************************************/
/* */
/* This macro is used whenever `exec' is unused in a function, to avoid */
/* stupid warnings from pedantic compilers. */
/* */
#define FT_UNUSED_EXEC FT_UNUSED( CUR )
/*************************************************************************/
/* */
/* This macro is used whenever `args' is unused in a function, to avoid */
/* stupid warnings from pedantic compilers. */
/* */
#define FT_UNUSED_ARG FT_UNUSED_EXEC; FT_UNUSED( args )
/*************************************************************************/
/* */
/* The following macros hide the use of EXEC_ARG and EXEC_ARG_ to */
/* increase readabilty of the code. */
/* */
/*************************************************************************/
#define SKIP_Code() \
SkipCode( EXEC_ARG )
#define GET_ShortIns() \
GetShortIns( EXEC_ARG )
#define NORMalize( x, y, v ) \
Normalize( EXEC_ARG_ x, y, v )
#define SET_SuperRound( scale, flags ) \
SetSuperRound( EXEC_ARG_ scale, flags )
#define ROUND_None( d, c ) \
Round_None( EXEC_ARG_ d, c )
#define INS_Goto_CodeRange( range, ip ) \
Ins_Goto_CodeRange( EXEC_ARG_ range, ip )
#define CUR_Func_project( x, y ) \
CUR.func_project( EXEC_ARG_ x, y )
#define CUR_Func_move( z, p, d ) \
CUR.func_move( EXEC_ARG_ z, p, d )
#define CUR_Func_dualproj( x, y ) \
CUR.func_dualproj( EXEC_ARG_ x, y )
#define CUR_Func_freeProj( x, y ) \
CUR.func_freeProj( EXEC_ARG_ x, y )
#define CUR_Func_round( d, c ) \
CUR.func_round( EXEC_ARG_ d, c )
#define CUR_Func_read_cvt( index ) \
CUR.func_read_cvt( EXEC_ARG_ index )
#define CUR_Func_write_cvt( index, val ) \
CUR.func_write_cvt( EXEC_ARG_ index, val )
#define CUR_Func_move_cvt( index, val ) \
CUR.func_move_cvt( EXEC_ARG_ index, val )
#define CURRENT_Ratio() \
Current_Ratio( EXEC_ARG )
#define CURRENT_Ppem() \
Current_Ppem( EXEC_ARG )
#define CUR_Ppem() \
Cur_PPEM( EXEC_ARG )
#define INS_SxVTL( a, b, c, d ) \
Ins_SxVTL( EXEC_ARG_ a, b, c, d )
#define COMPUTE_Funcs() \
Compute_Funcs( EXEC_ARG )
#define COMPUTE_Round( a ) \
Compute_Round( EXEC_ARG_ a )
#define COMPUTE_Point_Displacement( a, b, c, d ) \
Compute_Point_Displacement( EXEC_ARG_ a, b, c, d )
#define MOVE_Zp2_Point( a, b, c, t ) \
Move_Zp2_Point( EXEC_ARG_ a, b, c, t )
/*************************************************************************/
/* */
/* Instruction dispatch function, as used by the interpreter. */
/* */
typedef void (*TInstruction_Function)( INS_ARG );
/*************************************************************************/
/* */
/* A simple bounds-checking macro. */
/* */
#define BOUNDS( x, n ) ( (FT_UInt)(x) >= (FT_UInt)(n) )
#undef SUCCESS
#define SUCCESS 0
#undef FAILURE
#define FAILURE 1
/*************************************************************************/
/* */
/* CODERANGE FUNCTIONS */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* TT_Goto_CodeRange */
/* */
/* <Description> */
/* Switches to a new code range (updates the code related elements in */
/* `exec', and `IP'). */
/* */
/* <Input> */
/* range :: The new execution code range. */
/* */
/* IP :: The new IP in the new code range. */
/* */
/* <InOut> */
/* exec :: The target execution context. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Goto_CodeRange( TT_ExecContext exec,
FT_Int range,
FT_Long IP )
{
TT_CodeRange* coderange;
FT_Assert( range >= 1 && range <= 3 );
coderange = &exec->codeRangeTable[range - 1];
FT_Assert( coderange->base != NULL );
/* 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. */
/* */
FT_Assert( (FT_ULong)IP <= coderange->size );
exec->code = coderange->base;
exec->codeSize = coderange->size;
exec->IP = IP;
exec->curRange = range;
return TT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Set_CodeRange */
/* */
/* <Description> */
/* Sets a code range. */
/* */
/* <Input> */
/* range :: The code range index. */
/* */
/* base :: The new code base. */
/* */
/* length :: The range size in bytes. */
/* */
/* <InOut> */
/* exec :: The target execution context. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF FT_Error
TT_Set_CodeRange( TT_ExecContext exec,
FT_Int range,
void* base,
FT_Long length )
{
FT_Assert( range >= 1 && range <= 3 );
exec->codeRangeTable[range - 1].base = (FT_Byte*)base;
exec->codeRangeTable[range - 1].size = length;
return TT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Clear_CodeRange */
/* */
/* <Description> */
/* Clears a code range. */
/* */
/* <Input> */
/* range :: The code range index. */
/* */
/* <InOut> */
/* exec :: The target execution context. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* Does not set the Error variable. */
/* */
FT_LOCAL_DEF FT_Error
TT_Clear_CodeRange( TT_ExecContext exec,
FT_Int range )
{
FT_Assert( range >= 1 && range <= 3 );
exec->codeRangeTable[range - 1].base = NULL;
exec->codeRangeTable[range - 1].size = 0;
return TT_Err_Ok;
}
/*************************************************************************/
/* */
/* EXECUTION CONTEXT ROUTINES */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* TT_Destroy_Context */
/* */
/* <Description> */
/* Destroys a given context. */
/* */
/* <Input> */
/* exec :: A handle to the target execution context. */
/* */
/* memory :: A handle to the parent memory object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* Only the glyph loader and debugger should call this function. */
/* */
FT_LOCAL_DEF FT_Error
TT_Destroy_Context( TT_ExecContext exec,
FT_Memory memory )
{
/* free composite load stack */
FREE( exec->loadStack );
exec->loadSize = 0;
/* points zone */
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->size = NULL;
exec->face = NULL;
FREE( exec );
return TT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* Init_Context */
/* */
/* <Description> */
/* Initializes a context object. */
/* */
/* <Input> */
/* memory :: A handle to the parent memory object. */
/* */
/* face :: A handle to the source TrueType face object. */
/* */
/* <InOut> */
/* exec :: A handle to the target execution context. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
static FT_Error
Init_Context( TT_ExecContext exec,
TT_Face face,
FT_Memory memory )
{
FT_Error error;
FT_TRACE1(( "Init_Context: new object at 0x%08p, parent = 0x%08p\n",
exec, face ));
exec->memory = memory;
exec->callSize = 32;
if ( ALLOC_ARRAY( exec->callStack, exec->callSize, TT_CallRec ) )
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->size = NULL;
return TT_Err_Ok;
Fail_Memory:
FT_ERROR(( "Init_Context: not enough memory for 0x%08lx\n",
(FT_Long)exec ));
TT_Destroy_Context( exec, memory );
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* Update_Max */
/* */
/* <Description> */
/* Checks the size of a buffer and reallocates it if necessary. */
/* */
/* <Input> */
/* memory :: A handle to the parent memory object. */
/* */
/* multiplier :: The size in bytes of each element in the buffer. */
/* */
/* new_max :: The new capacity (size) of the buffer. */
/* */
/* <InOut> */
/* size :: The address of the buffer's current size expressed */
/* in elements. */
/* */
/* buff :: The address of the buffer base pointer. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
static FT_Error
Update_Max( FT_Memory memory,
FT_ULong* size,
FT_Long multiplier,
void** buff,
FT_ULong new_max )
{
FT_Error error;
if ( *size < new_max )
{
FREE( *buff );
if ( ALLOC( *buff, new_max * multiplier ) )
return error;
*size = new_max;
}
return TT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Load_Context */
/* */
/* <Description> */
/* Prepare an execution context for glyph hinting. */
/* */
/* <Input> */
/* face :: A handle to the source face object. */
/* */
/* size :: A handle to the source size object. */
/* */
/* <InOut> */
/* exec :: A handle to the target execution context. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* Only the glyph loader and debugger should call this function. */
/* */
FT_LOCAL_DEF FT_Error
TT_Load_Context( TT_ExecContext exec,
TT_Face face,
TT_Size size )
{
FT_Int i;
FT_ULong tmp;
TT_MaxProfile* maxp;
FT_Error error;
exec->face = face;
maxp = &face->max_profile;
exec->size = size;
if ( size )
{
exec->numFDefs = size->num_function_defs;
exec->maxFDefs = size->max_function_defs;
exec->numIDefs = size->num_instruction_defs;
exec->maxIDefs = size->max_instruction_defs;
exec->FDefs = size->function_defs;
exec->IDefs = size->instruction_defs;
exec->tt_metrics = size->ttmetrics;
exec->metrics = size->root.metrics;
exec->maxFunc = size->max_func;
exec->maxIns = size->max_ins;
for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
exec->codeRangeTable[i] = size->codeRangeTable[i];
/* set graphics state */
exec->GS = size->GS;
exec->cvtSize = size->cvt_size;
exec->cvt = size->cvt;
exec->storeSize = size->storage_size;
exec->storage = size->storage;
exec->twilight = size->twilight;
}
error = Update_Max( exec->memory,
&exec->loadSize,
sizeof ( TT_SubGlyphRec ),
(void**)&exec->loadStack,
exec->face->max_components + 1 );
if ( error )
return error;
/* XXX: We reserve a little more elements on the stack to deal safely */
/* with broken fonts like arialbs, courbs, timesbs, etc. */
tmp = exec->stackSize;
error = Update_Max( exec->memory,
&tmp,
sizeof ( FT_F26Dot6 ),
(void**)&exec->stack,
maxp->maxStackElements + 32 );
exec->stackSize = (FT_UInt)tmp;
if ( error )
return error;
tmp = exec->glyphSize;
error = Update_Max( exec->memory,
&tmp,
sizeof ( FT_Byte ),
(void**)&exec->glyphIns,
maxp->maxSizeOfInstructions );
exec->glyphSize = (FT_UShort)tmp;
if ( error )
return error;
exec->pts.n_points = 0;
exec->pts.n_contours = 0;
exec->instruction_trap = FALSE;
return TT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Save_Context */
/* */
/* <Description> */
/* Saves the code ranges in a `size' object. */
/* */
/* <Input> */
/* exec :: A handle to the source execution context. */
/* */
/* <InOut> */
/* size :: A handle to the target size object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* Only the glyph loader and debugger should call this function. */
/* */
FT_LOCAL_DEF FT_Error
TT_Save_Context( TT_ExecContext exec,
TT_Size size )
{
FT_Int i;
/* XXXX: Will probably disappear soon with all the code range */
/* management, which is now rather obsolete. */
/* */
size->num_function_defs = exec->numFDefs;
size->num_instruction_defs = exec->numIDefs;
size->max_func = exec->maxFunc;
size->max_ins = exec->maxIns;
for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
size->codeRangeTable[i] = exec->codeRangeTable[i];
return TT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Run_Context */
/* */
/* <Description> */
/* Executes one or more instructions in the execution context. */
/* */
/* <Input> */
/* debug :: A Boolean flag. If set, the function sets some internal */
/* variables and returns immediately, otherwise TT_RunIns() */
/* is called. */
/* */
/* This is commented out currently. */
/* */
/* <Input> */
/* exec :: A handle to the target execution context. */
/* */
/* <Return> */
/* TrueTyoe error code. 0 means success. */
/* */
/* <Note> */
/* Only the glyph loader and debugger should call this function. */
/* */
FT_LOCAL_DEF FT_Error
TT_Run_Context( TT_ExecContext exec,
FT_Bool debug )
{
FT_Error error;
if ( ( error = TT_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 1
FT_UNUSED( debug );
return exec->face->interpreter( exec );
#else
if ( !debug )
return TT_RunIns( exec );
else
return TT_Err_Ok;
#endif
}
const TT_GraphicsState tt_default_graphics_state =
{
0, 0, 0,
{ 0x4000, 0 },
{ 0x4000, 0 },
{ 0x4000, 0 },
1, 64, 1,
TRUE, 68, 0, 0, 9, 3,
0, FALSE, 2, 1, 1, 1
};
/* documentation is in ttinterp.h */
FT_EXPORT_DEF( TT_ExecContext )
TT_New_Context( TT_Face face )
{
TT_Driver driver;
TT_ExecContext exec;
FT_Memory memory;
if ( !face )
return 0;
driver = (TT_Driver)face->root.driver;
memory = driver->root.root.memory;
exec = driver->context;
if ( !driver->context )
{
FT_Error error;
/* allocate object */
if ( ALLOC( exec, sizeof ( *exec ) ) )
goto Exit;
/* initialize it */
error = Init_Context( exec, face, memory );
if ( error )
goto Fail;
/* store it into the driver */
driver->context = exec;
}
Exit:
return driver->context;
Fail:
FREE( exec );
return 0;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Done_Context */
/* */
/* <Description> */
/* Discards an execution context. */
/* */
/* <Input> */
/* exec :: A handle to the target execution context. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* Only the glyph loader and debugger should call this function. */
/* */
FT_LOCAL_DEF FT_Error
TT_Done_Context( TT_ExecContext exec )
{
/* Nothing at all for now */
FT_UNUSED( exec );
return TT_Err_Ok;
}
/* return length of given vector */
#ifdef FT_CONFIG_OPTION_OLD_CALCS
static FT_F26Dot6
Norm( FT_F26Dot6 X,
FT_F26Dot6 Y )
{
TT_INT64 T1, T2;
MUL_64( X, X, T1 );
MUL_64( Y, Y, T2 );
ADD_64( T1, T2, T1 );
return (FT_F26Dot6)SQRT_64( T1 );
}
#else /* !FT_CONFIG_OPTION_OLD_CALCS */
static FT_F26Dot6
Norm( FT_F26Dot6 X,
FT_F26Dot6 Y )
{
FT_Vector v;
v.x = X;
v.y = Y;
return FT_Vector_Length( &v );
}
#endif /* FT_CONFIG_OPTION_OLD_CALCS */
/*************************************************************************/
/* */
/* Before an opcode is executed, the interpreter verifies that there are */
/* enough arguments on the stack, with the help of the Pop_Push_Count */
/* table. */
/* */
/* For each opcode, the first column gives the number of arguments that */
/* are popped from the stack; the second one gives the number of those */
/* that are pushed in result. */
/* */
/* Note that for opcodes with a varying number of parameters, either 0 */
/* or 1 arg is verified before execution, depending on the nature of the */
/* instruction: */
/* */
/* - if the number of arguments is given by the bytecode stream or the */
/* loop variable, 0 is chosen. */
/* */
/* - if the first argument is a count n that is followed by arguments */
/* a1 .. an, then 1 is chosen. */
/* */
/*************************************************************************/
#undef PACK
#define PACK( x, y ) ( ( x << 4 ) | y )
static
const FT_Byte Pop_Push_Count[256] =
{
/* opcodes are gathered in groups of 16 */
/* please keep the spaces as they are */
/* SVTCA y */ PACK( 0, 0 ),
/* SVTCA x */ PACK( 0, 0 ),
/* SPvTCA y */ PACK( 0, 0 ),
/* SPvTCA x */ PACK( 0, 0 ),
/* SFvTCA y */ PACK( 0, 0 ),
/* SFvTCA x */ PACK( 0, 0 ),
/* SPvTL // */ PACK( 2, 0 ),
/* SPvTL + */ PACK( 2, 0 ),
/* SFvTL // */ PACK( 2, 0 ),
/* SFvTL + */ PACK( 2, 0 ),
/* SPvFS */ PACK( 2, 0 ),
/* SFvFS */ PACK( 2, 0 ),
/* GPV */ PACK( 0, 2 ),
/* GFV */ PACK( 0, 2 ),
/* SFvTPv */ PACK( 0, 0 ),
/* ISECT */ PACK( 5, 0 ),
/* SRP0 */ PACK( 1, 0 ),
/* SRP1 */ PACK( 1, 0 ),
/* SRP2 */ PACK( 1, 0 ),
/* SZP0 */ PACK( 1, 0 ),
/* SZP1 */ PACK( 1, 0 ),
/* SZP2 */ PACK( 1, 0 ),
/* SZPS */ PACK( 1, 0 ),
/* SLOOP */ PACK( 1, 0 ),
/* RTG */ PACK( 0, 0 ),
/* RTHG */ PACK( 0, 0 ),
/* SMD */ PACK( 1, 0 ),
/* ELSE */ PACK( 0, 0 ),
/* JMPR */ PACK( 1, 0 ),
/* SCvTCi */ PACK( 1, 0 ),
/* SSwCi */ PACK( 1, 0 ),
/* SSW */ PACK( 1, 0 ),
/* DUP */ PACK( 1, 2 ),
/* POP */ PACK( 1, 0 ),
/* CLEAR */ PACK( 0, 0 ),
/* SWAP */ PACK( 2, 2 ),
/* DEPTH */ PACK( 0, 1 ),
/* CINDEX */ PACK( 1, 1 ),
/* MINDEX */ PACK( 1, 0 ),
/* AlignPTS */ PACK( 2, 0 ),
/* INS_$28 */ PACK( 0, 0 ),
/* UTP */ PACK( 1, 0 ),
/* LOOPCALL */ PACK( 2, 0 ),
/* CALL */ PACK( 1, 0 ),
/* FDEF */ PACK( 1, 0 ),
/* ENDF */ PACK( 0, 0 ),
/* MDAP[0] */ PACK( 1, 0 ),
/* MDAP[1] */ PACK( 1, 0 ),
/* IUP[0] */ PACK( 0, 0 ),
/* IUP[1] */ PACK( 0, 0 ),
/* SHP[0] */ PACK( 0, 0 ),
/* SHP[1] */ PACK( 0, 0 ),
/* SHC[0] */ PACK( 1, 0 ),
/* SHC[1] */ PACK( 1, 0 ),
/* SHZ[0] */ PACK( 1, 0 ),
/* SHZ[1] */ PACK( 1, 0 ),
/* SHPIX */ PACK( 1, 0 ),
/* IP */ PACK( 0, 0 ),
/* MSIRP[0] */ PACK( 2, 0 ),
/* MSIRP[1] */ PACK( 2, 0 ),
/* AlignRP */ PACK( 0, 0 ),
/* RTDG */ PACK( 0, 0 ),
/* MIAP[0] */ PACK( 2, 0 ),
/* MIAP[1] */ PACK( 2, 0 ),
/* NPushB */ PACK( 0, 0 ),
/* NPushW */ PACK( 0, 0 ),
/* WS */ PACK( 2, 0 ),
/* RS */ PACK( 1, 1 ),
/* WCvtP */ PACK( 2, 0 ),
/* RCvt */ PACK( 1, 1 ),
/* GC[0] */ PACK( 1, 1 ),
/* GC[1] */ PACK( 1, 1 ),
/* SCFS */ PACK( 2, 0 ),
/* MD[0] */ PACK( 2, 1 ),
/* MD[1] */ PACK( 2, 1 ),
/* MPPEM */ PACK( 0, 1 ),
/* MPS */ PACK( 0, 1 ),
/* FlipON */ PACK( 0, 0 ),
/* FlipOFF */ PACK( 0, 0 ),
/* DEBUG */ PACK( 1, 0 ),
/* LT */ PACK( 2, 1 ),
/* LTEQ */ PACK( 2, 1 ),
/* GT */ PACK( 2, 1 ),
/* GTEQ */ PACK( 2, 1 ),
/* EQ */ PACK( 2, 1 ),
/* NEQ */ PACK( 2, 1 ),
/* ODD */ PACK( 1, 1 ),
/* EVEN */ PACK( 1, 1 ),
/* IF */ PACK( 1, 0 ),
/* EIF */ PACK( 0, 0 ),
/* AND */ PACK( 2, 1 ),
/* OR */ PACK( 2, 1 ),
/* NOT */ PACK( 1, 1 ),
/* DeltaP1 */ PACK( 1, 0 ),
/* SDB */ PACK( 1, 0 ),
/* SDS */ PACK( 1, 0 ),
/* ADD */ PACK( 2, 1 ),
/* SUB */ PACK( 2, 1 ),
/* DIV */ PACK( 2, 1 ),
/* MUL */ PACK( 2, 1 ),
/* ABS */ PACK( 1, 1 ),
/* NEG */ PACK( 1, 1 ),
/* FLOOR */ PACK( 1, 1 ),
/* CEILING */ PACK( 1, 1 ),
/* ROUND[0] */ PACK( 1, 1 ),
/* ROUND[1] */ PACK( 1, 1 ),
/* ROUND[2] */ PACK( 1, 1 ),
/* ROUND[3] */ PACK( 1, 1 ),
/* NROUND[0] */ PACK( 1, 1 ),
/* NROUND[1] */ PACK( 1, 1 ),
/* NROUND[2] */ PACK( 1, 1 ),
/* NROUND[3] */ PACK( 1, 1 ),
/* WCvtF */ PACK( 2, 0 ),
/* DeltaP2 */ PACK( 1, 0 ),
/* DeltaP3 */ PACK( 1, 0 ),
/* DeltaCn[0] */ PACK( 1, 0 ),
/* DeltaCn[1] */ PACK( 1, 0 ),
/* DeltaCn[2] */ PACK( 1, 0 ),
/* SROUND */ PACK( 1, 0 ),
/* S45Round */ PACK( 1, 0 ),
/* JROT */ PACK( 2, 0 ),
/* JROF */ PACK( 2, 0 ),
/* ROFF */ PACK( 0, 0 ),
/* INS_$7B */ PACK( 0, 0 ),
/* RUTG */ PACK( 0, 0 ),
/* RDTG */ PACK( 0, 0 ),
/* SANGW */ PACK( 1, 0 ),
/* AA */ PACK( 1, 0 ),
/* FlipPT */ PACK( 0, 0 ),
/* FlipRgON */ PACK( 2, 0 ),
/* FlipRgOFF */ PACK( 2, 0 ),
/* INS_$83 */ PACK( 0, 0 ),
/* INS_$84 */ PACK( 0, 0 ),
/* ScanCTRL */ PACK( 1, 0 ),
/* SDVPTL[0] */ PACK( 2, 0 ),
/* SDVPTL[1] */ PACK( 2, 0 ),
/* GetINFO */ PACK( 1, 1 ),
/* IDEF */ PACK( 1, 0 ),
/* ROLL */ PACK( 3, 3 ),
/* MAX */ PACK( 2, 1 ),
/* MIN */ PACK( 2, 1 ),
/* ScanTYPE */ PACK( 1, 0 ),
/* InstCTRL */ PACK( 2, 0 ),
/* INS_$8F */ PACK( 0, 0 ),
/* INS_$90 */ PACK( 0, 0 ),
/* INS_$91 */ PACK( 0, 0 ),
/* INS_$92 */ PACK( 0, 0 ),
/* INS_$93 */ PACK( 0, 0 ),
/* INS_$94 */ PACK( 0, 0 ),
/* INS_$95 */ PACK( 0, 0 ),
/* INS_$96 */ PACK( 0, 0 ),
/* INS_$97 */ PACK( 0, 0 ),
/* INS_$98 */ PACK( 0, 0 ),
/* INS_$99 */ PACK( 0, 0 ),
/* INS_$9A */ PACK( 0, 0 ),
/* INS_$9B */ PACK( 0, 0 ),
/* INS_$9C */ PACK( 0, 0 ),
/* INS_$9D */ PACK( 0, 0 ),
/* INS_$9E */ PACK( 0, 0 ),
/* INS_$9F */ PACK( 0, 0 ),
/* INS_$A0 */ PACK( 0, 0 ),
/* INS_$A1 */ PACK( 0, 0 ),
/* INS_$A2 */ PACK( 0, 0 ),
/* INS_$A3 */ PACK( 0, 0 ),
/* INS_$A4 */ PACK( 0, 0 ),
/* INS_$A5 */ PACK( 0, 0 ),
/* INS_$A6 */ PACK( 0, 0 ),
/* INS_$A7 */ PACK( 0, 0 ),
/* INS_$A8 */ PACK( 0, 0 ),
/* INS_$A9 */ PACK( 0, 0 ),
/* INS_$AA */ PACK( 0, 0 ),
/* INS_$AB */ PACK( 0, 0 ),
/* INS_$AC */ PACK( 0, 0 ),
/* INS_$AD */ PACK( 0, 0 ),
/* INS_$AE */ PACK( 0, 0 ),
/* INS_$AF */ PACK( 0, 0 ),
/* PushB[0] */ PACK( 0, 1 ),
/* PushB[1] */ PACK( 0, 2 ),
/* PushB[2] */ PACK( 0, 3 ),
/* PushB[3] */ PACK( 0, 4 ),
/* PushB[4] */ PACK( 0, 5 ),
/* PushB[5] */ PACK( 0, 6 ),
/* PushB[6] */ PACK( 0, 7 ),
/* PushB[7] */ PACK( 0, 8 ),
/* PushW[0] */ PACK( 0, 1 ),
/* PushW[1] */ PACK( 0, 2 ),
/* PushW[2] */ PACK( 0, 3 ),
/* PushW[3] */ PACK( 0, 4 ),
/* PushW[4] */ PACK( 0, 5 ),
/* PushW[5] */ PACK( 0, 6 ),
/* PushW[6] */ PACK( 0, 7 ),
/* PushW[7] */ PACK( 0, 8 ),
/* MDRP[00] */ PACK( 1, 0 ),
/* MDRP[01] */ PACK( 1, 0 ),
/* MDRP[02] */ PACK( 1, 0 ),
/* MDRP[03] */ PACK( 1, 0 ),
/* MDRP[04] */ PACK( 1, 0 ),
/* MDRP[05] */ PACK( 1, 0 ),
/* MDRP[06] */ PACK( 1, 0 ),
/* MDRP[07] */ PACK( 1, 0 ),
/* MDRP[08] */ PACK( 1, 0 ),
/* MDRP[09] */ PACK( 1, 0 ),
/* MDRP[10] */ PACK( 1, 0 ),
/* MDRP[11] */ PACK( 1, 0 ),
/* MDRP[12] */ PACK( 1, 0 ),
/* MDRP[13] */ PACK( 1, 0 ),
/* MDRP[14] */ PACK( 1, 0 ),
/* MDRP[15] */ PACK( 1, 0 ),
/* MDRP[16] */ PACK( 1, 0 ),
/* MDRP[17] */ PACK( 1, 0 ),
/* MDRP[18] */ PACK( 1, 0 ),
/* MDRP[19] */ PACK( 1, 0 ),
/* MDRP[20] */ PACK( 1, 0 ),
/* MDRP[21] */ PACK( 1, 0 ),
/* MDRP[22] */ PACK( 1, 0 ),
/* MDRP[23] */ PACK( 1, 0 ),
/* MDRP[24] */ PACK( 1, 0 ),
/* MDRP[25] */ PACK( 1, 0 ),
/* MDRP[26] */ PACK( 1, 0 ),
/* MDRP[27] */ PACK( 1, 0 ),
/* MDRP[28] */ PACK( 1, 0 ),
/* MDRP[29] */ PACK( 1, 0 ),
/* MDRP[30] */ PACK( 1, 0 ),
/* MDRP[31] */ PACK( 1, 0 ),
/* MIRP[00] */ PACK( 2, 0 ),
/* MIRP[01] */ PACK( 2, 0 ),
/* MIRP[02] */ PACK( 2, 0 ),
/* MIRP[03] */ PACK( 2, 0 ),
/* MIRP[04] */ PACK( 2, 0 ),
/* MIRP[05] */ PACK( 2, 0 ),
/* MIRP[06] */ PACK( 2, 0 ),
/* MIRP[07] */ PACK( 2, 0 ),
/* MIRP[08] */ PACK( 2, 0 ),
/* MIRP[09] */ PACK( 2, 0 ),
/* MIRP[10] */ PACK( 2, 0 ),
/* MIRP[11] */ PACK( 2, 0 ),
/* MIRP[12] */ PACK( 2, 0 ),
/* MIRP[13] */ PACK( 2, 0 ),
/* MIRP[14] */ PACK( 2, 0 ),
/* MIRP[15] */ PACK( 2, 0 ),
/* MIRP[16] */ PACK( 2, 0 ),
/* MIRP[17] */ PACK( 2, 0 ),
/* MIRP[18] */ PACK( 2, 0 ),
/* MIRP[19] */ PACK( 2, 0 ),
/* MIRP[20] */ PACK( 2, 0 ),
/* MIRP[21] */ PACK( 2, 0 ),
/* MIRP[22] */ PACK( 2, 0 ),
/* MIRP[23] */ PACK( 2, 0 ),
/* MIRP[24] */ PACK( 2, 0 ),
/* MIRP[25] */ PACK( 2, 0 ),
/* MIRP[26] */ PACK( 2, 0 ),
/* MIRP[27] */ PACK( 2, 0 ),
/* MIRP[28] */ PACK( 2, 0 ),
/* MIRP[29] */ PACK( 2, 0 ),
/* MIRP[30] */ PACK( 2, 0 ),
/* MIRP[31] */ PACK( 2, 0 )
};
static
const FT_Char opcode_length[256] =
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-1,-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 3, 4, 5, 6, 7, 8, 9, 3, 5, 7, 9, 11,13,15,17,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
static
const FT_Vector Null_Vector = {0,0};
#undef PACK
#undef NULL_Vector
#define NULL_Vector (FT_Vector*)&Null_Vector
/*************************************************************************/
/* */
/* <Function> */
/* Current_Ratio */
/* */
/* <Description> */
/* Returns the current aspect ratio scaling factor depending on the */
/* projection vector's state and device resolutions. */
/* */
/* <Return> */
/* The aspect ratio in 16.16 format, always <= 1.0 . */
/* */
static FT_Long
Current_Ratio( EXEC_OP )
{
if ( CUR.tt_metrics.ratio )
return CUR.tt_metrics.ratio;
if ( CUR.GS.projVector.y == 0 )
CUR.tt_metrics.ratio = CUR.tt_metrics.x_ratio;
else if ( CUR.GS.projVector.x == 0 )
CUR.tt_metrics.ratio = CUR.tt_metrics.y_ratio;
else
{
FT_Long x, y;
x = TT_MULDIV( CUR.GS.projVector.x, CUR.tt_metrics.x_ratio, 0x4000 );
y = TT_MULDIV( CUR.GS.projVector.y, CUR.tt_metrics.y_ratio, 0x4000 );
CUR.tt_metrics.ratio = Norm( x, y );
}
return CUR.tt_metrics.ratio;
}
static FT_Long
Current_Ppem( EXEC_OP )
{
return TT_MULFIX( CUR.tt_metrics.ppem, CURRENT_Ratio() );
}
/*************************************************************************/
/* */
/* Functions related to the control value table (CVT). */
/* */
/*************************************************************************/
FT_CALLBACK_DEF( FT_F26Dot6 )
Read_CVT( EXEC_OP_ FT_ULong index )
{
return CUR.cvt[index];
}
FT_CALLBACK_DEF( FT_F26Dot6 )
Read_CVT_Stretched( EXEC_OP_ FT_ULong index )
{
return TT_MULFIX( CUR.cvt[index], CURRENT_Ratio() );
}
FT_CALLBACK_DEF( void )
Write_CVT( EXEC_OP_ FT_ULong index,
FT_F26Dot6 value )
{
CUR.cvt[index] = value;
}
FT_CALLBACK_DEF( void )
Write_CVT_Stretched( EXEC_OP_ FT_ULong index,
FT_F26Dot6 value )
{
CUR.cvt[index] = FT_DivFix( value, CURRENT_Ratio() );
}
FT_CALLBACK_DEF( void )
Move_CVT( EXEC_OP_ FT_ULong index,
FT_F26Dot6 value )
{
CUR.cvt[index] += value;
}
FT_CALLBACK_DEF( void )
Move_CVT_Stretched( EXEC_OP_ FT_ULong index,
FT_F26Dot6 value )
{
CUR.cvt[index] += FT_DivFix( value, CURRENT_Ratio() );
}
/*************************************************************************/
/* */
/* <Function> */
/* GetShortIns */
/* */
/* <Description> */
/* Returns a short integer taken from the instruction stream at */
/* address IP. */
/* */
/* <Return> */
/* Short read at code[IP]. */
/* */
/* <Note> */
/* This one could become a macro. */
/* */
static FT_Short
GetShortIns( EXEC_OP )
{
/* Reading a byte stream so there is no endianess (DaveP) */
CUR.IP += 2;
return (FT_Short)( ( CUR.code[CUR.IP - 2] << 8 ) +
CUR.code[CUR.IP - 1] );
}
/*************************************************************************/
/* */
/* <Function> */
/* Ins_Goto_CodeRange */
/* */
/* <Description> */
/* Goes to a certain code range in the instruction stream. */
/* */
/* <Input> */
/* aRange :: The index of the code range. */
/* */
/* aIP :: The new IP address in the code range. */
/* */
/* <Return> */
/* SUCCESS or FAILURE. */
/* */
static FT_Bool
Ins_Goto_CodeRange( EXEC_OP_ FT_Int aRange,
FT_ULong aIP )
{
TT_CodeRange* range;
if ( aRange < 1 || aRange > 3 )
{
CUR.error = TT_Err_Bad_Argument;
return FAILURE;
}
range = &CUR.codeRangeTable[aRange - 1];
if ( range->base == NULL ) /* invalid coderange */
{
CUR.error = TT_Err_Invalid_CodeRange;
return FAILURE;
}
/* 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 AIP <= Size, instead of AIP < Size. */
if ( aIP > range->size )
{
CUR.error = TT_Err_Code_Overflow;
return FAILURE;
}
CUR.code = range->base;
CUR.codeSize = range->size;
CUR.IP = aIP;
CUR.curRange = aRange;
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* Direct_Move */
/* */
/* <Description> */
/* Moves a point by a given distance along the freedom vector. The */
/* point will be `touched'. */
/* */
/* <Input> */
/* point :: The index of the point to move. */
/* */
/* distance :: The distance to apply. */
/* */
/* <InOut> */
/* zone :: The affected glyph zone. */
/* */
static void
Direct_Move( EXEC_OP_ TT_GlyphZone* zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_F26Dot6 v;
v = CUR.GS.freeVector.x;
if ( v != 0 )
{
#ifdef NO_APPLE_PATENT
if ( ABS( CUR.F_dot_P ) > APPLE_THRESHOLD )
zone->cur[point].x += distance;
#else
zone->cur[point].x += TT_MULDIV( distance,
v * 0x10000L,
CUR.F_dot_P );
#endif
zone->tags[point] |= FT_Curve_Tag_Touch_X;
}
v = CUR.GS.freeVector.y;
if ( v != 0 )
{
#ifdef NO_APPLE_PATENT
if ( ABS( CUR.F_dot_P ) > APPLE_THRESHOLD )
zone->cur[point].y += distance;
#else
zone->cur[point].y += TT_MULDIV( distance,
v * 0x10000L,
CUR.F_dot_P );
#endif
zone->tags[point] |= FT_Curve_Tag_Touch_Y;
}
}
/*************************************************************************/
/* */
/* Special versions of Direct_Move() */
/* */
/* The following versions are used whenever both vectors are both */
/* along one of the coordinate unit vectors, i.e. in 90% of the cases. */
/* */
/*************************************************************************/
static void
Direct_Move_X( EXEC_OP_ TT_GlyphZone* zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_UNUSED_EXEC;
zone->cur[point].x += distance;
zone->tags[point] |= FT_Curve_Tag_Touch_X;
}
static void
Direct_Move_Y( EXEC_OP_ TT_GlyphZone* zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_UNUSED_EXEC;
zone->cur[point].y += distance;
zone->tags[point] |= FT_Curve_Tag_Touch_Y;
}
/*************************************************************************/
/* */
/* <Function> */
/* Round_None */
/* */
/* <Description> */
/* Does not round, but adds engine compensation. */
/* */
/* <Input> */
/* distance :: The distance (not) to round. */
/* */
/* compensation :: The engine compensation. */
/* */
/* <Return> */
/* The compensated distance. */
/* */
/* <Note> */
/* The TrueType specification says very few about the relationship */
/* between rounding and engine compensation. However, it seems from */
/* the description of super round that we should add the compensation */
/* before rounding. */
/* */
static FT_F26Dot6
Round_None( EXEC_OP_ FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED_EXEC;
if ( distance >= 0 )
{
val = distance + compensation;
if ( val < 0 )
val = 0;
}
else {
val = distance - compensation;
if ( val > 0 )
val = 0;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Round_To_Grid */
/* */
/* <Description> */
/* Rounds value to grid after adding engine compensation. */
/* */
/* <Input> */
/* distance :: The distance to round. */
/* */
/* compensation :: The engine compensation. */
/* */
/* <Return> */
/* Rounded distance. */
/* */
static FT_F26Dot6
Round_To_Grid( EXEC_OP_ FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED_EXEC;
if ( distance >= 0 )
{
val = distance + compensation + 32;
if ( val > 0 )
val &= ~63;
else
val = 0;
}
else
{
val = -( ( compensation - distance + 32 ) & -64 );
if ( val > 0 )
val = 0;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Round_To_Half_Grid */
/* */
/* <Description> */
/* Rounds value to half grid after adding engine compensation. */
/* */
/* <Input> */
/* distance :: The distance to round. */
/* */
/* compensation :: The engine compensation. */
/* */
/* <Return> */
/* Rounded distance. */
/* */
static FT_F26Dot6
Round_To_Half_Grid( EXEC_OP_ FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED_EXEC;
if ( distance >= 0 )
{
val = ( ( distance + compensation ) & -64 ) + 32;
if ( val < 0 )
val = 0;
}
else
{
val = -( ( (compensation - distance) & -64 ) + 32 );
if ( val > 0 )
val = 0;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Round_Down_To_Grid */
/* */
/* <Description> */
/* Rounds value down to grid after adding engine compensation. */
/* */
/* <Input> */
/* distance :: The distance to round. */
/* */
/* compensation :: The engine compensation. */
/* */
/* <Return> */
/* Rounded distance. */
/* */
static FT_F26Dot6
Round_Down_To_Grid( EXEC_OP_ FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED_EXEC;
if ( distance >= 0 )
{
val = distance + compensation;
if ( val > 0 )
val &= ~63;
else
val = 0;
}
else
{
val = -( ( compensation - distance ) & -64 );
if ( val > 0 )
val = 0;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Round_Up_To_Grid */
/* */
/* <Description> */
/* Rounds value up to grid after adding engine compensation. */
/* */
/* <Input> */
/* distance :: The distance to round. */
/* */
/* compensation :: The engine compensation. */
/* */
/* <Return> */
/* Rounded distance. */
/* */
static FT_F26Dot6
Round_Up_To_Grid( EXEC_OP_ FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED_EXEC;
if ( distance >= 0 )
{
val = distance + compensation + 63;
if ( val > 0 )
val &= ~63;
else
val = 0;
}
else
{
val = -( ( compensation - distance + 63 ) & -64 );
if ( val > 0 )
val = 0;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Round_To_Double_Grid */
/* */
/* <Description> */
/* Rounds value to double grid after adding engine compensation. */
/* */
/* <Input> */
/* distance :: The distance to round. */
/* */
/* compensation :: The engine compensation. */
/* */
/* <Return> */
/* Rounded distance. */
/* */
static FT_F26Dot6
Round_To_Double_Grid( EXEC_OP_ FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED_EXEC;
if ( distance >= 0 )
{
val = distance + compensation + 16;
if ( val > 0 )
val &= ~31;
else
val = 0;
}
else
{
val = -( ( compensation - distance + 16 ) & -32 );
if ( val > 0 )
val = 0;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Round_Super */
/* */
/* <Description> */
/* Super-rounds value to grid after adding engine compensation. */
/* */
/* <Input> */
/* distance :: The distance to round. */
/* */
/* compensation :: The engine compensation. */
/* */
/* <Return> */
/* Rounded distance. */
/* */
/* <Note> */
/* The TrueType specification says very few about the relationship */
/* between rounding and engine compensation. However, it seems from */
/* the description of super round that we should add the compensation */
/* before rounding. */
/* */
static FT_F26Dot6
Round_Super( EXEC_OP_ FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
if ( distance >= 0 )
{
val = ( distance - CUR.phase + CUR.threshold + compensation ) &
-CUR.period;
if ( val < 0 )
val = 0;
val += CUR.phase;
}
else
{
val = -( ( CUR.threshold - CUR.phase - distance + compensation ) &
-CUR.period );
if ( val > 0 )
val = 0;
val -= CUR.phase;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Round_Super_45 */
/* */
/* <Description> */
/* Super-rounds value to grid after adding engine compensation. */
/* */
/* <Input> */
/* distance :: The distance to round. */
/* */
/* compensation :: The engine compensation. */
/* */
/* <Return> */
/* Rounded distance. */
/* */
/* <Note> */
/* There is a separate function for Round_Super_45() as we may need */
/* greater precision. */
/* */
static FT_F26Dot6
Round_Super_45( EXEC_OP_ FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
if ( distance >= 0 )
{
val = ( ( distance - CUR.phase + CUR.threshold + compensation ) /
CUR.period ) * CUR.period;
if ( val < 0 )
val = 0;
val += CUR.phase;
}
else
{
val = -( ( ( CUR.threshold - CUR.phase - distance + compensation ) /
CUR.period ) * CUR.period );
if ( val > 0 )
val = 0;
val -= CUR.phase;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Compute_Round */
/* */
/* <Description> */
/* Sets the rounding mode. */
/* */
/* <Input> */
/* round_mode :: The rounding mode to be used. */
/* */
static void
Compute_Round( EXEC_OP_ FT_Byte round_mode )
{
switch ( round_mode )
{
case TT_Round_Off:
CUR.func_round = (TT_Round_Func)Round_None;
break;
case TT_Round_To_Grid:
CUR.func_round = (TT_Round_Func)Round_To_Grid;
break;
case TT_Round_Up_To_Grid:
CUR.func_round = (TT_Round_Func)Round_Up_To_Grid;
break;
case TT_Round_Down_To_Grid:
CUR.func_round = (TT_Round_Func)Round_Down_To_Grid;
break;
case TT_Round_To_Half_Grid:
CUR.func_round = (TT_Round_Func)Round_To_Half_Grid;
break;
case TT_Round_To_Double_Grid:
CUR.func_round = (TT_Round_Func)Round_To_Double_Grid;
break;
case TT_Round_Super:
CUR.func_round = (TT_Round_Func)Round_Super;
break;
case TT_Round_Super_45:
CUR.func_round = (TT_Round_Func)Round_Super_45;
break;
}
}
/*************************************************************************/
/* */
/* <Function> */
/* SetSuperRound */
/* */
/* <Description> */
/* Sets Super Round parameters. */
/* */
/* <Input> */
/* GridPeriod :: Grid period */
/* selector :: SROUND opcode */
/* */
static void
SetSuperRound( EXEC_OP_ FT_F26Dot6 GridPeriod,
FT_Long selector )
{
switch ( (FT_Int)( selector & 0xC0 ) )
{
case 0:
CUR.period = GridPeriod / 2;
break;
case 0x40:
CUR.period = GridPeriod;
break;
case 0x80:
CUR.period = GridPeriod * 2;
break;
/* This opcode is reserved, but... */
case 0xC0:
CUR.period = GridPeriod;
break;
}
switch ( (FT_Int)( selector & 0x30 ) )
{
case 0:
CUR.phase = 0;
break;
case 0x10:
CUR.phase = CUR.period / 4;
break;
case 0x20:
CUR.phase = CUR.period / 2;
break;
case 0x30:
CUR.phase = GridPeriod * 3 / 4;
break;
}
if ( (selector & 0x0F) == 0 )
CUR.threshold = CUR.period - 1;
else
CUR.threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * CUR.period / 8;
CUR.period /= 256;
CUR.phase /= 256;
CUR.threshold /= 256;
}
/*************************************************************************/
/* */
/* <Function> */
/* Project */
/* */
/* <Description> */
/* Computes the projection of vector given by (v2-v1) along the */
/* current projection vector. */
/* */
/* <Input> */
/* v1 :: First input vector. */
/* v2 :: Second input vector. */
/* */
/* <Return> */
/* The distance in F26dot6 format. */
/* */
static FT_F26Dot6
Project( EXEC_OP_ FT_Vector* v1,
FT_Vector* v2 )
{
return TT_MULDIV( v1->x - v2->x, CUR.GS.projVector.x, 0x4000 ) +
TT_MULDIV( v1->y - v2->y, CUR.GS.projVector.y, 0x4000 );
}
/*************************************************************************/
/* */
/* <Function> */
/* Dual_Project */
/* */
/* <Description> */
/* Computes the projection of the vector given by (v2-v1) along the */
/* current dual vector. */
/* */
/* <Input> */
/* v1 :: First input vector. */
/* v2 :: Second input vector. */
/* */
/* <Return> */
/* The distance in F26dot6 format. */
/* */
static FT_F26Dot6
Dual_Project( EXEC_OP_ FT_Vector* v1,
FT_Vector* v2 )
{
return TT_MULDIV( v1->x - v2->x, CUR.GS.dualVector.x, 0x4000 ) +
TT_MULDIV( v1->y - v2->y, CUR.GS.dualVector.y, 0x4000 );
}
/*************************************************************************/
/* */
/* <Function> */
/* Free_Project */
/* */
/* <Description> */
/* Computes the projection of the vector given by (v2-v1) along the */
/* current freedom vector. */
/* */
/* <Input> */
/* v1 :: First input vector. */
/* v2 :: Second input vector. */
/* */
/* <Return> */
/* The distance in F26dot6 format. */
/* */
static FT_F26Dot6
Free_Project( EXEC_OP_ FT_Vector* v1,
FT_Vector* v2 )
{
return TT_MULDIV( v1->x - v2->x, CUR.GS.freeVector.x, 0x4000 ) +
TT_MULDIV( v1->y - v2->y, CUR.GS.freeVector.y, 0x4000 );
}
/*************************************************************************/
/* */
/* <Function> */
/* Project_x */
/* */
/* <Description> */
/* Computes the projection of the vector given by (v2-v1) along the */
/* horizontal axis. */
/* */
/* <Input> */
/* v1 :: First input vector. */
/* v2 :: Second input vector. */
/* */
/* <Return> */
/* The distance in F26dot6 format. */
/* */
static FT_F26Dot6
Project_x( EXEC_OP_ FT_Vector* v1,
FT_Vector* v2 )
{
FT_UNUSED_EXEC;
return ( v1->x - v2->x );
}
/*************************************************************************/
/* */
/* <Function> */
/* Project_y */
/* */
/* <Description> */
/* Computes the projection of the vector given by (v2-v1) along the */
/* vertical axis. */
/* */
/* <Input> */
/* v1 :: First input vector. */
/* v2 :: Second input vector. */
/* */
/* <Return> */
/* The distance in F26dot6 format. */
/* */
static FT_F26Dot6
Project_y( EXEC_OP_ FT_Vector* v1,
FT_Vector* v2 )
{
FT_UNUSED_EXEC;
return ( v1->y - v2->y );
}
/*************************************************************************/
/* */
/* <Function> */
/* Compute_Funcs */
/* */
/* <Description> */
/* Computes the projection and movement function pointers according */
/* to the current graphics state. */
/* */
static void
Compute_Funcs( EXEC_OP )
{
if ( CUR.GS.freeVector.x == 0x4000 )
{
CUR.func_freeProj = (TT_Project_Func)Project_x;
CUR.F_dot_P = CUR.GS.projVector.x * 0x10000L;
}
else
{
if ( CUR.GS.freeVector.y == 0x4000 )
{
CUR.func_freeProj = (TT_Project_Func)Project_y;
CUR.F_dot_P = CUR.GS.projVector.y * 0x10000L;
}
else
{
CUR.func_freeProj = (TT_Project_Func)Free_Project;
CUR.F_dot_P = (FT_Long)CUR.GS.projVector.x * CUR.GS.freeVector.x * 4 +
(FT_Long)CUR.GS.projVector.y * CUR.GS.freeVector.y * 4;
}
}
if ( CUR.GS.projVector.x == 0x4000 )
CUR.func_project = (TT_Project_Func)Project_x;
else
{
if ( CUR.GS.projVector.y == 0x4000 )
CUR.func_project = (TT_Project_Func)Project_y;
else
CUR.func_project = (TT_Project_Func)Project;
}
if ( CUR.GS.dualVector.x == 0x4000 )
CUR.func_dualproj = (TT_Project_Func)Project_x;
else
{
if ( CUR.GS.dualVector.y == 0x4000 )
CUR.func_dualproj = (TT_Project_Func)Project_y;
else
CUR.func_dualproj = (TT_Project_Func)Dual_Project;
}
CUR.func_move = (TT_Move_Func)Direct_Move;
if ( CUR.F_dot_P == 0x40000000L )
{
if ( CUR.GS.freeVector.x == 0x4000 )
CUR.func_move = (TT_Move_Func)Direct_Move_X;
else
{
if ( CUR.GS.freeVector.y == 0x4000 )
CUR.func_move = (TT_Move_Func)Direct_Move_Y;
}
}
/* at small sizes, F_dot_P can become too small, resulting */
/* in overflows and `spikes' in a number of glyphs like `w'. */
if ( ABS( CUR.F_dot_P ) < 0x4000000L )
CUR.F_dot_P = 0x40000000L;
/* Disable cached aspect ratio */
CUR.tt_metrics.ratio = 0;
}
/*************************************************************************/
/* */
/* <Function> */
/* Normalize */
/* */
/* <Description> */
/* Norms a vector. */
/* */
/* <Input> */
/* Vx :: The horizontal input vector coordinate. */
/* Vy :: The vertical input vector coordinate. */
/* */
/* <Output> */
/* R :: The normed unit vector. */
/* */
/* <Return> */
/* Returns FAILURE if a vector parameter is zero. */
/* */
/* <Note> */
/* In case Vx and Vy are both zero, Normalize() returns SUCCESS, and */
/* R is undefined. */
/* */
#ifdef FT_CONFIG_OPTION_OLD_CALCS
static FT_Bool
Normalize( EXEC_OP_ FT_F26Dot6 Vx,
FT_F26Dot6 Vy,
FT_UnitVector* R )
{
FT_F26Dot6 W;
FT_Bool S1, S2;
FT_UNUSED_EXEC;
if ( ABS( Vx ) < 0x10000L && ABS( Vy ) < 0x10000L )
{
Vx *= 0x100;
Vy *= 0x100;
W = Norm( Vx, Vy );
if ( W == 0 )
{
/* XXX: UNDOCUMENTED! It seems that it is possible to try */
/* to normalize the vector (0,0). Return immediately. */
return SUCCESS;
}
R->x = (FT_F2Dot14)FT_MulDiv( Vx, 0x4000L, W );
R->y = (FT_F2Dot14)FT_MulDiv( Vy, 0x4000L, W );
return SUCCESS;
}
W = Norm( Vx, Vy );
Vx = FT_MulDiv( Vx, 0x4000L, W );
Vy = FT_MulDiv( Vy, 0x4000L, W );
W = Vx * Vx + Vy * Vy;
/* Now, we want that Sqrt( W ) = 0x4000 */
/* Or 0x1000000 <= W < 0x1004000 */
if ( Vx < 0 )
{
Vx = -Vx;
S1 = TRUE;
}
else
S1 = FALSE;
if ( Vy < 0 )
{
Vy = -Vy;
S2 = TRUE;
}
else
S2 = FALSE;
while ( W < 0x1000000L )
{
/* We need to increase W by a minimal amount */
if ( Vx < Vy )
Vx++;
else
Vy++;
W = Vx * Vx + Vy * Vy;
}
while ( W >= 0x1004000L )
{
/* We need to decrease W by a minimal amount */
if ( Vx < Vy )
Vx--;
else
Vy--;
W = Vx * Vx + Vy * Vy;
}
/* Note that in various cases, we can only */
/* compute a Sqrt(W) of 0x3FFF, eg. Vx = Vy */
if ( S1 )
Vx = -Vx;
if ( S2 )
Vy = -Vy;
R->x = (FT_F2Dot14)Vx; /* Type conversion */
R->y = (FT_F2Dot14)Vy; /* Type conversion */
return SUCCESS;
}
#else
static FT_Bool
Normalize( EXEC_OP_ FT_F26Dot6 Vx,
FT_F26Dot6 Vy,
FT_UnitVector* R )
{
FT_Vector v;
FT_Angle angle;
FT_UNUSED_EXEC;
angle = FT_Atan2( Vx, Vy );
FT_Vector_Unit( &v, angle );
R->x = (short)(v.x >> 2);
R->y = (short)(v.y >> 2);
return SUCCESS;
}
#endif /* FT_CONFIG_OPTION_OLD_CALCS */
/*************************************************************************/
/* */
/* Here we start with the implementation of the various opcodes. */
/* */
/*************************************************************************/
static FT_Bool
Ins_SxVTL( EXEC_OP_ FT_UShort aIdx1,
FT_UShort aIdx2,
FT_Int aOpc,
FT_UnitVector* Vec )
{
FT_Long A, B, C;
FT_Vector* p1;
FT_Vector* p2;
if ( BOUNDS( aIdx1, CUR.zp2.n_points ) ||
BOUNDS( aIdx2, CUR.zp1.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return FAILURE;
}
p1 = CUR.zp1.cur + aIdx2;
p2 = CUR.zp2.cur + aIdx1;
A = p1->x - p2->x;
B = p1->y - p2->y;
if ( ( aOpc & 1 ) != 0 )
{
C = B; /* counter clockwise rotation */
B = A;
A = -C;
}
NORMalize( A, B, Vec );
return SUCCESS;
}
/* When not using the big switch statements, the interpreter uses a */
/* call table defined later below in this source. Each opcode must */
/* thus have a corresponding function, even trivial ones. */
/* */
/* They are all defined there. */
#define DO_SVTCA \
{ \
FT_Short A, B; \
\
\
A = (FT_Short)( CUR.opcode & 1 ) << 14; \
B = A ^ (FT_Short)0x4000; \
\
CUR.GS.freeVector.x = A; \
CUR.GS.projVector.x = A; \
CUR.GS.dualVector.x = A; \
\
CUR.GS.freeVector.y = B; \
CUR.GS.projVector.y = B; \
CUR.GS.dualVector.y = B; \
\
COMPUTE_Funcs(); \
}
#define DO_SPVTCA \
{ \
FT_Short A, B; \
\
\
A = (FT_Short)( CUR.opcode & 1 ) << 14; \
B = A ^ (FT_Short)0x4000; \
\
CUR.GS.projVector.x = A; \
CUR.GS.dualVector.x = A; \
\
CUR.GS.projVector.y = B; \
CUR.GS.dualVector.y = B; \
\
COMPUTE_Funcs(); \
}
#define DO_SFVTCA \
{ \
FT_Short A, B; \
\
\
A = (FT_Short)( CUR.opcode & 1 ) << 14; \
B = A ^ (FT_Short)0x4000; \
\
CUR.GS.freeVector.x = A; \
CUR.GS.freeVector.y = B; \
\
COMPUTE_Funcs(); \
}
#define DO_SPVTL \
if ( INS_SxVTL( (FT_UShort)args[1], \
(FT_UShort)args[0], \
CUR.opcode, \
&CUR.GS.projVector ) == SUCCESS ) \
{ \
CUR.GS.dualVector = CUR.GS.projVector; \
COMPUTE_Funcs(); \
}
#define DO_SFVTL \
if ( INS_SxVTL( (FT_UShort)args[1], \
(FT_UShort)args[0], \
CUR.opcode, \
&CUR.GS.freeVector ) == SUCCESS ) \
COMPUTE_Funcs();
#define DO_SFVTPV \
CUR.GS.freeVector = CUR.GS.projVector; \
COMPUTE_Funcs();
#define DO_SPVFS \
{ \
FT_Short S; \
FT_Long X, Y; \
\
\
/* Only use low 16bits, then sign extend */ \
S = (FT_Short)args[1]; \
Y = (FT_Long)S; \
S = (FT_Short)args[0]; \
X = (FT_Long)S; \
\
NORMalize( X, Y, &CUR.GS.projVector ); \
\
CUR.GS.dualVector = CUR.GS.projVector; \
COMPUTE_Funcs(); \
}
#define DO_SFVFS \
{ \
FT_Short S; \
FT_Long X, Y; \
\
\
/* Only use low 16bits, then sign extend */ \
S = (FT_Short)args[1]; \
Y = (FT_Long)S; \
S = (FT_Short)args[0]; \
X = S; \
\
NORMalize( X, Y, &CUR.GS.freeVector ); \
COMPUTE_Funcs(); \
}
#define DO_GPV \
args[0] = CUR.GS.projVector.x; \
args[1] = CUR.GS.projVector.y;
#define DO_GFV \
args[0] = CUR.GS.freeVector.x; \
args[1] = CUR.GS.freeVector.y;
#define DO_SRP0 \
CUR.GS.rp0 = (FT_UShort)args[0];
#define DO_SRP1 \
CUR.GS.rp1 = (FT_UShort)args[0];
#define DO_SRP2 \
CUR.GS.rp2 = (FT_UShort)args[0];
#define DO_RTHG \
CUR.GS.round_state = TT_Round_To_Half_Grid; \
CUR.func_round = (TT_Round_Func)Round_To_Half_Grid;
#define DO_RTG \
CUR.GS.round_state = TT_Round_To_Grid; \
CUR.func_round = (TT_Round_Func)Round_To_Grid;
#define DO_RTDG \
CUR.GS.round_state = TT_Round_To_Double_Grid; \
CUR.func_round = (TT_Round_Func)Round_To_Double_Grid;
#define DO_RUTG \
CUR.GS.round_state = TT_Round_Up_To_Grid; \
CUR.func_round = (TT_Round_Func)Round_Up_To_Grid;
#define DO_RDTG \
CUR.GS.round_state = TT_Round_Down_To_Grid; \
CUR.func_round = (TT_Round_Func)Round_Down_To_Grid;
#define DO_ROFF \
CUR.GS.round_state = TT_Round_Off; \
CUR.func_round = (TT_Round_Func)Round_None;
#define DO_SROUND \
SET_SuperRound( 0x4000, args[0] ); \
CUR.GS.round_state = TT_Round_Super; \
CUR.func_round = (TT_Round_Func)Round_Super;
#define DO_S45ROUND \
SET_SuperRound( 0x2D41, args[0] ); \
CUR.GS.round_state = TT_Round_Super_45; \
CUR.func_round = (TT_Round_Func)Round_Super_45;
#define DO_SLOOP \
if ( args[0] < 0 ) \
CUR.error = TT_Err_Bad_Argument; \
else \
CUR.GS.loop = args[0];
#define DO_SMD \
CUR.GS.minimum_distance = args[0];
#define DO_SCVTCI \
CUR.GS.control_value_cutin = (FT_F26Dot6)args[0];
#define DO_SSWCI \
CUR.GS.single_width_cutin = (FT_F26Dot6)args[0];
/* XXX: UNDOCUMENTED! or bug in the Windows engine? */
/* */
/* It seems that the value that is read here is */
/* expressed in 16.16 format rather than in font */
/* units. */
/* */
#define DO_SSW \
CUR.GS.single_width_value = (FT_F26Dot6)( args[0] >> 10 );
#define DO_FLIPON \
CUR.GS.auto_flip = TRUE;
#define DO_FLIPOFF \
CUR.GS.auto_flip = FALSE;
#define DO_SDB \
CUR.GS.delta_base = (FT_Short)args[0];
#define DO_SDS \
CUR.GS.delta_shift = (FT_Short)args[0];
#define DO_MD /* nothing */
#define DO_MPPEM \
args[0] = CURRENT_Ppem();
/* Note: The pointSize should be irrelevant in a given font program; */
/* we thus decide to return only the ppem. */
#if 0
#define DO_MPS \
args[0] = CUR.metrics.pointSize;
#else
#define DO_MPS \
args[0] = CURRENT_Ppem();
#endif /* 0 */
#define DO_DUP \
args[1] = args[0];
#define DO_CLEAR \
CUR.new_top = 0;
#define DO_SWAP \
{ \
FT_Long L; \
\
\
L = args[0]; \
args[0] = args[1]; \
args[1] = L; \
}
#define DO_DEPTH \
args[0] = CUR.top;
#define DO_CINDEX \
{ \
FT_Long L; \
\
\
L = args[0]; \
\
if ( L <= 0 || L > CUR.args ) \
CUR.error = TT_Err_Invalid_Reference; \
else \
args[0] = CUR.stack[CUR.args - L]; \
}
#define DO_JROT \
if ( args[1] != 0 ) \
{ \
CUR.IP += args[0]; \
CUR.step_ins = FALSE; \
}
#define DO_JMPR \
CUR.IP += args[0]; \
CUR.step_ins = FALSE;
#define DO_JROF \
if ( args[1] == 0 ) \
{ \
CUR.IP += args[0]; \
CUR.step_ins = FALSE; \
}
#define DO_LT \
args[0] = ( args[0] < args[1] );
#define DO_LTEQ \
args[0] = ( args[0] <= args[1] );
#define DO_GT \
args[0] = ( args[0] > args[1] );
#define DO_GTEQ \
args[0] = ( args[0] >= args[1] );
#define DO_EQ \
args[0] = ( args[0] == args[1] );
#define DO_NEQ \
args[0] = ( args[0] != args[1] );
#define DO_ODD \
args[0] = ( ( CUR_Func_round( args[0], 0 ) & 127 ) == 64 );
#define DO_EVEN \
args[0] = ( ( CUR_Func_round( args[0], 0 ) & 127 ) == 0 );
#define DO_AND \
args[0] = ( args[0] && args[1] );
#define DO_OR \
args[0] = ( args[0] || args[1] );
#define DO_NOT \
args[0] = !args[0];
#define DO_ADD \
args[0] += args[1];
#define DO_SUB \
args[0] -= args[1];
#define DO_DIV \
if ( args[1] == 0 ) \
CUR.error = TT_Err_Divide_By_Zero; \
else \
args[0] = TT_MULDIV( args[0], 64L, args[1] );
#define DO_MUL \
args[0] = TT_MULDIV( args[0], args[1], 64L );
#define DO_ABS \
args[0] = ABS( args[0] );
#define DO_NEG \
args[0] = -args[0];
#define DO_FLOOR \
args[0] &= -64;
#define DO_CEILING \
args[0] = ( args[0] + 63 ) & -64;
#define DO_RS \
{ \
FT_ULong I = (FT_ULong)args[0]; \
\
\
if ( BOUNDS( I, CUR.storeSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
else \
args[0] = 0; \
} \
else \
args[0] = CUR.storage[I]; \
}
#define DO_WS \
{ \
FT_ULong I = (FT_ULong)args[0]; \
\
\
if ( BOUNDS( I, CUR.storeSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
} \
else \
CUR.storage[I] = args[1]; \
}
#define DO_RCVT \
{ \
FT_ULong I = (FT_ULong)args[0]; \
\
\
if ( BOUNDS( I, CUR.cvtSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
else \
args[0] = 0; \
} \
else \
args[0] = CUR_Func_read_cvt( I ); \
}
#define DO_WCVTP \
{ \
FT_ULong I = (FT_ULong)args[0]; \
\
\
if ( BOUNDS( I, CUR.cvtSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
} \
else \
CUR_Func_write_cvt( I, args[1] ); \
}
#define DO_WCVTF \
{ \
FT_ULong I = (FT_ULong)args[0]; \
\
\
if ( BOUNDS( I, CUR.cvtSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
} \
else \
CUR.cvt[I] = TT_MULFIX( args[1], CUR.tt_metrics.scale ); \
}
#define DO_DEBUG \
CUR.error = TT_Err_Debug_OpCode;
#define DO_ROUND \
args[0] = CUR_Func_round( \
args[0], \
CUR.tt_metrics.compensations[CUR.opcode - 0x68] );
#define DO_NROUND \
args[0] = ROUND_None( args[0], \
CUR.tt_metrics.compensations[CUR.opcode - 0x6C] );
#define DO_MAX \
if ( args[1] > args[0] ) \
args[0] = args[1];
#define DO_MIN \
if ( args[1] < args[0] ) \
args[0] = args[1];
#ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH
#undef ARRAY_BOUND_ERROR
#define ARRAY_BOUND_ERROR \
{ \
CUR.error = TT_Err_Invalid_Reference; \
return; \
}
/*************************************************************************/
/* */
/* SVTCA[a]: Set (F and P) Vectors to Coordinate Axis */
/* Opcode range: 0x00-0x01 */
/* Stack: --> */
/* */
static void
Ins_SVTCA( INS_ARG )
{
DO_SVTCA
}
/*************************************************************************/
/* */
/* SPVTCA[a]: Set PVector to Coordinate Axis */
/* Opcode range: 0x02-0x03 */
/* Stack: --> */
/* */
static void
Ins_SPVTCA( INS_ARG )
{
DO_SPVTCA
}
/*************************************************************************/
/* */
/* SFVTCA[a]: Set FVector to Coordinate Axis */
/* Opcode range: 0x04-0x05 */
/* Stack: --> */
/* */
static void
Ins_SFVTCA( INS_ARG )
{
DO_SFVTCA
}
/*************************************************************************/
/* */
/* SPVTL[a]: Set PVector To Line */
/* Opcode range: 0x06-0x07 */
/* Stack: uint32 uint32 --> */
/* */
static void
Ins_SPVTL( INS_ARG )
{
DO_SPVTL
}
/*************************************************************************/
/* */
/* SFVTL[a]: Set FVector To Line */
/* Opcode range: 0x08-0x09 */
/* Stack: uint32 uint32 --> */
/* */
static void
Ins_SFVTL( INS_ARG )
{
DO_SFVTL
}
/*************************************************************************/
/* */
/* SFVTPV[]: Set FVector To PVector */
/* Opcode range: 0x0E */
/* Stack: --> */
/* */
static void
Ins_SFVTPV( INS_ARG )
{
DO_SFVTPV
}
/*************************************************************************/
/* */
/* SPVFS[]: Set PVector From Stack */
/* Opcode range: 0x0A */
/* Stack: f2.14 f2.14 --> */
/* */
static void
Ins_SPVFS( INS_ARG )
{
DO_SPVFS
}
/*************************************************************************/
/* */
/* SFVFS[]: Set FVector From Stack */
/* Opcode range: 0x0B */
/* Stack: f2.14 f2.14 --> */
/* */
static void
Ins_SFVFS( INS_ARG )
{
DO_SFVFS
}
/*************************************************************************/
/* */
/* GPV[]: Get Projection Vector */
/* Opcode range: 0x0C */
/* Stack: ef2.14 --> ef2.14 */
/* */
static void
Ins_GPV( INS_ARG )
{
DO_GPV
}
/*************************************************************************/
/* GFV[]: Get Freedom Vector */
/* Opcode range: 0x0D */
/* Stack: ef2.14 --> ef2.14 */
/* */
static void
Ins_GFV( INS_ARG )
{
DO_GFV
}
/*************************************************************************/
/* */
/* SRP0[]: Set Reference Point 0 */
/* Opcode range: 0x10 */
/* Stack: uint32 --> */
/* */
static void
Ins_SRP0( INS_ARG )
{
DO_SRP0
}
/*************************************************************************/
/* */
/* SRP1[]: Set Reference Point 1 */
/* Opcode range: 0x11 */
/* Stack: uint32 --> */
/* */
static void
Ins_SRP1( INS_ARG )
{
DO_SRP1
}
/*************************************************************************/
/* */
/* SRP2[]: Set Reference Point 2 */
/* Opcode range: 0x12 */
/* Stack: uint32 --> */
/* */
static void
Ins_SRP2( INS_ARG )
{
DO_SRP2
}
/*************************************************************************/
/* */
/* RTHG[]: Round To Half Grid */
/* Opcode range: 0x19 */
/* Stack: --> */
/* */
static void
Ins_RTHG( INS_ARG )
{
DO_RTHG
}
/*************************************************************************/
/* */
/* RTG[]: Round To Grid */
/* Opcode range: 0x18 */
/* Stack: --> */
/* */
static void
Ins_RTG( INS_ARG )
{
DO_RTG
}
/*************************************************************************/
/* RTDG[]: Round To Double Grid */
/* Opcode range: 0x3D */
/* Stack: --> */
/* */
static void
Ins_RTDG( INS_ARG )
{
DO_RTDG
}
/*************************************************************************/
/* RUTG[]: Round Up To Grid */
/* Opcode range: 0x7C */
/* Stack: --> */
/* */
static void
Ins_RUTG( INS_ARG )
{
DO_RUTG
}
/*************************************************************************/
/* */
/* RDTG[]: Round Down To Grid */
/* Opcode range: 0x7D */
/* Stack: --> */
/* */
static void
Ins_RDTG( INS_ARG )
{
DO_RDTG
}
/*************************************************************************/
/* */
/* ROFF[]: Round OFF */
/* Opcode range: 0x7A */
/* Stack: --> */
/* */
static void
Ins_ROFF( INS_ARG )
{
DO_ROFF
}
/*************************************************************************/
/* */
/* SROUND[]: Super ROUND */
/* Opcode range: 0x76 */
/* Stack: Eint8 --> */
/* */
static void
Ins_SROUND( INS_ARG )
{
DO_SROUND
}
/*************************************************************************/
/* */
/* S45ROUND[]: Super ROUND 45 degrees */
/* Opcode range: 0x77 */
/* Stack: uint32 --> */
/* */
static void