blob: 089f604b50faa17da4749e5ac706aa1cec54069f [file] [log] [blame] [edit]
/***************************************************************************/
/* */
/* ttinterp.c */
/* */
/* TrueType bytecode interpreter (body). */
/* */
/* Copyright 1996-2015 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. */
/* */
/***************************************************************************/
/* Greg Hitchcock from Microsoft has helped a lot in resolving unclear */
/* issues; many thanks! */
#include <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_CALC_H
#include FT_TRIGONOMETRY_H
#include FT_SYSTEM_H
#include FT_TRUETYPE_DRIVER_H
#include "ttinterp.h"
#include "tterrors.h"
#include "ttsubpix.h"
#ifdef TT_USE_BYTECODE_INTERPRETER
/*************************************************************************/
/* */
/* 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
/*************************************************************************/
/* */
/* 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 */
/* limited to a maximum number of opcodes defined below. */
/* */
#define MAX_RUNNABLE_OPCODES 1000000L
#define SUBPIXEL_HINTING \
( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \
TT_INTERPRETER_VERSION_38 )
#define PROJECT( v1, v2 ) \
exc->func_project( exc, (v1)->x - (v2)->x, (v1)->y - (v2)->y )
#define DUALPROJ( v1, v2 ) \
exc->func_dualproj( exc, (v1)->x - (v2)->x, (v1)->y - (v2)->y )
#define FAST_PROJECT( v ) \
exc->func_project( exc, (v)->x, (v)->y )
#define FAST_DUALPROJ( v ) \
exc->func_dualproj( exc, (v)->x, (v)->y )
/*************************************************************************/
/* */
/* Instruction dispatch function, as used by the interpreter. */
/* */
typedef void (*TInstruction_Function)( TT_ExecContext exc,
FT_Long* args );
/*************************************************************************/
/* */
/* Two simple bounds-checking macros. */
/* */
#define BOUNDS( x, n ) ( (FT_UInt)(x) >= (FT_UInt)(n) )
#define BOUNDSL( x, n ) ( (FT_ULong)(x) >= (FT_ULong)(n) )
/*************************************************************************/
/* */
/* This macro computes (a*2^14)/b and complements TT_MulFix14. */
/* */
#define TT_DivFix14( a, b ) FT_DivFix( a, (b) << 2 )
#undef SUCCESS
#define SUCCESS 0
#undef FAILURE
#define FAILURE 1
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
#define GUESS_VECTOR( V ) \
do \
{ \
if ( exc->face->unpatented_hinting ) \
{ \
exc->GS.V.x = (FT_F2Dot14)( exc->GS.both_x_axis ? 0x4000 : 0 ); \
exc->GS.V.y = (FT_F2Dot14)( exc->GS.both_x_axis ? 0 : 0x4000 ); \
} \
} while (0)
#else
#define GUESS_VECTOR( V ) do { } while (0)
#endif
/*************************************************************************/
/* */
/* 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. */
/* */
FT_LOCAL_DEF( void )
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( IP <= coderange->size );
exec->code = coderange->base;
exec->codeSize = coderange->size;
exec->IP = IP;
exec->curRange = range;
}
/*************************************************************************/
/* */
/* <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. */
/* */
FT_LOCAL_DEF( void )
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;
}
/*************************************************************************/
/* */
/* <Function> */
/* TT_Clear_CodeRange */
/* */
/* <Description> */
/* Clears a code range. */
/* */
/* <Input> */
/* range :: The code range index. */
/* */
/* <InOut> */
/* exec :: The target execution context. */
/* */
FT_LOCAL_DEF( void )
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;
}
/*************************************************************************/
/* */
/* EXECUTION CONTEXT ROUTINES */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* TT_Done_Context */
/* */
/* <Description> */
/* Destroys a given context. */
/* */
/* <Input> */
/* exec :: A handle to the target execution context. */
/* */
/* memory :: A handle to the parent memory object. */
/* */
/* <Note> */
/* Only the glyph loader and debugger should call this function. */
/* */
FT_LOCAL_DEF( void )
TT_Done_Context( TT_ExecContext exec )
{
FT_Memory memory = exec->memory;
/* points zone */
exec->maxPoints = 0;
exec->maxContours = 0;
/* free stack */
FT_FREE( exec->stack );
exec->stackSize = 0;
/* free call stack */
FT_FREE( exec->callStack );
exec->callSize = 0;
exec->callTop = 0;
/* free glyph code range */
FT_FREE( exec->glyphIns );
exec->glyphSize = 0;
exec->size = NULL;
exec->face = NULL;
FT_FREE( exec );
}
/*************************************************************************/
/* */
/* <Function> */
/* Init_Context */
/* */
/* <Description> */
/* Initializes a context object. */
/* */
/* <Input> */
/* memory :: A handle to the parent memory 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,
FT_Memory memory )
{
FT_Error error;
FT_TRACE1(( "Init_Context: new object at 0x%08p\n", exec ));
exec->memory = memory;
exec->callSize = 32;
if ( FT_NEW_ARRAY( exec->callStack, exec->callSize ) )
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->glyphSize = 0;
exec->stack = NULL;
exec->glyphIns = NULL;
exec->face = NULL;
exec->size = NULL;
return FT_Err_Ok;
Fail_Memory:
FT_ERROR(( "Init_Context: not enough memory for %p\n", exec ));
TT_Done_Context( exec );
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. */
/* */
FT_LOCAL_DEF( FT_Error )
Update_Max( FT_Memory memory,
FT_ULong* size,
FT_ULong multiplier,
void* _pbuff,
FT_ULong new_max )
{
FT_Error error;
void** pbuff = (void**)_pbuff;
if ( *size < new_max )
{
if ( FT_REALLOC( *pbuff, *size * multiplier, new_max * multiplier ) )
return error;
*size = new_max;
}
return FT_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->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;
/* In case of multi-threading it can happen that the old size object */
/* no longer exists, thus we must clear all glyph zone references. */
ft_memset( &exec->zp0, 0, sizeof ( exec->zp0 ) );
exec->zp1 = exec->zp0;
exec->zp2 = exec->zp0;
}
/* XXX: We reserve a little more elements on the stack to deal safely */
/* with broken fonts like arialbs, courbs, timesbs, etc. */
tmp = (FT_ULong)exec->stackSize;
error = Update_Max( exec->memory,
&tmp,
sizeof ( FT_F26Dot6 ),
(void*)&exec->stack,
maxp->maxStackElements + 32 );
exec->stackSize = (FT_Long)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->zp1 = exec->pts;
exec->zp2 = exec->pts;
exec->zp0 = exec->pts;
exec->instruction_trap = FALSE;
return FT_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. */
/* */
/* <Note> */
/* Only the glyph loader and debugger should call this function. */
/* */
FT_LOCAL_DEF( void )
TT_Save_Context( TT_ExecContext exec,
TT_Size size )
{
FT_Int i;
/* XXX: 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];
}
/*************************************************************************/
/* */
/* <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> */
/* TrueType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
TT_Run_Context( TT_ExecContext exec )
{
TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 );
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;
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
exec->GS.both_x_axis = TRUE;
#endif
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;
return exec->face->interpreter( exec );
}
/* The default value for `scan_control' is documented as FALSE in the */
/* TrueType specification. This is confusing since it implies a */
/* Boolean value. However, this is not the case, thus both the */
/* default values of our `scan_type' and `scan_control' fields (which */
/* the documentation's `scan_control' variable is split into) are */
/* zero. */
const TT_GraphicsState tt_default_graphics_state =
{
0, 0, 0,
{ 0x4000, 0 },
{ 0x4000, 0 },
{ 0x4000, 0 },
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
TRUE,
#endif
1, 64, 1,
TRUE, 68, 0, 0, 9, 3,
0, FALSE, 0, 1, 1, 1
};
/* documentation is in ttinterp.h */
FT_EXPORT_DEF( TT_ExecContext )
TT_New_Context( TT_Driver driver )
{
FT_Memory memory;
FT_Error error;
TT_ExecContext exec = NULL;
if ( !driver )
goto Fail;
memory = driver->root.root.memory;
/* allocate object */
if ( FT_NEW( exec ) )
goto Fail;
/* initialize it; in case of error this deallocates `exec' too */
error = Init_Context( exec, memory );
if ( error )
goto Fail;
return exec;
Fail:
return NULL;
}
/*************************************************************************/
/* */
/* 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. */
/* */
/* Opcodes which have a varying number of parameters in the data stream */
/* (NPUSHB, NPUSHW) are handled specially; they have a negative value in */
/* the `opcode_length' table, and the value in `Pop_Push_Count' is set */
/* to zero. */
/* */
/*************************************************************************/
#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 ),
/* SDPvTL[0] */ PACK( 2, 0 ),
/* SDPvTL[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 )
};
#ifdef FT_DEBUG_LEVEL_TRACE
/* the first hex digit gives the length of the opcode name; the space */
/* after the digit is here just to increase readability of the source */
/* code */
static
const char* const opcode_name[256] =
{
"7 SVTCA y",
"7 SVTCA x",
"8 SPvTCA y",
"8 SPvTCA x",
"8 SFvTCA y",
"8 SFvTCA x",
"8 SPvTL ||",
"7 SPvTL +",
"8 SFvTL ||",
"7 SFvTL +",
"5 SPvFS",
"5 SFvFS",
"3 GPv",
"3 GFv",
"6 SFvTPv",
"5 ISECT",
"4 SRP0",
"4 SRP1",
"4 SRP2",
"4 SZP0",
"4 SZP1",
"4 SZP2",
"4 SZPS",
"5 SLOOP",
"3 RTG",
"4 RTHG",
"3 SMD",
"4 ELSE",
"4 JMPR",
"6 SCvTCi",
"5 SSwCi",
"3 SSW",
"3 DUP",
"3 POP",
"5 CLEAR",
"4 SWAP",
"5 DEPTH",
"6 CINDEX",
"6 MINDEX",
"8 AlignPTS",
"7 INS_$28",
"3 UTP",
"8 LOOPCALL",
"4 CALL",
"4 FDEF",
"4 ENDF",
"7 MDAP[0]",
"7 MDAP[1]",
"6 IUP[0]",
"6 IUP[1]",
"6 SHP[0]",
"6 SHP[1]",
"6 SHC[0]",
"6 SHC[1]",
"6 SHZ[0]",
"6 SHZ[1]",
"5 SHPIX",
"2 IP",
"8 MSIRP[0]",
"8 MSIRP[1]",
"7 AlignRP",
"4 RTDG",
"7 MIAP[0]",
"7 MIAP[1]",
"6 NPushB",
"6 NPushW",
"2 WS",
"2 RS",
"5 WCvtP",
"4 RCvt",
"5 GC[0]",
"5 GC[1]",
"4 SCFS",
"5 MD[0]",
"5 MD[1]",
"5 MPPEM",
"3 MPS",
"6 FlipON",
"7 FlipOFF",
"5 DEBUG",
"2 LT",
"4 LTEQ",
"2 GT",
"4 GTEQ",
"2 EQ",
"3 NEQ",
"3 ODD",
"4 EVEN",
"2 IF",
"3 EIF",
"3 AND",
"2 OR",
"3 NOT",
"7 DeltaP1",
"3 SDB",
"3 SDS",
"3 ADD",
"3 SUB",
"3 DIV",
"3 MUL",
"3 ABS",
"3 NEG",
"5 FLOOR",
"7 CEILING",
"8 ROUND[0]",
"8 ROUND[1]",
"8 ROUND[2]",
"8 ROUND[3]",
"9 NROUND[0]",
"9 NROUND[1]",
"9 NROUND[2]",
"9 NROUND[3]",
"5 WCvtF",
"7 DeltaP2",
"7 DeltaP3",
"A DeltaCn[0]",
"A DeltaCn[1]",
"A DeltaCn[2]",
"6 SROUND",
"8 S45Round",
"4 JROT",
"4 JROF",
"4 ROFF",
"7 INS_$7B",
"4 RUTG",
"4 RDTG",
"5 SANGW",
"2 AA",
"6 FlipPT",
"8 FlipRgON",
"9 FlipRgOFF",
"7 INS_$83",
"7 INS_$84",
"8 ScanCTRL",
"9 SDPvTL[0]",
"9 SDPvTL[1]",
"7 GetINFO",
"4 IDEF",
"4 ROLL",
"3 MAX",
"3 MIN",
"8 ScanTYPE",
"8 InstCTRL",
"7 INS_$8F",
"7 INS_$90",
"7 INS_$91",
"7 INS_$92",
"7 INS_$93",
"7 INS_$94",
"7 INS_$95",
"7 INS_$96",
"7 INS_$97",
"7 INS_$98",
"7 INS_$99",
"7 INS_$9A",
"7 INS_$9B",
"7 INS_$9C",
"7 INS_$9D",
"7 INS_$9E",
"7 INS_$9F",
"7 INS_$A0",
"7 INS_$A1",
"7 INS_$A2",
"7 INS_$A3",
"7 INS_$A4",
"7 INS_$A5",
"7 INS_$A6",
"7 INS_$A7",
"7 INS_$A8",
"7 INS_$A9",
"7 INS_$AA",
"7 INS_$AB",
"7 INS_$AC",
"7 INS_$AD",
"7 INS_$AE",
"7 INS_$AF",
"8 PushB[0]",
"8 PushB[1]",
"8 PushB[2]",
"8 PushB[3]",
"8 PushB[4]",
"8 PushB[5]",
"8 PushB[6]",
"8 PushB[7]",
"8 PushW[0]",
"8 PushW[1]",
"8 PushW[2]",
"8 PushW[3]",
"8 PushW[4]",
"8 PushW[5]",
"8 PushW[6]",
"8 PushW[7]",
"8 MDRP[00]",
"8 MDRP[01]",
"8 MDRP[02]",
"8 MDRP[03]",
"8 MDRP[04]",
"8 MDRP[05]",
"8 MDRP[06]",
"8 MDRP[07]",
"8 MDRP[08]",
"8 MDRP[09]",
"8 MDRP[10]",
"8 MDRP[11]",
"8 MDRP[12]",
"8 MDRP[13]",
"8 MDRP[14]",
"8 MDRP[15]",
"8 MDRP[16]",
"8 MDRP[17]",
"8 MDRP[18]",
"8 MDRP[19]",
"8 MDRP[20]",
"8 MDRP[21]",
"8 MDRP[22]",
"8 MDRP[23]",
"8 MDRP[24]",
"8 MDRP[25]",
"8 MDRP[26]",
"8 MDRP[27]",
"8 MDRP[28]",
"8 MDRP[29]",
"8 MDRP[30]",
"8 MDRP[31]",
"8 MIRP[00]",
"8 MIRP[01]",
"8 MIRP[02]",
"8 MIRP[03]",
"8 MIRP[04]",
"8 MIRP[05]",
"8 MIRP[06]",
"8 MIRP[07]",
"8 MIRP[08]",
"8 MIRP[09]",
"8 MIRP[10]",
"8 MIRP[11]",
"8 MIRP[12]",
"8 MIRP[13]",
"8 MIRP[14]",
"8 MIRP[15]",
"8 MIRP[16]",
"8 MIRP[17]",
"8 MIRP[18]",
"8 MIRP[19]",
"8 MIRP[20]",
"8 MIRP[21]",
"8 MIRP[22]",
"8 MIRP[23]",
"8 MIRP[24]",
"8 MIRP[25]",
"8 MIRP[26]",
"8 MIRP[27]",
"8 MIRP[28]",
"8 MIRP[29]",
"8 MIRP[30]",
"8 MIRP[31]"
};
#endif /* FT_DEBUG_LEVEL_TRACE */
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,-2, 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
};
#undef PACK
#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER
#if defined( __arm__ ) && \
( defined( __thumb2__ ) || !defined( __thumb__ ) )
#define TT_MulFix14 TT_MulFix14_arm
static FT_Int32
TT_MulFix14_arm( FT_Int32 a,
FT_Int b )
{
FT_Int32 t, t2;
#if defined( __CC_ARM ) || defined( __ARMCC__ )
__asm
{
smull t2, t, b, a /* (lo=t2,hi=t) = a*b */
mov a, t, asr #31 /* a = (hi >> 31) */
add a, a, #0x2000 /* a += 0x2000 */
adds t2, t2, a /* t2 += a */
adc t, t, #0 /* t += carry */
mov a, t2, lsr #14 /* a = t2 >> 14 */
orr a, a, t, lsl #18 /* a |= t << 18 */
}
#elif defined( __GNUC__ )
__asm__ __volatile__ (
"smull %1, %2, %4, %3\n\t" /* (lo=%1,hi=%2) = a*b */
"mov %0, %2, asr #31\n\t" /* %0 = (hi >> 31) */
#if defined( __clang__ ) && defined( __thumb2__ )
"add.w %0, %0, #0x2000\n\t" /* %0 += 0x2000 */
#else
"add %0, %0, #0x2000\n\t" /* %0 += 0x2000 */
#endif
"adds %1, %1, %0\n\t" /* %1 += %0 */
"adc %2, %2, #0\n\t" /* %2 += carry */
"mov %0, %1, lsr #14\n\t" /* %0 = %1 >> 16 */
"orr %0, %0, %2, lsl #18\n\t" /* %0 |= %2 << 16 */
: "=r"(a), "=&r"(t2), "=&r"(t)
: "r"(a), "r"(b)
: "cc" );
#endif
return a;
}
#endif /* __arm__ && ( __thumb2__ || !__thumb__ ) */
#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */
#if defined( __GNUC__ ) && \
( defined( __i386__ ) || defined( __x86_64__ ) )
#define TT_MulFix14 TT_MulFix14_long_long
/* Temporarily disable the warning that C90 doesn't support `long long'. */
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wlong-long"
/* This is declared `noinline' because inlining the function results */
/* in slower code. The `pure' attribute indicates that the result */
/* only depends on the parameters. */
static __attribute__(( noinline ))
__attribute__(( pure )) FT_Int32
TT_MulFix14_long_long( FT_Int32 a,
FT_Int b )
{
long long ret = (long long)a * b;
/* The following line assumes that right shifting of signed values */
/* will actually preserve the sign bit. The exact behaviour is */
/* undefined, but this is true on x86 and x86_64. */
long long tmp = ret >> 63;
ret += 0x2000 + tmp;
return (FT_Int32)( ret >> 14 );
}
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
#pragma GCC diagnostic pop
#endif
#endif /* __GNUC__ && ( __i386__ || __x86_64__ ) */
#ifndef TT_MulFix14
/* Compute (a*b)/2^14 with maximum accuracy and rounding. */
/* This is optimized to be faster than calling FT_MulFix() */
/* for platforms where sizeof(int) == 2. */
static FT_Int32
TT_MulFix14( FT_Int32 a,
FT_Int b )
{
FT_Int32 sign;
FT_UInt32 ah, al, mid, lo, hi;
sign = a ^ b;
if ( a < 0 )
a = -a;
if ( b < 0 )
b = -b;
ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU );
al = (FT_UInt32)( a & 0xFFFFU );
lo = al * b;
mid = ah * b;
hi = mid >> 16;
mid = ( mid << 16 ) + ( 1 << 13 ); /* rounding */
lo += mid;
if ( lo < mid )
hi += 1;
mid = ( lo >> 14 ) | ( hi << 18 );
return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid;
}
#endif /* !TT_MulFix14 */
#if defined( __GNUC__ ) && \
( defined( __i386__ ) || \
defined( __x86_64__ ) || \
defined( __arm__ ) )
#define TT_DotFix14 TT_DotFix14_long_long
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wlong-long"
static __attribute__(( pure )) FT_Int32
TT_DotFix14_long_long( FT_Int32 ax,
FT_Int32 ay,
FT_Int bx,
FT_Int by )
{
/* Temporarily disable the warning that C90 doesn't support */
/* `long long'. */
long long temp1 = (long long)ax * bx;
long long temp2 = (long long)ay * by;
temp1 += temp2;
temp2 = temp1 >> 63;
temp1 += 0x2000 + temp2;
return (FT_Int32)( temp1 >> 14 );
}
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
#pragma GCC diagnostic pop
#endif
#endif /* __GNUC__ && (__arm__ || __i386__ || __x86_64__) */
#ifndef TT_DotFix14
/* compute (ax*bx+ay*by)/2^14 with maximum accuracy and rounding */
static FT_Int32
TT_DotFix14( FT_Int32 ax,
FT_Int32 ay,
FT_Int bx,
FT_Int by )
{
FT_Int32 m, s, hi1, hi2, hi;
FT_UInt32 l, lo1, lo2, lo;
/* compute ax*bx as 64-bit value */
l = (FT_UInt32)( ( ax & 0xFFFFU ) * bx );
m = ( ax >> 16 ) * bx;
lo1 = l + ( (FT_UInt32)m << 16 );
hi1 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo1 < l );
/* compute ay*by as 64-bit value */
l = (FT_UInt32)( ( ay & 0xFFFFU ) * by );
m = ( ay >> 16 ) * by;
lo2 = l + ( (FT_UInt32)m << 16 );
hi2 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo2 < l );
/* add them */
lo = lo1 + lo2;
hi = hi1 + hi2 + ( lo < lo1 );
/* divide the result by 2^14 with rounding */
s = hi >> 31;
l = lo + (FT_UInt32)s;
hi += s + ( l < lo );
lo = l;
l = lo + 0x2000U;
hi += ( l < lo );
return (FT_Int32)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) );
}
#endif /* TT_DotFix14 */
/*************************************************************************/
/* */
/* <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( TT_ExecContext exc )
{
if ( !exc->tt_metrics.ratio )
{
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
if ( exc->face->unpatented_hinting )
{
if ( exc->GS.both_x_axis )
exc->tt_metrics.ratio = exc->tt_metrics.x_ratio;
else
exc->tt_metrics.ratio = exc->tt_metrics.y_ratio;
}
else
#endif
{
if ( exc->GS.projVector.y == 0 )
exc->tt_metrics.ratio = exc->tt_metrics.x_ratio;
else if ( exc->GS.projVector.x == 0 )
exc->tt_metrics.ratio = exc->tt_metrics.y_ratio;
else
{
FT_F26Dot6 x, y;
x = TT_MulFix14( exc->tt_metrics.x_ratio,
exc->GS.projVector.x );
y = TT_MulFix14( exc->tt_metrics.y_ratio,
exc->GS.projVector.y );
exc->tt_metrics.ratio = FT_Hypot( x, y );
}
}
}
return exc->tt_metrics.ratio;
}
FT_CALLBACK_DEF( FT_Long )
Current_Ppem( TT_ExecContext exc )
{
return exc->tt_metrics.ppem;
}
FT_CALLBACK_DEF( FT_Long )
Current_Ppem_Stretched( TT_ExecContext exc )
{
return FT_MulFix( exc->tt_metrics.ppem, Current_Ratio( exc ) );
}
/*************************************************************************/
/* */
/* Functions related to the control value table (CVT). */
/* */
/*************************************************************************/
FT_CALLBACK_DEF( FT_F26Dot6 )
Read_CVT( TT_ExecContext exc,
FT_ULong idx )
{
return exc->cvt[idx];
}
FT_CALLBACK_DEF( FT_F26Dot6 )
Read_CVT_Stretched( TT_ExecContext exc,
FT_ULong idx )
{
return FT_MulFix( exc->cvt[idx], Current_Ratio( exc ) );
}
FT_CALLBACK_DEF( void )
Write_CVT( TT_ExecContext exc,
FT_ULong idx,
FT_F26Dot6 value )
{
exc->cvt[idx] = value;
}
FT_CALLBACK_DEF( void )
Write_CVT_Stretched( TT_ExecContext exc,
FT_ULong idx,
FT_F26Dot6 value )
{
exc->cvt[idx] = FT_DivFix( value, Current_Ratio( exc ) );
}
FT_CALLBACK_DEF( void )
Move_CVT( TT_ExecContext exc,
FT_ULong idx,
FT_F26Dot6 value )
{
exc->cvt[idx] += value;
}
FT_CALLBACK_DEF( void )
Move_CVT_Stretched( TT_ExecContext exc,
FT_ULong idx,
FT_F26Dot6 value )
{
exc->cvt[idx] += FT_DivFix( value, Current_Ratio( exc ) );
}
/*************************************************************************/
/* */
/* <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( TT_ExecContext exc )
{
/* Reading a byte stream so there is no endianess (DaveP) */
exc->IP += 2;
return (FT_Short)( ( exc->code[exc->IP - 2] << 8 ) +
exc->code[exc->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( TT_ExecContext exc,
FT_Int aRange,
FT_Long aIP )
{
TT_CodeRange* range;
if ( aRange < 1 || aRange > 3 )
{
exc->error = FT_THROW( Bad_Argument );
return FAILURE;
}
range = &exc->codeRangeTable[aRange - 1];
if ( range->base == NULL ) /* invalid coderange */
{
exc->error = FT_THROW( 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 )
{
exc->error = FT_THROW( Code_Overflow );
return FAILURE;
}
exc->code = range->base;
exc->codeSize = range->size;
exc->IP = aIP;
exc->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( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_F26Dot6 v;
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
FT_ASSERT( !exc->face->unpatented_hinting );
#endif
v = exc->GS.freeVector.x;
if ( v != 0 )
{
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( !SUBPIXEL_HINTING ||
( !exc->ignore_x_mode ||
( exc->sph_tweak_flags & SPH_TWEAK_ALLOW_X_DMOVE ) ) )
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
zone->cur[point].x += FT_MulDiv( distance, v, exc->F_dot_P );
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
}
v = exc->GS.freeVector.y;
if ( v != 0 )
{
zone->cur[point].y += FT_MulDiv( distance, v, exc->F_dot_P );
zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
}
}
/*************************************************************************/
/* */
/* <Function> */
/* Direct_Move_Orig */
/* */
/* <Description> */
/* Moves the *original* position of a point by a given distance along */
/* the freedom vector. Obviously, the point will not 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_Orig( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_F26Dot6 v;
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
FT_ASSERT( !exc->face->unpatented_hinting );
#endif
v = exc->GS.freeVector.x;
if ( v != 0 )
zone->org[point].x += FT_MulDiv( distance, v, exc->F_dot_P );
v = exc->GS.freeVector.y;
if ( v != 0 )
zone->org[point].y += FT_MulDiv( distance, v, exc->F_dot_P );
}
/*************************************************************************/
/* */
/* 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( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_UNUSED( exc );
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
if ( !SUBPIXEL_HINTING ||
!exc->ignore_x_mode )
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */
zone->cur[point].x += distance;
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
}
static void
Direct_Move_Y( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_UNUSED( exc );
zone->cur[point].y += distance;
zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
}
/*************************************************************************/
/* */
/* Special versions of Direct_Move_Orig() */
/* */
/* 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_Orig_X( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_UNUSED( exc );
zone->org[point].x += distance;
}
static void
Direct_Move_Orig_Y( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_UNUSED( exc );
zone->org[point].y += distance;
}
/*************************************************************************/
/* */
/* <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( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED( exc );
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( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED( exc );
if ( distance >= 0 )
{
val = FT_PIX_ROUND( distance + compensation );
if ( val < 0 )
val = 0;
}
else
{
val = -FT_PIX_ROUND( compensation - distance );
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( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED( exc );
if ( distance >= 0 )
{
val = FT_PIX_FLOOR( distance + compensation ) + 32;
if ( val < 0 )
val = 32;
}
else
{
val = -( FT_PIX_FLOOR( compensation - distance ) + 32 );
if ( val > 0 )
val = -32;
}
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( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED( exc );
if ( distance >= 0 )
{
val = FT_PIX_FLOOR( distance + compensation );
if ( val < 0 )
val = 0;
}
else
{
val = -FT_PIX_FLOOR( compensation - distance );
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( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED( exc );
if ( distance >= 0 )
{
val = FT_PIX_CEIL( distance + compensation );
if ( val < 0 )
val = 0;
}
else
{
val = -FT_PIX_CEIL( compensation - distance );
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( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
FT_UNUSED( exc );
if ( distance >= 0 )
{
val = FT_PAD_ROUND( distance + compensation, 32 );
if ( val < 0 )
val = 0;
}
else
{
val = -FT_PAD_ROUND( compensation - distance, 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( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
if ( distance >= 0 )
{
val = ( distance - exc->phase + exc->threshold + compensation ) &
-exc->period;
val += exc->phase;
if ( val < 0 )
val = exc->phase;
}
else
{
val = -( ( exc->threshold - exc->phase - distance + compensation ) &
-exc->period );
val -= exc->phase;
if ( val > 0 )
val = -exc->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( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_F26Dot6 compensation )
{
FT_F26Dot6 val;
if ( distance >= 0 )
{
val = ( ( distance - exc->phase + exc->threshold + compensation ) /
exc->period ) * exc->period;
val += exc->phase;
if ( val < 0 )
val = exc->phase;
}
else
{
val = -( ( ( exc->threshold - exc->phase - distance + compensation ) /
exc->period ) * exc->period );
val -= exc->phase;
if ( val > 0 )
val = -exc->phase;
}
return val;
}
/*************************************************************************/
/* */
/* <Function> */
/* Compute_Round */
/* */
/* <Description> */
/* Sets the rounding mode. */
/* */
/* <Input> */
/* round_mode :: The rounding mode to be used. */
/* */
static void
Compute_Round( TT_ExecContext exc,
FT_Byte round_mode )
{
switch ( round_mode )
{
case TT_Round_Off:
exc->func_round = (TT_Round_Func)Round_None;
break;
case TT_Round_To_Grid:
exc->func_round = (TT_Round_Func)Round_To_Grid;
break;
case TT_Round_Up_To_Grid:
exc->func_round = (TT_Round_Func)Round_Up_To_Grid;
break;
case TT_Round_Down_To_Grid:
exc->func_round = (TT_Round_Func)Round_Down_To_Grid;
break;
case TT_Round_To_Half_Grid:
exc->func_round = (TT_Round_Func)Round_To_Half_Grid;
break;
case TT_Round_To_Double_Grid:
exc->func_round = (TT_Round_Func)Round_To_Double_Grid;
break;
case TT_Round_Super:
exc->func_round = (TT_Round_Func)Round_Super;
break;
case TT_Round_Super_45:
exc->func_round = (TT_Round_Func)Round_Super_45;
break;
}
}
/*************************************************************************/
/* */
/* <Function> */
/* SetSuperRound */
/* */
/* <Description> */
/* Sets Super Round parameters. */
/* */
/* <Input> */
/* GridPeriod :: The grid period. */
/* */
/* selector :: The SROUND opcode. */
/* */
static void
SetSuperRound( TT_ExecContext exc,
FT_F2Dot14 GridPeriod,
FT_Long selector )
{
switch ( (FT_Int)( selector & 0xC0 ) )
{
case 0:
exc->period = GridPeriod / 2;
break;
case 0x40:
exc->period = GridPeriod;
break;
case 0x80:
exc->period = GridPeriod * 2;
break;
/* This opcode is reserved, but... */
case 0xC0:
exc->period = GridPeriod;
break;
}
switch ( (FT_Int)( selector & 0x30 ) )
{
case 0:
exc->phase = 0;
break;
case 0x10:
exc->phase = exc->period / 4;
break;
case 0x20:
exc->phase = exc->period / 2;
break;
case 0x30:
exc->phase = exc->period * 3 / 4;
break;
}
if ( ( selector & 0x0F ) == 0 )
exc->threshold = exc->period - 1;
else
exc->threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * exc->period / 8;
/* convert to F26Dot6 format */
exc->period >>= 8;
exc->phase >>= 8;
exc->threshold >>= 8;
}
/*************************************************************************/
/* */
/* <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( TT_ExecContext exc,
FT_Pos dx,
FT_Pos dy )
{
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
FT_ASSERT( !exc->face->unpatented_hinting );
#endif
return TT_DotFix14( dx, dy,
exc->GS.projVector.x,
exc->GS.projVector.y );
}
/*************************************************************************/
/* */
/* <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( TT_ExecContext exc,
FT_Pos dx,
FT_Pos dy )
{
return TT_DotFix14( dx, dy,
exc->GS.dualVector.x,
exc->GS.dualVector.y );
}
/*************************************************************************/
/* */
/* <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( TT_ExecContext exc,
FT_Pos dx,
FT_Pos dy )
{
FT_UNUSED( exc );
FT_UNUSED( dy );
return dx;
}
/*************************************************************************/
/* */
/* <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( TT_ExecContext exc,
FT_Pos dx,
FT_Pos dy )
{
FT_UNUSED( exc );
FT_UNUSED( dx );
return dy;
}
/*************************************************************************/
/* */
/* <Function> */
/* Compute_Funcs */
/* */
/* <Description> */
/* Computes the projection and movement function pointers according */
/* to the current graphics state. */
/* */
static void
Compute_Funcs( TT_ExecContext exc )
{
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
if ( exc->face->unpatented_hinting )
{
/* If both vectors point rightwards along the x axis, set */
/* `both-x-axis' true, otherwise set it false. The x values only */
/* need be tested because the vector has been normalised to a unit */
/* vector of length 0x4000 = unity. */
exc->GS.both_x_axis = (FT_Bool)( exc->GS.projVector.x == 0x4000 &&
exc->GS.freeVector.x == 0x4000 );
/* Throw away projection and freedom vector information */
/* because the patents don't allow them to be stored. */
/* The relevant US Patents are 5155805 and 5325479. */
exc->GS.projVector.x = 0;
exc->GS.projVector.y = 0;
exc->GS.freeVector.x = 0;
exc->GS.freeVector.y = 0;
if ( exc->GS.both_x_axis )
{
exc->func_project = Project_x;
exc->func_move = Direct_Move_X;
exc->func_move_orig = Direct_Move_Orig_X;
}
else
{
exc->func_project = Project_y;
exc->func_move = Direct_Move_Y;
exc->func_move_orig = Direct_Move_Orig_Y;
}
if ( exc->GS.dualVector.x == 0x4000 )
exc->func_dualproj = Project_x;
else if ( exc->GS.dualVector.y == 0x4000 )
exc->func_dualproj = Project_y;
else
exc->func_dualproj = Dual_Project;
/* Force recalculation of cached aspect ratio */
exc->tt_metrics.ratio = 0;
return;
}
#endif /* TT_CONFIG_OPTION_UNPATENTED_HINTING */
if ( exc->GS.freeVector.x == 0x4000 )
exc->F_dot_P = exc->GS.projVector.x;
else if ( exc->GS.freeVector.y == 0x4000 )
exc->F_dot_P = exc->GS.projVector.y;
else
exc->F_dot_P =
( (FT_Long)exc->GS.projVector.x * exc->GS.freeVector.x +
(FT_Long)exc->GS.projVector.y * exc->GS.freeVector.y ) >> 14;
if ( exc->GS.projVector.x == 0x4000 )
exc->func_project = (TT_Project_Func)Project_x;
else if ( exc->GS.projVector.y == 0x4000 )
exc->func_project = (TT_Project_Func)Project_y;
else
exc->func_project = (TT_Project_Func)Project;
if ( exc->GS.dualVector.x == 0x4000 )
exc->func_dualproj = (TT_Project_Func)Project_x;
else if ( exc->GS.dualVector.y == 0x4000 )
exc->func_dualproj = (TT_Project_Func)Project_y;
else
exc->func_dualproj = (TT_Project_Func)Dual_Project;
exc->func_move = (TT_Move_Func)Direct_Move;
exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig;
if ( exc->F_dot_P == 0x4000L )
{
if ( exc->GS.freeVector.x == 0x4000 )
{
exc->func_move = (TT_Move_Func)Direct_Move_X;
exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_X;
}
else if ( exc->GS.freeVector.y == 0x4000 )
{
exc->func_move = (TT_Move_Func)Direct_Move_Y;
exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y;
}
}
/* at small sizes, F_dot_P can become too small, resulting */
/* in overflows and `spikes' in a number of glyphs like `w'. */
if ( FT_ABS( exc->F_dot_P ) < 0x400L )
exc->F_dot_P = 0x4000L;
/* Disable cached aspect ratio */
exc->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. */
/* */
static FT_Bool
Normalize( FT_F26Dot6 Vx,
FT_F26Dot6 Vy,
FT_UnitVector* R )
{
FT_F26Dot6 W;
if ( FT_ABS( Vx ) < 0x4000L && FT_ABS( Vy ) < 0x4000L )
{
if ( Vx == 0 && Vy == 0 )
{
/* XXX: UNDOCUMENTED! It seems that it is possible to try */
/* to normalize the vector (0,0). Return immediately. */
return SUCCESS;
}
Vx *= 0x4000;
Vy *= 0x4000;
}
W = FT_Hypot( Vx, Vy );
R->x = (FT_F2Dot14)TT_DivFix14( Vx, W );
R->y = (FT_F2Dot14)TT_DivFix14( Vy, W );
return SUCCESS;
}
/*************************************************************************/
/* */
/* Here we start with the implementation of the various opcodes. */
/* */
/*************************************************************************/
#define ARRAY_BOUND_ERROR \
do \
{ \
exc->error = FT_THROW( Invalid_Reference ); \
return; \
} while (0)
/*************************************************************************/
/* */
/* MPPEM[]: Measure Pixel Per EM */
/* Opcode range: 0x4B */
/* Stack: --> Euint16 */
/* */
static void
Ins_MPPEM( TT_ExecContext exc,
FT_Long* args )
{
args[0] = exc->func_cur_ppem( exc );
}
/*************************************************************************/
/* */
/* MPS[]: Measure Point Size */
/* Opcode range: 0x4C */
/* Stack: --> Euint16 */
/* */
static void
Ins_MPS( TT_ExecContext exc,
FT_Long* args )
{
/* Note: The point size should be irrelevant in a given font program; */
/* we thus decide to return only the PPEM value. */
#if 0
args[0] = exc->metrics.pointSize;
#else
args[0] = exc->func_cur_ppem( exc );
#endif
}
/*************************************************************************/
/* */
/* DUP[]: DUPlicate the stack's top element */
/* Opcode range: 0x20 */
/* Stack: StkElt --> StkElt StkElt */
/* */
static void
Ins_DUP( FT_Long* args )
{
args[1] = args[0];
}
/*************************************************************************/
/* */
/* POP[]: POP the stack's top element */
/* Opcode range: 0x21 */
/* Stack: StkElt --> */
/* */
static void
Ins_POP( void )
{
/* nothing to do */
}
/*************************************************************************/
/* */
/* CLEAR[]: CLEAR the entire stack */
/* Opcode range: 0x22 */
/* Stack: StkElt... --> */
/* */
static void
Ins_CLEAR( TT_ExecContext exc )
{
exc->new_top = 0;
}
/*************************************************************************/
/* */
/* SWAP[]: SWAP the stack's top two elements */
/* Opcode range: 0x23 */
/* Stack: 2 * StkElt --> 2 * StkElt */
/* */
static void
Ins_SWAP( FT_Long* args )
{
FT_Long L;
L = args[0];
args[0] = args[1];
args[1] = L;
}
/*************************************************************************/
/* */
/* DEPTH[]: return the stack DEPTH */
/* Opcode range: 0x24 */
/* Stack: --> uint32 */
/* */
static void
Ins_DEPTH( TT_ExecContext exc,
FT_Long* args )
{
args[0] = exc->top;
}
/*************************************************************************/
/* */
/* LT[]: Less Than */
/* Opcode range: 0x50 */
/* Stack: int32? int32? --> bool */
/* */
static void
Ins_LT( FT_Long* args )
{
args[0] = ( args[0] < args[1] );
}
/*************************************************************************/
/* */
/* LTEQ[]: Less Than or EQual */
/* Opcode range: 0x51 */
/* Stack: int32? int32? --> bool */
/* */
static void
Ins_LTEQ( FT_Long* args )
{
args[0] = ( args[0] <= args[1] );
}
/*************************************************************************/
/* */
/* GT[]: Greater Than */
/* Opcode range: 0x52 */
/* Stack: int32? int32? --> bool */
/* */
static void
Ins_GT( FT_Long* args )
{
args[0] = ( args[0] > args[1] );
}
/*************************************************************************/
/* */
/* GTEQ[]: Greater Than or EQual */
/* Opcode range: 0x53 */
/* Stack: int32? int32? --> bool */
/* */
static void
Ins_GTEQ( FT_Long* args )
{
args[0] = ( args[0] >= args[1] );
}
/*************************************************************************/
/* */
/* EQ[]: EQual */
/* Opcode range: 0x54 */
/* Stack: StkElt StkElt --> bool */
/* */
static void
Ins_EQ( FT_Long* args )
{
args[0] = ( args[0] == args[1] );
}
/*************************************************************************/
/* */
/* NEQ[]: Not EQual */
/* Opcode range: 0x55 */
/* Stack: StkElt StkElt --> bool */
/* */
static void
Ins_NEQ( FT_Long* args )
{
args[0] = ( args[0] != args[1] );
}
/*************************************************************************/
/* */
/* ODD[]: Is ODD */
/* Opcode range: 0x56 */
/* Stack: f26.6 --> bool */
/* */
static void
Ins_ODD( TT_ExecContext exc,
FT_Long* args )
{
args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 64 );
}
/*************************************************************************/
/* */
/* EVEN[]: Is EVEN */
/* Opcode range: 0x57 */
/* Stack: f26.6 --> bool */
/* */
static void
Ins_EVEN( TT_ExecContext exc,
FT_Long* args )
{
args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 0 );
}
/*************************************************************************/
/* */
/* AND[]: logical AND */
/* Opcode range: 0x5A */
/* Stack: uint32 uint32 --> uint32 */
/* */
static void
Ins_AND( FT_Long* args )
{
args[0] = ( args[0] && args[1] );
}
/*************************************************************************/
/* */
/* OR[]: logical OR */
/* Opcode range: 0x5B */
/* Stack: uint32 uint32 --> uint32 */
/* */
static void
Ins_OR( FT_Long* args )
{
args[0] = ( args[0] || args[1] );
}
/*************************************************************************/
/* */
/* NOT[]: logical NOT */
/* Opcode range: 0x5C */
/* Stack: StkElt --> uint32 */
/* */
static void
Ins_NOT( FT_Long* args )
{
args[0] = !args[0];
}
/*************************************************************************/
/* */
/* ADD[]: ADD */
/* Opcode range: 0x60 */
/* Stack: f26.6 f26.6 --> f26.6 */
/* */
static void
Ins_ADD( FT_Long* args )
{
args[0] += args[1];
}
/*************************************************************************/
/* */
/* SUB[]: SUBtract */
/* Opcode range: 0x61 */
/* Stack: f26.6 f26.6 --> f26.6 */
/* */
static void
Ins_SUB( FT_Long* args )
{
args[0] -= args[1];
}
/*************************************************************************/
/* */
/* DIV[]: DIVide */
/* Opcode range: 0x62 */
/* Stack: f26.6 f26.6 --> f26.6 */
/* */
static void
Ins_DIV( TT_ExecContext exc,
FT_Long* args )
{
if ( args[1] == 0 )
exc->error = FT_THROW( Divide_By_Zero );
else
args[0] = FT_MulDiv_No_Round( args[0], 64L, args[1] );
}
/*************************************************************************/
/* */
/* MUL[]: MULtiply */
/* Opcode range: 0x63 */
/* Stack: f26.6 f26.6 --> f26.6 */
/* */
static void
Ins_MUL( FT_Long* args )
{
args[0] = FT_MulDiv( args[0], args[1], 64L );
}
/*************************************************************************/
/* */
/* ABS[]: ABSolute value */
/* Opcode range: 0x64 */
/* Stack: f26.6 --> f26.6 */
/* */
static void
Ins_ABS( FT_Long* args )
{
args[0] = FT_ABS( args[0] );
}
/*************************************************************************/
/* */
/* NEG[]: NEGate */
/* Opcode range: 0x65 */
/* Stack: f26.6 --> f26.6 */
/* */
static void
Ins_NEG( FT_Long* args )
{
args[0] = -args[0];
}
/*************************************************************************/
/* */
/* FLOOR[]: FLOOR */
/* Opcode range: 0x66 */
/* Stack: f26.6 --> f26.6 */
/* */
static void
Ins_FLOOR( FT_Long* args )
{
args[0] = FT_PIX_FLOOR( args[0] );
}
/*************************************************************************/
/* */
/* CEILING[]: CEILING */
/* Opcode range: 0x67 */
/* Stack: f26.6 --> f26.6 */
/* */
static void
Ins_CEILING( FT_Long* args )
{
args[0] = FT_PIX_CEIL( args[0] );
}
/*************************************************************************/
/* */
/* RS[]: Read Store */
/* Opcode range: 0x43 */
/* Stack: uint32 --> uint32 */
/* */
static void
Ins_RS( TT_ExecContext exc,
FT_Long* args )
{
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
FT_ULong I = (FT_ULong)args[0];
if ( BOUNDSL( I, exc->storeSize ) )
{
if ( exc->pedantic_hinting )
ARRAY_BOUND_ERROR;
else
args[0] = 0;
}
else
{
/* subpixel hinting - avoid Typeman Dstroke and */
/* IStroke and Vacuform rounds */
if ( SUBPIXEL_HINTING &&
exc->ignore_x_mode &&
( ( I == 24 &&
( exc->face->sph_found_func_flags &
( SPH_FDEF_SPACING_1 |
SPH_FDEF_SPACING_2 ) ) ) ||
( I == 22 &&
( exc->sph_in_func_flags &
SPH_FDEF_TYPEMAN_STROKES ) ) ||
( I == 8 &&
( exc->face->sph_found_func_flags &
SPH_FDEF_VACUFORM_ROUND_1 ) &&
exc->iup_called ) ) )
args[0] = 0;
else
args[0] = exc->storage[I];
}
#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */
FT_ULong I = (FT_ULong)args[0];
if ( BOUNDSL( I, exc->storeSize ) )
{
if ( exc->pedantic_hinting )
ARRAY_BOUND_ERROR;
else
args[0] = 0;
}
else
args[0] = exc->storage[I];
#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */
}
/*************************************************************************/
/* */
/* WS[]: Write Store */
/* Opcode range: 0x42 */
/* Stack: uint32 uint32 --> */
/* */
static void
Ins_WS( TT_ExecContext exc,
FT_Long* args )
{
FT_ULong I = (FT_ULong)args[0];
if ( BOUNDSL( I, exc->storeSize ) )
{
if ( exc->pedantic_hinting )
ARRAY_BOUND_ERROR;
}
else
exc->storage[I] = args[1];
}
/*************************************************************************/
/* */
/* WCVTP[]: Write CVT in Pixel units */
/* Opcode range: 0x44 */
/* Stack: f26.6 uint32 --> */
/* */
static void
Ins_WCVTP( TT_ExecContext exc,
FT_Long* args )
{
FT_ULong I = (FT_ULong)args[0];
if ( BOUNDSL( I, exc->cvtSize ) )
{
if ( exc->pedantic_hinting )
ARRAY_BOUND_ERROR;
}
else
exc->func_write_cvt( exc, I, args[1] );
}
/*************************************************************************/
/* */
/* WCVTF[]: Write CVT in Funits */
/* Opcode range: 0x70 */
/* Stack: uint32 uint32 --> */
/* */
static void
Ins_WCVTF( TT_ExecContext exc,
FT_Long* args )
{
FT_ULong I = (FT_ULong)args[0];
if ( BOUNDSL( I, exc->cvtSize ) )
{
if ( exc->pedantic_hinting )
ARRAY_BOUND_ERROR;
}
else
exc->cvt[I] = FT_MulFix( args[1], exc->tt_metrics.scale );
}
/*************************************************************************/
/* */
/* RCVT[]: Read CVT */
/* Opcode range: 0x45 */
/* Stack: uint32 --> f26.6 */
/* */
static void
Ins_RCVT( TT_ExecContext exc,
FT_Long* args )
{
FT_ULong I = (FT_ULong)args[0];
if ( BOUNDSL(