/***************************************************************************/ /* */ /* ttinterp.c */ /* */ /* TrueType bytecode interpreter (body). */ /* */ /* Copyright 1996-2011 */ /* 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 #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_USE_BYTECODE_INTERPRETER #define TT_MULFIX FT_MulFix #define TT_MULDIV FT_MulDiv #define TT_MULDIV_NO_ROUND FT_MulDiv_No_Round /*************************************************************************/ /* */ /* 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 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 */ /*************************************************************************/ /* */ /* This macro is used whenever `exec' is unused in a function, to avoid */ /* stupid warnings from pedantic compilers. */ /* */ #define FT_UNUSED_EXEC FT_UNUSED( exc ) #else /* static implementation */ #define CUR cur #define FT_UNUSED_EXEC int __dummy = __dummy 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 `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 readability 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_move( z, p, d ) \ CUR.func_move( EXEC_ARG_ z, p, d ) #define CUR_Func_move_orig( z, p, d ) \ CUR.func_move_orig( EXEC_ARG_ z, p, d ) #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 ) #define CUR_Func_project( v1, v2 ) \ CUR.func_project( EXEC_ARG_ (v1)->x - (v2)->x, (v1)->y - (v2)->y ) #define CUR_Func_dualproj( v1, v2 ) \ CUR.func_dualproj( EXEC_ARG_ (v1)->x - (v2)->x, (v1)->y - (v2)->y ) #define CUR_fast_project( v ) \ CUR.func_project( EXEC_ARG_ (v)->x, (v)->y ) #define CUR_fast_dualproj( v ) \ CUR.func_dualproj( EXEC_ARG_ (v)->x, (v)->y ) /*************************************************************************/ /* */ /* Instruction dispatch function, as used by the interpreter. */ /* */ typedef void (*TInstruction_Function)( INS_ARG ); /*************************************************************************/ /* */ /* 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) ) #undef SUCCESS #define SUCCESS 0 #undef FAILURE #define FAILURE 1 #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING #define GUESS_VECTOR( V ) \ if ( CUR.face->unpatented_hinting ) \ { \ CUR.GS.V.x = (FT_F2Dot14)( CUR.GS.both_x_axis ? 0x4000 : 0 ); \ CUR.GS.V.y = (FT_F2Dot14)( CUR.GS.both_x_axis ? 0 : 0x4000 ); \ } #else #define GUESS_VECTOR( V ) #endif /*************************************************************************/ /* */ /* CODERANGE FUNCTIONS */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* */ /* TT_Goto_CodeRange */ /* */ /* */ /* Switches to a new code range (updates the code related elements in */ /* `exec', and `IP'). */ /* */ /* */ /* range :: The new execution code range. */ /* */ /* IP :: The new IP in the new code range. */ /* */ /* */ /* exec :: The target execution context. */ /* */ /* */ /* 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; } /*************************************************************************/ /* */ /* */ /* TT_Set_CodeRange */ /* */ /* */ /* Sets a code range. */ /* */ /* */ /* range :: The code range index. */ /* */ /* base :: The new code base. */ /* */ /* length :: The range size in bytes. */ /* */ /* */ /* exec :: The target execution context. */ /* */ /* */ /* 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; } /*************************************************************************/ /* */ /* */ /* TT_Clear_CodeRange */ /* */ /* */ /* Clears a code range. */ /* */ /* */ /* range :: The code range index. */ /* */ /* */ /* exec :: The target execution context. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ /* */ /* 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 */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* */ /* TT_Done_Context */ /* */ /* */ /* Destroys a given context. */ /* */ /* */ /* exec :: A handle to the target execution context. */ /* */ /* memory :: A handle to the parent memory object. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ /* */ /* Only the glyph loader and debugger should call this function. */ /* */ FT_LOCAL_DEF( FT_Error ) 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 ); return TT_Err_Ok; } /*************************************************************************/ /* */ /* */ /* Init_Context */ /* */ /* */ /* Initializes a context object. */ /* */ /* */ /* memory :: A handle to the parent memory object. */ /* */ /* */ /* exec :: A handle to the target execution context. */ /* */ /* */ /* 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 TT_Err_Ok; Fail_Memory: FT_ERROR(( "Init_Context: not enough memory for %p\n", exec )); TT_Done_Context( exec ); return error; } /*************************************************************************/ /* */ /* */ /* Update_Max */ /* */ /* */ /* Checks the size of a buffer and reallocates it if necessary. */ /* */ /* */ /* 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. */ /* */ /* */ /* size :: The address of the buffer's current size expressed */ /* in elements. */ /* */ /* buff :: The address of the buffer base pointer. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ FT_LOCAL_DEF( FT_Error ) Update_Max( FT_Memory memory, FT_ULong* size, FT_Long 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 TT_Err_Ok; } /*************************************************************************/ /* */ /* */ /* TT_Load_Context */ /* */ /* */ /* Prepare an execution context for glyph hinting. */ /* */ /* */ /* face :: A handle to the source face object. */ /* */ /* size :: A handle to the source size object. */ /* */ /* */ /* exec :: A handle to the target execution context. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ /* */ /* 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 = 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->zp1 = exec->pts; exec->zp2 = exec->pts; exec->zp0 = exec->pts; exec->instruction_trap = FALSE; return TT_Err_Ok; } /*************************************************************************/ /* */ /* */ /* TT_Save_Context */ /* */ /* */ /* Saves the code ranges in a `size' object. */ /* */ /* */ /* exec :: A handle to the source execution context. */ /* */ /* */ /* size :: A handle to the target size object. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ /* */ /* 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; /* 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]; return TT_Err_Ok; } /*************************************************************************/ /* */ /* */ /* TT_Run_Context */ /* */ /* */ /* Executes one or more instructions in the execution context. */ /* */ /* */ /* 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. */ /* */ /* */ /* exec :: A handle to the target execution context. */ /* */ /* */ /* TrueType error code. 0 means success. */ /* */ /* */ /* 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; #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; #if 1 FT_UNUSED( debug ); return exec->face->interpreter( exec ); #else if ( !debug ) return TT_RunIns( exec ); else return TT_Err_Ok; #endif } /* 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 ) { TT_ExecContext exec; FT_Memory memory; memory = driver->root.root.memory; exec = driver->context; if ( !driver->context ) { FT_Error error; /* 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; /* store it into the driver */ driver->context = exec; } return driver->context; 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 static const char* const opcode_name[256] = { "SVTCA y", "SVTCA x", "SPvTCA y", "SPvTCA x", "SFvTCA y", "SFvTCA x", "SPvTL ||", "SPvTL +", "SFvTL ||", "SFvTL +", "SPvFS", "SFvFS", "GPV", "GFV", "SFvTPv", "ISECT", "SRP0", "SRP1", "SRP2", "SZP0", "SZP1", "SZP2", "SZPS", "SLOOP", "RTG", "RTHG", "SMD", "ELSE", "JMPR", "SCvTCi", "SSwCi", "SSW", "DUP", "POP", "CLEAR", "SWAP", "DEPTH", "CINDEX", "MINDEX", "AlignPTS", "INS_$28", "UTP", "LOOPCALL", "CALL", "FDEF", "ENDF", "MDAP[0]", "MDAP[1]", "IUP[0]", "IUP[1]", "SHP[0]", "SHP[1]", "SHC[0]", "SHC[1]", "SHZ[0]", "SHZ[1]", "SHPIX", "IP", "MSIRP[0]", "MSIRP[1]", "AlignRP", "RTDG", "MIAP[0]", "MIAP[1]", "NPushB", "NPushW", "WS", "RS", "WCvtP", "RCvt", "GC[0]", "GC[1]", "SCFS", "MD[0]", "MD[1]", "MPPEM", "MPS", "FlipON", "FlipOFF", "DEBUG", "LT", "LTEQ", "GT", "GTEQ", "EQ", "NEQ", "ODD", "EVEN", "IF", "EIF", "AND", "OR", "NOT", "DeltaP1", "SDB", "SDS", "ADD", "SUB", "DIV", "MUL", "ABS", "NEG", "FLOOR", "CEILING", "ROUND[0]", "ROUND[1]", "ROUND[2]", "ROUND[3]", "NROUND[0]", "NROUND[1]", "NROUND[2]", "NROUND[3]", "WCvtF", "DeltaP2", "DeltaP3", "DeltaCn[0]", "DeltaCn[1]", "DeltaCn[2]", "SROUND", "S45Round", "JROT", "JROF", "ROFF", "INS_$7B", "RUTG", "RDTG", "SANGW", "AA", "FlipPT", "FlipRgON", "FlipRgOFF", "INS_$83", "INS_$84", "ScanCTRL", "SDVPTL[0]", "SDVPTL[1]", "GetINFO", "IDEF", "ROLL", "MAX", "MIN", "ScanTYPE", "InstCTRL", "INS_$8F", "INS_$90", "INS_$91", "INS_$92", "INS_$93", "INS_$94", "INS_$95", "INS_$96", "INS_$97", "INS_$98", "INS_$99", "INS_$9A", "INS_$9B", "INS_$9C", "INS_$9D", "INS_$9E", "INS_$9F", "INS_$A0", "INS_$A1", "INS_$A2", "INS_$A3", "INS_$A4", "INS_$A5", "INS_$A6", "INS_$A7", "INS_$A8", "INS_$A9", "INS_$AA", "INS_$AB", "INS_$AC", "INS_$AD", "INS_$AE", "INS_$AF", "PushB[0]", "PushB[1]", "PushB[2]", "PushB[3]", "PushB[4]", "PushB[5]", "PushB[6]", "PushB[7]", "PushW[0]", "PushW[1]", "PushW[2]", "PushW[3]", "PushW[4]", "PushW[5]", "PushW[6]", "PushW[7]", "MDRP[00]", "MDRP[01]", "MDRP[02]", "MDRP[03]", "MDRP[04]", "MDRP[05]", "MDRP[06]", "MDRP[07]", "MDRP[08]", "MDRP[09]", "MDRP[10]", "MDRP[11]", "MDRP[12]", "MDRP[13]", "MDRP[14]", "MDRP[15]", "MDRP[16]", "MDRP[17]", "MDRP[18]", "MDRP[19]", "MDRP[20]", "MDRP[21]", "MDRP[22]", "MDRP[23]", "MDRP[24]", "MDRP[25]", "MDRP[26]", "MDRP[27]", "MDRP[28]", "MDRP[29]", "MDRP[30]", "MDRP[31]", "MIRP[00]", "MIRP[01]", "MIRP[02]", "MIRP[03]", "MIRP[04]", "MIRP[05]", "MIRP[06]", "MIRP[07]", "MIRP[08]", "MIRP[09]", "MIRP[10]", "MIRP[11]", "MIRP[12]", "MIRP[13]", "MIRP[14]", "MIRP[15]", "MIRP[16]", "MIRP[17]", "MIRP[18]", "MIRP[19]", "MIRP[20]", "MIRP[21]", "MIRP[22]", "MIRP[23]", "MIRP[24]", "MIRP[25]", "MIRP[26]", "MIRP[27]", "MIRP[28]", "MIRP[29]", "MIRP[30]", "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 #if 1 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; } #else /* compute (a*b)/2^14 with maximal accuracy and rounding */ static FT_Int32 TT_MulFix14( FT_Int32 a, FT_Int b ) { FT_Int32 m, s, hi; FT_UInt32 l, lo; /* compute ax*bx as 64-bit value */ l = (FT_UInt32)( ( a & 0xFFFFU ) * b ); m = ( a >> 16 ) * b; lo = l + (FT_UInt32)( m << 16 ); hi = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo < l ); /* 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 ( hi << 18 ) | ( l >> 14 ); } #endif /* compute (ax*bx+ay*by)/2^14 with maximal 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 ( hi << 18 ) | ( l >> 14 ); } /* return length of given vector */ #if 0 static FT_Int32 TT_VecLen( FT_Int32 x, FT_Int32 y ) { FT_Int32 m, hi1, hi2, hi; FT_UInt32 l, lo1, lo2, lo; /* compute x*x as 64-bit value */ lo = (FT_UInt32)( x & 0xFFFFU ); hi = x >> 16; l = lo * lo; m = hi * lo; hi = hi * hi; lo1 = l + (FT_UInt32)( m << 17 ); hi1 = hi + ( m >> 15 ) + ( lo1 < l ); /* compute y*y as 64-bit value */ lo = (FT_UInt32)( y & 0xFFFFU ); hi = y >> 16; l = lo * lo; m = hi * lo; hi = hi * hi; lo2 = l + (FT_UInt32)( m << 17 ); hi2 = hi + ( m >> 15 ) + ( lo2 < l ); /* add them to get 'x*x+y*y' as 64-bit value */ lo = lo1 + lo2; hi = hi1 + hi2 + ( lo < lo1 ); /* compute the square root of this value */ { FT_UInt32 root, rem, test_div; FT_Int count; root = 0; { rem = 0; count = 32; do { rem = ( rem << 2 ) | ( (FT_UInt32)hi >> 30 ); hi = ( hi << 2 ) | ( lo >> 30 ); lo <<= 2; root <<= 1; test_div = ( root << 1 ) + 1; if ( rem >= test_div ) { rem -= test_div; root += 1; } } while ( --count ); } return (FT_Int32)root; } } #else /* this version uses FT_Vector_Length which computes the same value */ /* much, much faster.. */ /* */ static FT_F26Dot6 TT_VecLen( FT_F26Dot6 X, FT_F26Dot6 Y ) { FT_Vector v; v.x = X; v.y = Y; return FT_Vector_Length( &v ); } #endif /*************************************************************************/ /* */ /* */ /* Current_Ratio */ /* */ /* */ /* Returns the current aspect ratio scaling factor depending on the */ /* projection vector's state and device resolutions. */ /* */ /* */ /* The aspect ratio in 16.16 format, always <= 1.0 . */ /* */ static FT_Long Current_Ratio( EXEC_OP ) { if ( !CUR.tt_metrics.ratio ) { #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING if ( CUR.face->unpatented_hinting ) { if ( CUR.GS.both_x_axis ) CUR.tt_metrics.ratio = CUR.tt_metrics.x_ratio; else CUR.tt_metrics.ratio = CUR.tt_metrics.y_ratio; } else #endif { 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 = TT_VecLen( 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 idx ) { return CUR.cvt[idx]; } FT_CALLBACK_DEF( FT_F26Dot6 ) Read_CVT_Stretched( EXEC_OP_ FT_ULong idx ) { return TT_MULFIX( CUR.cvt[idx], CURRENT_Ratio() ); } FT_CALLBACK_DEF( void ) Write_CVT( EXEC_OP_ FT_ULong idx, FT_F26Dot6 value ) { CUR.cvt[idx] = value; } FT_CALLBACK_DEF( void ) Write_CVT_Stretched( EXEC_OP_ FT_ULong idx, FT_F26Dot6 value ) { CUR.cvt[idx] = FT_DivFix( value, CURRENT_Ratio() ); } FT_CALLBACK_DEF( void ) Move_CVT( EXEC_OP_ FT_ULong idx, FT_F26Dot6 value ) { CUR.cvt[idx] += value; } FT_CALLBACK_DEF( void ) Move_CVT_Stretched( EXEC_OP_ FT_ULong idx, FT_F26Dot6 value ) { CUR.cvt[idx] += FT_DivFix( value, CURRENT_Ratio() ); } /*************************************************************************/ /* */ /* */ /* GetShortIns */ /* */ /* */ /* Returns a short integer taken from the instruction stream at */ /* address IP. */ /* */ /* */ /* Short read at code[IP]. */ /* */ /* */ /* 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] ); } /*************************************************************************/ /* */ /* */ /* Ins_Goto_CodeRange */ /* */ /* */ /* Goes to a certain code range in the instruction stream. */ /* */ /* */ /* aRange :: The index of the code range. */ /* */ /* aIP :: The new IP address in the code range. */ /* */ /* */ /* 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; } /*************************************************************************/ /* */ /* */ /* Direct_Move */ /* */ /* */ /* Moves a point by a given distance along the freedom vector. The */ /* point will be `touched'. */ /* */ /* */ /* point :: The index of the point to move. */ /* */ /* distance :: The distance to apply. */ /* */ /* */ /* zone :: The affected glyph zone. */ /* */ static void Direct_Move( EXEC_OP_ TT_GlyphZone zone, FT_UShort point, FT_F26Dot6 distance ) { FT_F26Dot6 v; #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING FT_ASSERT( !CUR.face->unpatented_hinting ); #endif v = CUR.GS.freeVector.x; if ( v != 0 ) { zone->cur[point].x += TT_MULDIV( distance, v * 0x10000L, CUR.F_dot_P ); zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; } v = CUR.GS.freeVector.y; if ( v != 0 ) { zone->cur[point].y += TT_MULDIV( distance, v * 0x10000L, CUR.F_dot_P ); zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; } } /*************************************************************************/ /* */ /* */ /* Direct_Move_Orig */ /* */ /* */ /* Moves the *original* position of a point by a given distance along */ /* the freedom vector. Obviously, the point will not be `touched'. */ /* */ /* */ /* point :: The index of the point to move. */ /* */ /* distance :: The distance to apply. */ /* */ /* */ /* zone :: The affected glyph zone. */ /* */ static void Direct_Move_Orig( EXEC_OP_ TT_GlyphZone zone, FT_UShort point, FT_F26Dot6 distance ) { FT_F26Dot6 v; #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING FT_ASSERT( !CUR.face->unpatented_hinting ); #endif v = CUR.GS.freeVector.x; if ( v != 0 ) zone->org[point].x += TT_MULDIV( distance, v * 0x10000L, CUR.F_dot_P ); v = CUR.GS.freeVector.y; if ( v != 0 ) zone->org[point].y += TT_MULDIV( distance, v * 0x10000L, CUR.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( 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; } /*************************************************************************/ /* */ /* 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( EXEC_OP_ TT_GlyphZone zone, FT_UShort point, FT_F26Dot6 distance ) { FT_UNUSED_EXEC; zone->org[point].x += distance; } static void Direct_Move_Orig_Y( EXEC_OP_ TT_GlyphZone zone, FT_UShort point, FT_F26Dot6 distance ) { FT_UNUSED_EXEC; zone->org[point].y += distance; } /*************************************************************************/ /* */ /* */ /* Round_None */ /* */ /* */ /* Does not round, but adds engine compensation. */ /* */ /* */ /* distance :: The distance (not) to round. */ /* */ /* compensation :: The engine compensation. */ /* */ /* */ /* The compensated distance. */ /* */ /* */ /* 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 ( distance && val < 0 ) val = 0; } else { val = distance - compensation; if ( val > 0 ) val = 0; } return val; } /*************************************************************************/ /* */ /* */ /* Round_To_Grid */ /* */ /* */ /* Rounds value to grid after adding engine compensation. */ /* */ /* */ /* distance :: The distance to round. */ /* */ /* compensation :: The engine compensation. */ /* */ /* */ /* 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 ( distance && val > 0 ) val &= ~63; else val = 0; } else { val = -FT_PIX_ROUND( compensation - distance ); if ( val > 0 ) val = 0; } return val; } /*************************************************************************/ /* */ /* */ /* Round_To_Half_Grid */ /* */ /* */ /* Rounds value to half grid after adding engine compensation. */ /* */ /* */ /* distance :: The distance to round. */ /* */ /* compensation :: The engine compensation. */ /* */ /* */ /* 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 = FT_PIX_FLOOR( distance + compensation ) + 32; if ( distance && val < 0 ) val = 0; } else { val = -( FT_PIX_FLOOR( compensation - distance ) + 32 ); if ( val > 0 ) val = 0; } return val; } /*************************************************************************/ /* */ /* */ /* Round_Down_To_Grid */ /* */ /* */ /* Rounds value down to grid after adding engine compensation. */ /* */ /* */ /* distance :: The distance to round. */ /* */ /* compensation :: The engine compensation. */ /* */ /* */ /* 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 ( distance && val > 0 ) val &= ~63; else val = 0; } else { val = -( ( compensation - distance ) & -64 ); if ( val > 0 ) val = 0; } return val; } /*************************************************************************/ /* */ /* */ /* Round_Up_To_Grid */ /* */ /* */ /* Rounds value up to grid after adding engine compensation. */ /* */ /* */ /* distance :: The distance to round. */ /* */ /* compensation :: The engine compensation. */ /* */ /* */ /* 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 ( distance && val > 0 ) val &= ~63; else val = 0; } else { val = - FT_PIX_CEIL( compensation - distance ); if ( val > 0 ) val = 0; } return val; } /*************************************************************************/ /* */ /* */ /* Round_To_Double_Grid */ /* */ /* */ /* Rounds value to double grid after adding engine compensation. */ /* */ /* */ /* distance :: The distance to round. */ /* */ /* compensation :: The engine compensation. */ /* */ /* */ /* 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 ( distance && val > 0 ) val &= ~31; else val = 0; } else { val = -FT_PAD_ROUND( compensation - distance, 32 ); if ( val > 0 ) val = 0; } return val; } /*************************************************************************/ /* */ /* */ /* Round_Super */ /* */ /* */ /* Super-rounds value to grid after adding engine compensation. */ /* */ /* */ /* distance :: The distance to round. */ /* */ /* compensation :: The engine compensation. */ /* */ /* */ /* Rounded distance. */ /* */ /* */ /* 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 ( distance && 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; } /*************************************************************************/ /* */ /* */ /* Round_Super_45 */ /* */ /* */ /* Super-rounds value to grid after adding engine compensation. */ /* */ /* */ /* distance :: The distance to round. */ /* */ /* compensation :: The engine compensation. */ /* */ /* */ /* Rounded distance. */ /* */ /* */ /* 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 ( distance && 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; } /*************************************************************************/ /* */ /* */ /* Compute_Round */ /* */ /* */ /* Sets the rounding mode. */ /* */ /* */ /* 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; } } /*************************************************************************/ /* */ /* */ /* SetSuperRound */ /* */ /* */ /* Sets Super Round parameters. */ /* */ /* */ /* 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 = CUR.period * 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; } /*************************************************************************/ /* */ /* */ /* Project */ /* */ /* */ /* Computes the projection of vector given by (v2-v1) along the */ /* current projection vector. */ /* */ /* */ /* v1 :: First input vector. */ /* v2 :: Second input vector. */ /* */ /* */ /* The distance in F26dot6 format. */ /* */ static FT_F26Dot6 Project( EXEC_OP_ FT_Pos dx, FT_Pos dy ) { #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING FT_ASSERT( !CUR.face->unpatented_hinting ); #endif return TT_DotFix14( (FT_UInt32)dx, (FT_UInt32)dy, CUR.GS.projVector.x, CUR.GS.projVector.y ); } /*************************************************************************/ /* */ /* */ /* Dual_Project */ /* */ /* */ /* Computes the projection of the vector given by (v2-v1) along the */ /* current dual vector. */ /* */ /* */ /* v1 :: First input vector. */ /* v2 :: Second input vector. */ /* */ /* */ /* The distance in F26dot6 format. */ /* */ static FT_F26Dot6 Dual_Project( EXEC_OP_ FT_Pos dx, FT_Pos dy ) { return TT_DotFix14( (FT_UInt32)dx, (FT_UInt32)dy, CUR.GS.dualVector.x, CUR.GS.dualVector.y ); } /*************************************************************************/ /* */ /* */ /* Project_x */ /* */ /* */ /* Computes the projection of the vector given by (v2-v1) along the */ /* horizontal axis. */ /* */ /* */ /* v1 :: First input vector. */ /* v2 :: Second input vector. */ /* */ /* */ /* The distance in F26dot6 format. */ /* */ static FT_F26Dot6 Project_x( EXEC_OP_ FT_Pos dx, FT_Pos dy ) { FT_UNUSED_EXEC; FT_UNUSED( dy ); return dx; } /*************************************************************************/ /* */ /* */ /* Project_y */ /* */ /* */ /* Computes the projection of the vector given by (v2-v1) along the */ /* vertical axis. */ /* */ /* */ /* v1 :: First input vector. */ /* v2 :: Second input vector. */ /* */ /* */ /* The distance in F26dot6 format. */ /* */ static FT_F26Dot6 Project_y( EXEC_OP_ FT_Pos dx, FT_Pos dy ) { FT_UNUSED_EXEC; FT_UNUSED( dx ); return dy; } /*************************************************************************/ /* */ /* */ /* Compute_Funcs */ /* */ /* */ /* Computes the projection and movement function pointers according */ /* to the current graphics state. */ /* */ static void Compute_Funcs( EXEC_OP ) { #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING if ( CUR.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. */ CUR.GS.both_x_axis = (FT_Bool)( CUR.GS.projVector.x == 0x4000 && CUR.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. */ CUR.GS.projVector.x = 0; CUR.GS.projVector.y = 0; CUR.GS.freeVector.x = 0; CUR.GS.freeVector.y = 0; if ( CUR.GS.both_x_axis ) { CUR.func_project = Project_x; CUR.func_move = Direct_Move_X; CUR.func_move_orig = Direct_Move_Orig_X; } else { CUR.func_project = Project_y; CUR.func_move = Direct_Move_Y; CUR.func_move_orig = Direct_Move_Orig_Y; } if ( CUR.GS.dualVector.x == 0x4000 ) CUR.func_dualproj = Project_x; else { if ( CUR.GS.dualVector.y == 0x4000 ) CUR.func_dualproj = Project_y; else CUR.func_dualproj = Dual_Project; } /* Force recalculation of cached aspect ratio */ CUR.tt_metrics.ratio = 0; return; } #endif /* TT_CONFIG_OPTION_UNPATENTED_HINTING */ if ( CUR.GS.freeVector.x == 0x4000 ) CUR.F_dot_P = CUR.GS.projVector.x * 0x10000L; else { if ( CUR.GS.freeVector.y == 0x4000 ) CUR.F_dot_P = CUR.GS.projVector.y * 0x10000L; else 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; CUR.func_move_orig = (TT_Move_Func)Direct_Move_Orig; if ( CUR.F_dot_P == 0x40000000L ) { if ( CUR.GS.freeVector.x == 0x4000 ) { CUR.func_move = (TT_Move_Func)Direct_Move_X; CUR.func_move_orig = (TT_Move_Func)Direct_Move_Orig_X; } else { if ( CUR.GS.freeVector.y == 0x4000 ) { CUR.func_move = (TT_Move_Func)Direct_Move_Y; CUR.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( CUR.F_dot_P ) < 0x4000000L ) CUR.F_dot_P = 0x40000000L; /* Disable cached aspect ratio */ CUR.tt_metrics.ratio = 0; } /*************************************************************************/ /* */ /* */ /* Normalize */ /* */ /* */ /* Norms a vector. */ /* */ /* */ /* Vx :: The horizontal input vector coordinate. */ /* Vy :: The vertical input vector coordinate. */ /* */ /* */ /* R :: The normed unit vector. */ /* */ /* */ /* Returns FAILURE if a vector parameter is zero. */ /* */ /* */ /* In case Vx and Vy are both zero, Normalize() returns SUCCESS, and */ /* R is undefined. */ /* */ 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 ( FT_ABS( Vx ) < 0x10000L && FT_ABS( Vy ) < 0x10000L ) { Vx *= 0x100; Vy *= 0x100; W = TT_VecLen( 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 = TT_VecLen( 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 0x10000000 <= W < 0x10004000 */ if ( Vx < 0 ) { Vx = -Vx; S1 = TRUE; } else S1 = FALSE; if ( Vy < 0 ) { Vy = -Vy; S2 = TRUE; } else S2 = FALSE; while ( W < 0x10000000L ) { /* We need to increase W by a minimal amount */ if ( Vx < Vy ) Vx++; else Vy++; W = Vx * Vx + Vy * Vy; } while ( W >= 0x10004000L ) { /* 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; } /*************************************************************************/ /* */ /* 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 p1 == p2, SPVTL and SFVTL behave the same as */ /* SPVTCA[X] and SFVTCA[X], respectively. */ /* */ /* Confirmed by Greg Hitchcock. */ if ( A == 0 && B == 0 ) { A = 0x4000; aOpc = 0; } 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; \ \ GUESS_VECTOR( freeVector ); \ \ 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; \ \ GUESS_VECTOR( projVector ); \ \ 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; \ GUESS_VECTOR( freeVector ); \ COMPUTE_Funcs(); \ } #define DO_SFVTL \ if ( INS_SxVTL( (FT_UShort)args[1], \ (FT_UShort)args[0], \ CUR.opcode, \ &CUR.GS.freeVector ) == SUCCESS ) \ { \ GUESS_VECTOR( projVector ); \ COMPUTE_Funcs(); \ } #define DO_SFVTPV \ GUESS_VECTOR( projVector ); \ 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; \ GUESS_VECTOR( freeVector ); \ 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 ); \ GUESS_VECTOR( projVector ); \ COMPUTE_Funcs(); \ } #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING #define DO_GPV \ if ( CUR.face->unpatented_hinting ) \ { \ args[0] = CUR.GS.both_x_axis ? 0x4000 : 0; \ args[1] = CUR.GS.both_x_axis ? 0 : 0x4000; \ } \ else \ { \ args[0] = CUR.GS.projVector.x; \ args[1] = CUR.GS.projVector.y; \ } #else #define DO_GPV \ args[0] = CUR.GS.projVector.x; \ args[1] = CUR.GS.projVector.y; #endif #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING #define DO_GFV \ if ( CUR.face->unpatented_hinting ) \ { \ args[0] = CUR.GS.both_x_axis ? 0x4000 : 0; \ args[1] = CUR.GS.both_x_axis ? 0 : 0x4000; \ } \ else \ { \ args[0] = CUR.GS.freeVector.x; \ args[1] = CUR.GS.freeVector.y; \ } #else #define DO_GFV \ args[0] = CUR.GS.freeVector.x; \ args[1] = CUR.GS.freeVector.y; #endif #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 ) \ { \ if ( CUR.pedantic_hinting ) \ CUR.error = TT_Err_Invalid_Reference; \ args[0] = 0; \ } \ else \ args[0] = CUR.stack[CUR.args - L]; \ } #define DO_JROT \ if ( args[1] != 0 ) \ { \ if ( args[0] == 0 && CUR.args == 0 ) \ CUR.error = TT_Err_Bad_Argument; \ CUR.IP += args[0]; \ if ( CUR.IP < 0 ) \ CUR.error = TT_Err_Bad_Argument; \ CUR.step_ins = FALSE; \ } #define DO_JMPR \ if ( args[0] == 0 && CUR.args == 0 ) \ CUR.error = TT_Err_Bad_Argument; \ CUR.IP += args[0]; \ if ( CUR.IP < 0 ) \ CUR.error = TT_Err_Bad_Argument; \ CUR.step_ins = FALSE; #define DO_JROF \ if ( args[1] == 0 ) \ { \ if ( args[0] == 0 && CUR.args == 0 ) \ CUR.error = TT_Err_Bad_Argument; \ CUR.IP += args[0]; \ if ( CUR.IP < 0 ) \ CUR.error = TT_Err_Bad_Argument; \ 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_NO_ROUND( args[0], 64L, args[1] ); #define DO_MUL \ args[0] = TT_MULDIV( args[0], args[1], 64L ); #define DO_ABS \ args[0] = FT_ABS( args[0] ); #define DO_NEG \ args[0] = -args[0]; #define DO_FLOOR \ args[0] = FT_PIX_FLOOR( args[0] ); #define DO_CEILING \ args[0] = FT_PIX_CEIL( args[0] ); #define DO_RS \ { \ FT_ULong I = (FT_ULong)args[0]; \ \ \ if ( BOUNDSL( 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 ( BOUNDSL( 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 ( BOUNDSL( 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 ( BOUNDSL( 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 ( BOUNDSL( 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 Ins_S45ROUND( INS_ARG ) { DO_S45ROUND } /*************************************************************************/ /* */ /* SLOOP[]: Set LOOP variable */ /* Opcode range: 0x17 */ /* Stack: int32? --> */ /* */ static void Ins_SLOOP( INS_ARG ) { DO_SLOOP } /*************************************************************************/ /* */ /* SMD[]: Set Minimum Distance */ /* Opcode range: 0x1A */ /* Stack: f26.6 --> */ /* */ static void Ins_SMD( INS_ARG ) { DO_SMD } /*************************************************************************/ /* */ /* SCVTCI[]: Set Control Value Table Cut In */ /* Opcode range: 0x1D */ /* Stack: f26.6 --> */ /* */ static void Ins_SCVTCI( INS_ARG ) { DO_SCVTCI } /*************************************************************************/ /* */ /* SSWCI[]: Set Single Width Cut In */ /* Opcode range: 0x1E */ /* Stack: f26.6 --> */ /* */ static void Ins_SSWCI( INS_ARG ) { DO_SSWCI } /*************************************************************************/ /* */ /* SSW[]: Set Single Width */ /* Opcode range: 0x1F */ /* Stack: int32? --> */ /* */ static void Ins_SSW( INS_ARG ) { DO_SSW } /*************************************************************************/ /* */ /* FLIPON[]: Set auto-FLIP to ON */ /* Opcode range: 0x4D */ /* Stack: --> */ /* */ static void Ins_FLIPON( INS_ARG ) { DO_FLIPON } /*************************************************************************/ /* */ /* FLIPOFF[]: Set auto-FLIP to OFF */ /* Opcode range: 0x4E */ /* Stack: --> */ /* */ static void Ins_FLIPOFF( INS_ARG ) { DO_FLIPOFF } /*************************************************************************/ /* */ /* SANGW[]: Set ANGle Weight */ /* Opcode range: 0x7E */ /* Stack: uint32 --> */ /* */ static void Ins_SANGW( INS_ARG ) { /* instruction not supported anymore */ } /*************************************************************************/ /* */ /* SDB[]: Set Delta Base */ /* Opcode range: 0x5E */ /* Stack: uint32 --> */ /* */ static void Ins_SDB( INS_ARG ) { DO_SDB } /*************************************************************************/ /* */ /* SDS[]: Set Delta Shift */ /* Opcode range: 0x5F */ /* Stack: uint32 --> */ /* */ static void Ins_SDS( INS_ARG ) { DO_SDS } /*************************************************************************/ /* */ /* MPPEM[]: Measure Pixel Per EM */ /* Opcode range: 0x4B */ /* Stack: --> Euint16 */ /* */ static void Ins_MPPEM( INS_ARG ) { DO_MPPEM } /*************************************************************************/ /* */ /* MPS[]: Measure Point Size */ /* Opcode range: 0x4C */ /* Stack: --> Euint16 */ /* */ static void Ins_MPS( INS_ARG ) { DO_MPS } /*************************************************************************/ /* */ /* DUP[]: DUPlicate the top stack's element */ /* Opcode range: 0x20 */ /* Stack: StkElt --> StkElt StkElt */ /* */ static void Ins_DUP( INS_ARG ) { DO_DUP } /*************************************************************************/ /* */ /* POP[]: POP the stack's top element */ /* Opcode range: 0x21 */ /* Stack: StkElt --> */ /* */ static void Ins_POP( INS_ARG ) { /* nothing to do */ } /*************************************************************************/ /* */ /* CLEAR[]: CLEAR the entire stack */ /* Opcode range: 0x22 */ /* Stack: StkElt... --> */ /* */ static void Ins_CLEAR( INS_ARG ) { DO_CLEAR } /*************************************************************************/ /* */ /* SWAP[]: SWAP the stack's top two elements */ /* Opcode range: 0x23 */ /* Stack: 2 * StkElt --> 2 * StkElt */ /* */ static void Ins_SWAP( INS_ARG ) { DO_SWAP } /*************************************************************************/ /* */ /* DEPTH[]: return the stack DEPTH */ /* Opcode range: 0x24 */ /* Stack: --> uint32 */ /* */ static void Ins_DEPTH( INS_ARG ) { DO_DEPTH } /*************************************************************************/ /* */ /* CINDEX[]: Copy INDEXed element */ /* Opcode range: 0x25 */ /* Stack: int32 --> StkElt */ /* */ static void Ins_CINDEX( INS_ARG ) { DO_CINDEX } /*************************************************************************/ /* */ /* EIF[]: End IF */ /* Opcode range: 0x59 */ /* Stack: --> */ /* */ static void Ins_EIF( INS_ARG ) { /* nothing to do */ } /*************************************************************************/ /* */ /* JROT[]: Jump Relative On True */ /* Opcode range: 0x78 */ /* Stack: StkElt int32 --> */ /* */ static void Ins_JROT( INS_ARG ) { DO_JROT } /*************************************************************************/ /* */ /* JMPR[]: JuMP Relative */ /* Opcode range: 0x1C */ /* Stack: int32 --> */ /* */ static void Ins_JMPR( INS_ARG ) { DO_JMPR } /*************************************************************************/ /* */ /* JROF[]: Jump Relative On False */ /* Opcode range: 0x79 */ /* Stack: StkElt int32 --> */ /* */ static void Ins_JROF( INS_ARG ) { DO_JROF } /*************************************************************************/ /* */ /* LT[]: Less Than */ /* Opcode range: 0x50 */ /* Stack: int32? int32? --> bool */ /* */ static void Ins_LT( INS_ARG ) { DO_LT } /*************************************************************************/ /* */ /* LTEQ[]: Less Than or EQual */ /* Opcode range: 0x51 */ /* Stack: int32? int32? --> bool */ /* */ static void Ins_LTEQ( INS_ARG ) { DO_LTEQ } /*************************************************************************/ /* */ /* GT[]: Greater Than */ /* Opcode range: 0x52 */ /* Stack: int32? int32? --> bool */ /* */ static void Ins_GT( INS_ARG ) { DO_GT } /*************************************************************************/ /* */ /* GTEQ[]: Greater Than or EQual */ /* Opcode range: 0x53 */ /* Stack: int32? int32? --> bool */ /* */ static void Ins_GTEQ( INS_ARG ) { DO_GTEQ } /*************************************************************************/ /* */ /* EQ[]: EQual */ /* Opcode range: 0x54 */ /* Stack: StkElt StkElt --> bool */ /* */ static void Ins_EQ( INS_ARG ) { DO_EQ } /*************************************************************************/ /* */ /* NEQ[]: Not EQual */ /* Opcode range: 0x55 */ /* Stack: StkElt StkElt --> bool */ /* */ static void Ins_NEQ( INS_ARG ) { DO_NEQ } /*************************************************************************/ /* */ /* ODD[]: Is ODD */ /* Opcode range: 0x56 */ /* Stack: f26.6 --> bool */ /* */ static void Ins_ODD( INS_ARG ) { DO_ODD } /*************************************************************************/ /* */ /* EVEN[]: Is EVEN */ /* Opcode range: 0x57 */ /* Stack: f26.6 --> bool */ /* */ static void Ins_EVEN( INS_ARG ) { DO_EVEN } /*************************************************************************/ /* */ /* AND[]: logical AND */ /* Opcode range: 0x5A */ /* Stack: uint32 uint32 --> uint32 */ /* */ static void Ins_AND( INS_ARG ) { DO_AND } /*************************************************************************/ /* */ /* OR[]: logical OR */ /* Opcode range: 0x5B */ /* Stack: uint32 uint32 --> uint32 */ /* */ static void Ins_OR( INS_ARG ) { DO_OR } /*************************************************************************/ /* */ /* NOT[]: logical NOT */ /* Opcode range: 0x5C */ /* Stack: StkElt --> uint32 */ /* */ static void Ins_NOT( INS_ARG ) { DO_NOT } /*************************************************************************/ /* */ /* ADD[]: ADD */ /* Opcode range: 0x60 */ /* Stack: f26.6 f26.6 --> f26.6 */ /* */ static void Ins_ADD( INS_ARG ) { DO_ADD } /*************************************************************************/ /* */ /* SUB[]: SUBtract */ /* Opcode range: 0x61 */ /* Stack: f26.6 f26.6 --> f26.6 */ /* */ static void Ins_SUB( INS_ARG ) { DO_SUB } /*************************************************************************/ /* */ /* DIV[]: DIVide */ /* Opcode range: 0x62 */ /* Stack: f26.6 f26.6 --> f26.6 */ /* */ static void Ins_DIV( INS_ARG ) { DO_DIV } /*************************************************************************/ /* */ /* MUL[]: MULtiply */ /* Opcode range: 0x63 */ /* Stack: f26.6 f26.6 --> f26.6 */ /* */ static void Ins_MUL( INS_ARG ) { DO_MUL } /*************************************************************************/ /* */ /* ABS[]: ABSolute value */ /* Opcode range: 0x64 */ /* Stack: f26.6 --> f26.6 */ /* */ static void Ins_ABS( INS_ARG ) { DO_ABS } /*************************************************************************/ /* */ /* NEG[]: NEGate */ /* Opcode range: 0x65 */ /* Stack: f26.6 --> f26.6 */ /* */ static void Ins_NEG( INS_ARG ) { DO_NEG } /*************************************************************************/ /* */ /* FLOOR[]: FLOOR */ /* Opcode range: 0x66 */ /* Stack: f26.6 --> f26.6 */ /* */ static void Ins_FLOOR( INS_ARG ) { DO_FLOOR } /*************************************************************************/ /* */ /* CEILING[]: CEILING */ /* Opcode range: 0x67 */ /* Stack: f26.6 --> f26.6 */ /* */ static void Ins_CEILING( INS_ARG ) { DO_CEILING } /*************************************************************************/ /* */ /* RS[]: Read Store */ /* Opcode range: 0x43 */ /* Stack: uint32 --> uint32 */ /* */ static void Ins_RS( INS_ARG ) { DO_RS } /*************************************************************************/ /* */ /* WS[]: Write Store */ /* Opcode range: 0x42 */ /* Stack: uint32 uint32 --> */ /* */ static void Ins_WS( INS_ARG ) { DO_WS } /*************************************************************************/ /* */ /* WCVTP[]: Write CVT in Pixel units */ /* Opcode range: 0x44 */ /* Stack: f26.6 uint32 --> */ /* */ static void Ins_WCVTP( INS_ARG ) { DO_WCVTP } /*************************************************************************/ /* */ /* WCVTF[]: Write CVT in Funits */ /* Opcode range: 0x70 */ /* Stack: uint32 uint32 --> */ /* */ static void Ins_WCVTF( INS_ARG ) { DO_WCVTF } /*************************************************************************/ /* */ /* RCVT[]: Read CVT */ /* Opcode range: 0x45 */ /* Stack: uint32 --> f26.6 */ /* */ static void Ins_RCVT( INS_ARG ) { DO_RCVT } /*************************************************************************/ /* */ /* AA[]: Adjust Angle */ /* Opcode range: 0x7F */ /* Stack: uint32 --> */ /* */ static void Ins_AA( INS_ARG ) { /* intentionally no longer supported */ } /*************************************************************************/ /* */ /* DEBUG[]: DEBUG. Unsupported. */ /* Opcode range: 0x4F */ /* Stack: uint32 --> */ /* */ /* Note: The original instruction pops a value from the stack. */ /* */ static void Ins_DEBUG( INS_ARG ) { DO_DEBUG } /*************************************************************************/ /* */ /* ROUND[ab]: ROUND value */ /* Opcode range: 0x68-0x6B */ /* Stack: f26.6 --> f26.6 */ /* */ static void Ins_ROUND( INS_ARG ) { DO_ROUND } /*************************************************************************/ /* */ /* NROUND[ab]: No ROUNDing of value */ /* Opcode range: 0x6C-0x6F */ /* Stack: f26.6 --> f26.6 */ /* */ static void Ins_NROUND( INS_ARG ) { DO_NROUND } /*************************************************************************/ /* */ /* MAX[]: MAXimum */ /* Opcode range: 0x68 */ /* Stack: int32? int32? --> int32 */ /* */ static void Ins_MAX( INS_ARG ) { DO_MAX } /*************************************************************************/ /* */ /* MIN[]: MINimum */ /* Opcode range: 0x69 */ /* Stack: int32? int32? --> int32 */ /* */ static void Ins_MIN( INS_ARG ) { DO_MIN } #endif /* !TT_CONFIG_OPTION_INTERPRETER_SWITCH */ /*************************************************************************/ /* */ /* The following functions are called as is within the switch statement. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* MINDEX[]: Move INDEXed element */ /* Opcode range: 0x26 */ /* Stack: int32? --> StkElt */ /* */ static void Ins_MINDEX( INS_ARG ) { FT_Long L, K; L = args[0]; if ( L <= 0 || L > CUR.args ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; } else { K = CUR.stack[CUR.args - L]; FT_ARRAY_MOVE( &CUR.stack[CUR.args - L ], &CUR.stack[CUR.args - L + 1], ( L - 1 ) ); CUR.stack[CUR.args - 1] = K; } } /*************************************************************************/ /* */ /* ROLL[]: ROLL top three elements */ /* Opcode range: 0x8A */ /* Stack: 3 * StkElt --> 3 * StkElt */ /* */ static void Ins_ROLL( INS_ARG ) { FT_Long A, B, C; FT_UNUSED_EXEC; A = args[2]; B = args[1]; C = args[0]; args[2] = C; args[1] = A; args[0] = B; } /*************************************************************************/ /* */ /* MANAGING THE FLOW OF CONTROL */ /* */ /* Instructions appear in the specification's order. */ /* */ /*************************************************************************/ static FT_Bool SkipCode( EXEC_OP ) { CUR.IP += CUR.length; if ( CUR.IP < CUR.codeSize ) { CUR.opcode = CUR.code[CUR.IP]; CUR.length = opcode_length[CUR.opcode]; if ( CUR.length < 0 ) { if ( CUR.IP + 1 > CUR.codeSize ) goto Fail_Overflow; CUR.length = 2 - CUR.length * CUR.code[CUR.IP + 1]; } if ( CUR.IP + CUR.length <= CUR.codeSize ) return SUCCESS; } Fail_Overflow: CUR.error = TT_Err_Code_Overflow; return FAILURE; } /*************************************************************************/ /* */ /* IF[]: IF test */ /* Opcode range: 0x58 */ /* Stack: StkElt --> */ /* */ static void Ins_IF( INS_ARG ) { FT_Int nIfs; FT_Bool Out; if ( args[0] != 0 ) return; nIfs = 1; Out = 0; do { if ( SKIP_Code() == FAILURE ) return; switch ( CUR.opcode ) { case 0x58: /* IF */ nIfs++; break; case 0x1B: /* ELSE */ Out = FT_BOOL( nIfs == 1 ); break; case 0x59: /* EIF */ nIfs--; Out = FT_BOOL( nIfs == 0 ); break; } } while ( Out == 0 ); } /*************************************************************************/ /* */ /* ELSE[]: ELSE */ /* Opcode range: 0x1B */ /* Stack: --> */ /* */ static void Ins_ELSE( INS_ARG ) { FT_Int nIfs; FT_UNUSED_ARG; nIfs = 1; do { if ( SKIP_Code() == FAILURE ) return; switch ( CUR.opcode ) { case 0x58: /* IF */ nIfs++; break; case 0x59: /* EIF */ nIfs--; break; } } while ( nIfs != 0 ); } /*************************************************************************/ /* */ /* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS */ /* */ /* Instructions appear in the specification's order. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* FDEF[]: Function DEFinition */ /* Opcode range: 0x2C */ /* Stack: uint32 --> */ /* */ static void Ins_FDEF( INS_ARG ) { FT_ULong n; TT_DefRecord* rec; TT_DefRecord* limit; /* some font programs are broken enough to redefine functions! */ /* We will then parse the current table. */ rec = CUR.FDefs; limit = rec + CUR.numFDefs; n = args[0]; for ( ; rec < limit; rec++ ) { if ( rec->opc == n ) break; } if ( rec == limit ) { /* check that there is enough room for new functions */ if ( CUR.numFDefs >= CUR.maxFDefs ) { CUR.error = TT_Err_Too_Many_Function_Defs; return; } CUR.numFDefs++; } /* Although FDEF takes unsigned 32-bit integer, */ /* func # must be within unsigned 16-bit integer */ if ( n > 0xFFFFU ) { CUR.error = TT_Err_Too_Many_Function_Defs; return; } rec->range = CUR.curRange; rec->opc = (FT_UInt16)n; rec->start = CUR.IP + 1; rec->active = TRUE; if ( n > CUR.maxFunc ) CUR.maxFunc = (FT_UInt16)n; /* Now skip the whole function definition. */ /* We don't allow nested IDEFS & FDEFs. */ while ( SKIP_Code() == SUCCESS ) { switch ( CUR.opcode ) { case 0x89: /* IDEF */ case 0x2C: /* FDEF */ CUR.error = TT_Err_Nested_DEFS; return; case 0x2D: /* ENDF */ return; } } } /*************************************************************************/ /* */ /* ENDF[]: END Function definition */ /* Opcode range: 0x2D */ /* Stack: --> */ /* */ static void Ins_ENDF( INS_ARG ) { TT_CallRec* pRec; FT_UNUSED_ARG; if ( CUR.callTop <= 0 ) /* We encountered an ENDF without a call */ { CUR.error = TT_Err_ENDF_In_Exec_Stream; return; } CUR.callTop--; pRec = &CUR.callStack[CUR.callTop]; pRec->Cur_Count--; CUR.step_ins = FALSE; if ( pRec->Cur_Count > 0 ) { CUR.callTop++; CUR.IP = pRec->Cur_Restart; } else /* Loop through the current function */ INS_Goto_CodeRange( pRec->Caller_Range, pRec->Caller_IP ); /* Exit the current call frame. */ /* NOTE: If the last instruction of a program is a */ /* CALL or LOOPCALL, the return address is */ /* always out of the code range. This is a */ /* valid address, and it is why we do not test */ /* the result of Ins_Goto_CodeRange() here! */ } /*************************************************************************/ /* */ /* CALL[]: CALL function */ /* Opcode range: 0x2B */ /* Stack: uint32? --> */ /* */ static void Ins_CALL( INS_ARG ) { FT_ULong F; TT_CallRec* pCrec; TT_DefRecord* def; /* first of all, check the index */ F = args[0]; if ( BOUNDSL( F, CUR.maxFunc + 1 ) ) goto Fail; /* Except for some old Apple fonts, all functions in a TrueType */ /* font are defined in increasing order, starting from 0. This */ /* means that we normally have */ /* */ /* CUR.maxFunc+1 == CUR.numFDefs */ /* CUR.FDefs[n].opc == n for n in 0..CUR.maxFunc */ /* */ /* If this isn't true, we need to look up the function table. */ def = CUR.FDefs + F; if ( CUR.maxFunc + 1 != CUR.numFDefs || def->opc != F ) { /* look up the FDefs table */ TT_DefRecord* limit; def = CUR.FDefs; limit = def + CUR.numFDefs; while ( def < limit && def->opc != F ) def++; if ( def == limit ) goto Fail; } /* check that the function is active */ if ( !def->active ) goto Fail; /* check the call stack */ if ( CUR.callTop >= CUR.callSize ) { CUR.error = TT_Err_Stack_Overflow; return; } pCrec = CUR.callStack + CUR.callTop; pCrec->Caller_Range = CUR.curRange; pCrec->Caller_IP = CUR.IP + 1; pCrec->Cur_Count = 1; pCrec->Cur_Restart = def->start; CUR.callTop++; INS_Goto_CodeRange( def->range, def->start ); CUR.step_ins = FALSE; return; Fail: CUR.error = TT_Err_Invalid_Reference; } /*************************************************************************/ /* */ /* LOOPCALL[]: LOOP and CALL function */ /* Opcode range: 0x2A */ /* Stack: uint32? Eint16? --> */ /* */ static void Ins_LOOPCALL( INS_ARG ) { FT_ULong F; TT_CallRec* pCrec; TT_DefRecord* def; /* first of all, check the index */ F = args[1]; if ( BOUNDSL( F, CUR.maxFunc + 1 ) ) goto Fail; /* Except for some old Apple fonts, all functions in a TrueType */ /* font are defined in increasing order, starting from 0. This */ /* means that we normally have */ /* */ /* CUR.maxFunc+1 == CUR.numFDefs */ /* CUR.FDefs[n].opc == n for n in 0..CUR.maxFunc */ /* */ /* If this isn't true, we need to look up the function table. */ def = CUR.FDefs + F; if ( CUR.maxFunc + 1 != CUR.numFDefs || def->opc != F ) { /* look up the FDefs table */ TT_DefRecord* limit; def = CUR.FDefs; limit = def + CUR.numFDefs; while ( def < limit && def->opc != F ) def++; if ( def == limit ) goto Fail; } /* check that the function is active */ if ( !def->active ) goto Fail; /* check stack */ if ( CUR.callTop >= CUR.callSize ) { CUR.error = TT_Err_Stack_Overflow; return; } if ( args[0] > 0 ) { pCrec = CUR.callStack + CUR.callTop; pCrec->Caller_Range = CUR.curRange; pCrec->Caller_IP = CUR.IP + 1; pCrec->Cur_Count = (FT_Int)args[0]; pCrec->Cur_Restart = def->start; CUR.callTop++; INS_Goto_CodeRange( def->range, def->start ); CUR.step_ins = FALSE; } return; Fail: CUR.error = TT_Err_Invalid_Reference; } /*************************************************************************/ /* */ /* IDEF[]: Instruction DEFinition */ /* Opcode range: 0x89 */ /* Stack: Eint8 --> */ /* */ static void Ins_IDEF( INS_ARG ) { TT_DefRecord* def; TT_DefRecord* limit; /* First of all, look for the same function in our table */ def = CUR.IDefs; limit = def + CUR.numIDefs; for ( ; def < limit; def++ ) if ( def->opc == (FT_ULong)args[0] ) break; if ( def == limit ) { /* check that there is enough room for a new instruction */ if ( CUR.numIDefs >= CUR.maxIDefs ) { CUR.error = TT_Err_Too_Many_Instruction_Defs; return; } CUR.numIDefs++; } /* opcode must be unsigned 8-bit integer */ if ( 0 > args[0] || args[0] > 0x00FF ) { CUR.error = TT_Err_Too_Many_Instruction_Defs; return; } def->opc = (FT_Byte)args[0]; def->start = CUR.IP + 1; def->range = CUR.curRange; def->active = TRUE; if ( (FT_ULong)args[0] > CUR.maxIns ) CUR.maxIns = (FT_Byte)args[0]; /* Now skip the whole function definition. */ /* We don't allow nested IDEFs & FDEFs. */ while ( SKIP_Code() == SUCCESS ) { switch ( CUR.opcode ) { case 0x89: /* IDEF */ case 0x2C: /* FDEF */ CUR.error = TT_Err_Nested_DEFS; return; case 0x2D: /* ENDF */ return; } } } /*************************************************************************/ /* */ /* PUSHING DATA ONTO THE INTERPRETER STACK */ /* */ /* Instructions appear in the specification's order. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* NPUSHB[]: PUSH N Bytes */ /* Opcode range: 0x40 */ /* Stack: --> uint32... */ /* */ static void Ins_NPUSHB( INS_ARG ) { FT_UShort L, K; L = (FT_UShort)CUR.code[CUR.IP + 1]; if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) ) { CUR.error = TT_Err_Stack_Overflow; return; } for ( K = 1; K <= L; K++ ) args[K - 1] = CUR.code[CUR.IP + K + 1]; CUR.new_top += L; } /*************************************************************************/ /* */ /* NPUSHW[]: PUSH N Words */ /* Opcode range: 0x41 */ /* Stack: --> int32... */ /* */ static void Ins_NPUSHW( INS_ARG ) { FT_UShort L, K; L = (FT_UShort)CUR.code[CUR.IP + 1]; if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) ) { CUR.error = TT_Err_Stack_Overflow; return; } CUR.IP += 2; for ( K = 0; K < L; K++ ) args[K] = GET_ShortIns(); CUR.step_ins = FALSE; CUR.new_top += L; } /*************************************************************************/ /* */ /* PUSHB[abc]: PUSH Bytes */ /* Opcode range: 0xB0-0xB7 */ /* Stack: --> uint32... */ /* */ static void Ins_PUSHB( INS_ARG ) { FT_UShort L, K; L = (FT_UShort)( CUR.opcode - 0xB0 + 1 ); if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) ) { CUR.error = TT_Err_Stack_Overflow; return; } for ( K = 1; K <= L; K++ ) args[K - 1] = CUR.code[CUR.IP + K]; } /*************************************************************************/ /* */ /* PUSHW[abc]: PUSH Words */ /* Opcode range: 0xB8-0xBF */ /* Stack: --> int32... */ /* */ static void Ins_PUSHW( INS_ARG ) { FT_UShort L, K; L = (FT_UShort)( CUR.opcode - 0xB8 + 1 ); if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) ) { CUR.error = TT_Err_Stack_Overflow; return; } CUR.IP++; for ( K = 0; K < L; K++ ) args[K] = GET_ShortIns(); CUR.step_ins = FALSE; } /*************************************************************************/ /* */ /* MANAGING THE GRAPHICS STATE */ /* */ /* Instructions appear in the specs' order. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* GC[a]: Get Coordinate projected onto */ /* Opcode range: 0x46-0x47 */ /* Stack: uint32 --> f26.6 */ /* */ /* BULLSHIT: Measures from the original glyph must be taken along the */ /* dual projection vector! */ /* */ static void Ins_GC( INS_ARG ) { FT_ULong L; FT_F26Dot6 R; L = (FT_ULong)args[0]; if ( BOUNDSL( L, CUR.zp2.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; R = 0; } else { if ( CUR.opcode & 1 ) R = CUR_fast_dualproj( &CUR.zp2.org[L] ); else R = CUR_fast_project( &CUR.zp2.cur[L] ); } args[0] = R; } /*************************************************************************/ /* */ /* SCFS[]: Set Coordinate From Stack */ /* Opcode range: 0x48 */ /* Stack: f26.6 uint32 --> */ /* */ /* Formula: */ /* */ /* OA := OA + ( value - OA.p )/( f.p ) * f */ /* */ static void Ins_SCFS( INS_ARG ) { FT_Long K; FT_UShort L; L = (FT_UShort)args[0]; if ( BOUNDS( L, CUR.zp2.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } K = CUR_fast_project( &CUR.zp2.cur[L] ); CUR_Func_move( &CUR.zp2, L, args[1] - K ); /* UNDOCUMENTED! The MS rasterizer does that with */ /* twilight points (confirmed by Greg Hitchcock) */ if ( CUR.GS.gep2 == 0 ) CUR.zp2.org[L] = CUR.zp2.cur[L]; } /*************************************************************************/ /* */ /* MD[a]: Measure Distance */ /* Opcode range: 0x49-0x4A */ /* Stack: uint32 uint32 --> f26.6 */ /* */ /* BULLSHIT: Measure taken in the original glyph must be along the dual */ /* projection vector. */ /* */ /* Second BULLSHIT: Flag attributes are inverted! */ /* 0 => measure distance in original outline */ /* 1 => measure distance in grid-fitted outline */ /* */ /* Third one: `zp0 - zp1', and not `zp2 - zp1! */ /* */ static void Ins_MD( INS_ARG ) { FT_UShort K, L; FT_F26Dot6 D; K = (FT_UShort)args[1]; L = (FT_UShort)args[0]; if ( BOUNDS( L, CUR.zp0.n_points ) || BOUNDS( K, CUR.zp1.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; D = 0; } else { if ( CUR.opcode & 1 ) D = CUR_Func_project( CUR.zp0.cur + L, CUR.zp1.cur + K ); else { /* XXX: UNDOCUMENTED: twilight zone special case */ if ( CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0 ) { FT_Vector* vec1 = CUR.zp0.org + L; FT_Vector* vec2 = CUR.zp1.org + K; D = CUR_Func_dualproj( vec1, vec2 ); } else { FT_Vector* vec1 = CUR.zp0.orus + L; FT_Vector* vec2 = CUR.zp1.orus + K; if ( CUR.metrics.x_scale == CUR.metrics.y_scale ) { /* this should be faster */ D = CUR_Func_dualproj( vec1, vec2 ); D = TT_MULFIX( D, CUR.metrics.x_scale ); } else { FT_Vector vec; vec.x = TT_MULFIX( vec1->x - vec2->x, CUR.metrics.x_scale ); vec.y = TT_MULFIX( vec1->y - vec2->y, CUR.metrics.y_scale ); D = CUR_fast_dualproj( &vec ); } } } } args[0] = D; } /*************************************************************************/ /* */ /* SDPVTL[a]: Set Dual PVector to Line */ /* Opcode range: 0x86-0x87 */ /* Stack: uint32 uint32 --> */ /* */ static void Ins_SDPVTL( INS_ARG ) { FT_Long A, B, C; FT_UShort p1, p2; /* was FT_Int in pas type ERROR */ FT_Int aOpc = CUR.opcode; p1 = (FT_UShort)args[1]; p2 = (FT_UShort)args[0]; if ( BOUNDS( p2, CUR.zp1.n_points ) || BOUNDS( p1, CUR.zp2.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } { FT_Vector* v1 = CUR.zp1.org + p2; FT_Vector* v2 = CUR.zp2.org + p1; A = v1->x - v2->x; B = v1->y - v2->y; /* If v1 == v2, SDPVTL behaves the same as */ /* SVTCA[X], respectively. */ /* */ /* Confirmed by Greg Hitchcock. */ if ( A == 0 && B == 0 ) { A = 0x4000; aOpc = 0; } } if ( ( aOpc & 1 ) != 0 ) { C = B; /* counter clockwise rotation */ B = A; A = -C; } NORMalize( A, B, &CUR.GS.dualVector ); { FT_Vector* v1 = CUR.zp1.cur + p2; FT_Vector* v2 = CUR.zp2.cur + p1; A = v1->x - v2->x; B = v1->y - v2->y; } if ( ( aOpc & 1 ) != 0 ) { C = B; /* counter clockwise rotation */ B = A; A = -C; } NORMalize( A, B, &CUR.GS.projVector ); GUESS_VECTOR( freeVector ); COMPUTE_Funcs(); } /*************************************************************************/ /* */ /* SZP0[]: Set Zone Pointer 0 */ /* Opcode range: 0x13 */ /* Stack: uint32 --> */ /* */ static void Ins_SZP0( INS_ARG ) { switch ( (FT_Int)args[0] ) { case 0: CUR.zp0 = CUR.twilight; break; case 1: CUR.zp0 = CUR.pts; break; default: if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } CUR.GS.gep0 = (FT_UShort)args[0]; } /*************************************************************************/ /* */ /* SZP1[]: Set Zone Pointer 1 */ /* Opcode range: 0x14 */ /* Stack: uint32 --> */ /* */ static void Ins_SZP1( INS_ARG ) { switch ( (FT_Int)args[0] ) { case 0: CUR.zp1 = CUR.twilight; break; case 1: CUR.zp1 = CUR.pts; break; default: if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } CUR.GS.gep1 = (FT_UShort)args[0]; } /*************************************************************************/ /* */ /* SZP2[]: Set Zone Pointer 2 */ /* Opcode range: 0x15 */ /* Stack: uint32 --> */ /* */ static void Ins_SZP2( INS_ARG ) { switch ( (FT_Int)args[0] ) { case 0: CUR.zp2 = CUR.twilight; break; case 1: CUR.zp2 = CUR.pts; break; default: if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } CUR.GS.gep2 = (FT_UShort)args[0]; } /*************************************************************************/ /* */ /* SZPS[]: Set Zone PointerS */ /* Opcode range: 0x16 */ /* Stack: uint32 --> */ /* */ static void Ins_SZPS( INS_ARG ) { switch ( (FT_Int)args[0] ) { case 0: CUR.zp0 = CUR.twilight; break; case 1: CUR.zp0 = CUR.pts; break; default: if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } CUR.zp1 = CUR.zp0; CUR.zp2 = CUR.zp0; CUR.GS.gep0 = (FT_UShort)args[0]; CUR.GS.gep1 = (FT_UShort)args[0]; CUR.GS.gep2 = (FT_UShort)args[0]; } /*************************************************************************/ /* */ /* INSTCTRL[]: INSTruction ConTRoL */ /* Opcode range: 0x8e */ /* Stack: int32 int32 --> */ /* */ static void Ins_INSTCTRL( INS_ARG ) { FT_Long K, L; K = args[1]; L = args[0]; if ( K < 1 || K > 2 ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } if ( L != 0 ) L = K; CUR.GS.instruct_control = FT_BOOL( ( (FT_Byte)CUR.GS.instruct_control & ~(FT_Byte)K ) | (FT_Byte)L ); } /*************************************************************************/ /* */ /* SCANCTRL[]: SCAN ConTRoL */ /* Opcode range: 0x85 */ /* Stack: uint32? --> */ /* */ static void Ins_SCANCTRL( INS_ARG ) { FT_Int A; /* Get Threshold */ A = (FT_Int)( args[0] & 0xFF ); if ( A == 0xFF ) { CUR.GS.scan_control = TRUE; return; } else if ( A == 0 ) { CUR.GS.scan_control = FALSE; return; } if ( ( args[0] & 0x100 ) != 0 && CUR.tt_metrics.ppem <= A ) CUR.GS.scan_control = TRUE; if ( ( args[0] & 0x200 ) != 0 && CUR.tt_metrics.rotated ) CUR.GS.scan_control = TRUE; if ( ( args[0] & 0x400 ) != 0 && CUR.tt_metrics.stretched ) CUR.GS.scan_control = TRUE; if ( ( args[0] & 0x800 ) != 0 && CUR.tt_metrics.ppem > A ) CUR.GS.scan_control = FALSE; if ( ( args[0] & 0x1000 ) != 0 && CUR.tt_metrics.rotated ) CUR.GS.scan_control = FALSE; if ( ( args[0] & 0x2000 ) != 0 && CUR.tt_metrics.stretched ) CUR.GS.scan_control = FALSE; } /*************************************************************************/ /* */ /* SCANTYPE[]: SCAN TYPE */ /* Opcode range: 0x8D */ /* Stack: uint32? --> */ /* */ static void Ins_SCANTYPE( INS_ARG ) { if ( args[0] >= 0 ) CUR.GS.scan_type = (FT_Int)args[0]; } /*************************************************************************/ /* */ /* MANAGING OUTLINES */ /* */ /* Instructions appear in the specification's order. */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* FLIPPT[]: FLIP PoinT */ /* Opcode range: 0x80 */ /* Stack: uint32... --> */ /* */ static void Ins_FLIPPT( INS_ARG ) { FT_UShort point; FT_UNUSED_ARG; if ( CUR.top < CUR.GS.loop ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Too_Few_Arguments; goto Fail; } while ( CUR.GS.loop > 0 ) { CUR.args--; point = (FT_UShort)CUR.stack[CUR.args]; if ( BOUNDS( point, CUR.pts.n_points ) ) { if ( CUR.pedantic_hinting ) { CUR.error = TT_Err_Invalid_Reference; return; } } else CUR.pts.tags[point] ^= FT_CURVE_TAG_ON; CUR.GS.loop--; } Fail: CUR.GS.loop = 1; CUR.new_top = CUR.args; } /*************************************************************************/ /* */ /* FLIPRGON[]: FLIP RanGe ON */ /* Opcode range: 0x81 */ /* Stack: uint32 uint32 --> */ /* */ static void Ins_FLIPRGON( INS_ARG ) { FT_UShort I, K, L; K = (FT_UShort)args[1]; L = (FT_UShort)args[0]; if ( BOUNDS( K, CUR.pts.n_points ) || BOUNDS( L, CUR.pts.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } for ( I = L; I <= K; I++ ) CUR.pts.tags[I] |= FT_CURVE_TAG_ON; } /*************************************************************************/ /* */ /* FLIPRGOFF: FLIP RanGe OFF */ /* Opcode range: 0x82 */ /* Stack: uint32 uint32 --> */ /* */ static void Ins_FLIPRGOFF( INS_ARG ) { FT_UShort I, K, L; K = (FT_UShort)args[1]; L = (FT_UShort)args[0]; if ( BOUNDS( K, CUR.pts.n_points ) || BOUNDS( L, CUR.pts.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } for ( I = L; I <= K; I++ ) CUR.pts.tags[I] &= ~FT_CURVE_TAG_ON; } static FT_Bool Compute_Point_Displacement( EXEC_OP_ FT_F26Dot6* x, FT_F26Dot6* y, TT_GlyphZone zone, FT_UShort* refp ) { TT_GlyphZoneRec zp; FT_UShort p; FT_F26Dot6 d; if ( CUR.opcode & 1 ) { zp = CUR.zp0; p = CUR.GS.rp1; } else { zp = CUR.zp1; p = CUR.GS.rp2; } if ( BOUNDS( p, zp.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; *refp = 0; return FAILURE; } *zone = zp; *refp = p; d = CUR_Func_project( zp.cur + p, zp.org + p ); #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING if ( CUR.face->unpatented_hinting ) { if ( CUR.GS.both_x_axis ) { *x = d; *y = 0; } else { *x = 0; *y = d; } } else #endif { *x = TT_MULDIV( d, (FT_Long)CUR.GS.freeVector.x * 0x10000L, CUR.F_dot_P ); *y = TT_MULDIV( d, (FT_Long)CUR.GS.freeVector.y * 0x10000L, CUR.F_dot_P ); } return SUCCESS; } static void Move_Zp2_Point( EXEC_OP_ FT_UShort point, FT_F26Dot6 dx, FT_F26Dot6 dy, FT_Bool touch ) { #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING if ( CUR.face->unpatented_hinting ) { if ( CUR.GS.both_x_axis ) { CUR.zp2.cur[point].x += dx; if ( touch ) CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X; } else { CUR.zp2.cur[point].y += dy; if ( touch ) CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y; } return; } #endif if ( CUR.GS.freeVector.x != 0 ) { CUR.zp2.cur[point].x += dx; if ( touch ) CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X; } if ( CUR.GS.freeVector.y != 0 ) { CUR.zp2.cur[point].y += dy; if ( touch ) CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y; } } /*************************************************************************/ /* */ /* SHP[a]: SHift Point by the last point */ /* Opcode range: 0x32-0x33 */ /* Stack: uint32... --> */ /* */ static void Ins_SHP( INS_ARG ) { TT_GlyphZoneRec zp; FT_UShort refp; FT_F26Dot6 dx, dy; FT_UShort point; FT_UNUSED_ARG; if ( CUR.top < CUR.GS.loop ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; goto Fail; } if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) ) return; while ( CUR.GS.loop > 0 ) { CUR.args--; point = (FT_UShort)CUR.stack[CUR.args]; if ( BOUNDS( point, CUR.zp2.n_points ) ) { if ( CUR.pedantic_hinting ) { CUR.error = TT_Err_Invalid_Reference; return; } } else MOVE_Zp2_Point( point, dx, dy, TRUE ); CUR.GS.loop--; } Fail: CUR.GS.loop = 1; CUR.new_top = CUR.args; } /*************************************************************************/ /* */ /* SHC[a]: SHift Contour */ /* Opcode range: 0x34-35 */ /* Stack: uint32 --> */ /* */ static void Ins_SHC( INS_ARG ) { TT_GlyphZoneRec zp; FT_UShort refp; FT_F26Dot6 dx, dy; FT_Short contour; FT_UShort first_point, last_point, i; contour = (FT_UShort)args[0]; if ( BOUNDS( contour, CUR.pts.n_contours ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) ) return; if ( contour == 0 ) first_point = 0; else first_point = (FT_UShort)( CUR.pts.contours[contour - 1] + 1 - CUR.pts.first_point ); last_point = (FT_UShort)( CUR.pts.contours[contour] - CUR.pts.first_point ); /* XXX: this is probably wrong... at least it prevents memory */ /* corruption when zp2 is the twilight zone */ if ( BOUNDS( last_point, CUR.zp2.n_points ) ) { if ( CUR.zp2.n_points > 0 ) last_point = (FT_UShort)(CUR.zp2.n_points - 1); else last_point = 0; } for ( i = first_point; i <= last_point; i++ ) { if ( zp.cur != CUR.zp2.cur || refp != i ) MOVE_Zp2_Point( i, dx, dy, TRUE ); } } /*************************************************************************/ /* */ /* SHZ[a]: SHift Zone */ /* Opcode range: 0x36-37 */ /* Stack: uint32 --> */ /* */ static void Ins_SHZ( INS_ARG ) { TT_GlyphZoneRec zp; FT_UShort refp; FT_F26Dot6 dx, dy; FT_UShort last_point, i; if ( BOUNDS( args[0], 2 ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) ) return; /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points. */ /* Twilight zone has no contours, so use `n_points'. */ /* Normal zone's `n_points' includes phantoms, so must */ /* use end of last contour. */ if ( CUR.GS.gep2 == 0 && CUR.zp2.n_points > 0 ) last_point = (FT_UShort)( CUR.zp2.n_points - 1 ); else if ( CUR.GS.gep2 == 1 && CUR.zp2.n_contours > 0 ) { last_point = (FT_UShort)( CUR.zp2.contours[CUR.zp2.n_contours - 1] ); if ( BOUNDS( last_point, CUR.zp2.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } } else last_point = 0; /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */ for ( i = 0; i <= last_point; i++ ) { if ( zp.cur != CUR.zp2.cur || refp != i ) MOVE_Zp2_Point( i, dx, dy, FALSE ); } } /*************************************************************************/ /* */ /* SHPIX[]: SHift points by a PIXel amount */ /* Opcode range: 0x38 */ /* Stack: f26.6 uint32... --> */ /* */ static void Ins_SHPIX( INS_ARG ) { FT_F26Dot6 dx, dy; FT_UShort point; if ( CUR.top < CUR.GS.loop + 1 ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; goto Fail; } #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING if ( CUR.face->unpatented_hinting ) { if ( CUR.GS.both_x_axis ) { dx = TT_MulFix14( (FT_UInt32)args[0], 0x4000 ); dy = 0; } else { dx = 0; dy = TT_MulFix14( (FT_UInt32)args[0], 0x4000 ); } } else #endif { dx = TT_MulFix14( (FT_UInt32)args[0], CUR.GS.freeVector.x ); dy = TT_MulFix14( (FT_UInt32)args[0], CUR.GS.freeVector.y ); } while ( CUR.GS.loop > 0 ) { CUR.args--; point = (FT_UShort)CUR.stack[CUR.args]; if ( BOUNDS( point, CUR.zp2.n_points ) ) { if ( CUR.pedantic_hinting ) { CUR.error = TT_Err_Invalid_Reference; return; } } else MOVE_Zp2_Point( point, dx, dy, TRUE ); CUR.GS.loop--; } Fail: CUR.GS.loop = 1; CUR.new_top = CUR.args; } /*************************************************************************/ /* */ /* MSIRP[a]: Move Stack Indirect Relative Position */ /* Opcode range: 0x3A-0x3B */ /* Stack: f26.6 uint32 --> */ /* */ static void Ins_MSIRP( INS_ARG ) { FT_UShort point; FT_F26Dot6 distance; point = (FT_UShort)args[0]; if ( BOUNDS( point, CUR.zp1.n_points ) || BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } /* UNDOCUMENTED! The MS rasterizer does that with */ /* twilight points (confirmed by Greg Hitchcock) */ if ( CUR.GS.gep1 == 0 ) { CUR.zp1.org[point] = CUR.zp0.org[CUR.GS.rp0]; CUR_Func_move_orig( &CUR.zp1, point, args[1] ); CUR.zp1.cur[point] = CUR.zp1.org[point]; } distance = CUR_Func_project( CUR.zp1.cur + point, CUR.zp0.cur + CUR.GS.rp0 ); CUR_Func_move( &CUR.zp1, point, args[1] - distance ); CUR.GS.rp1 = CUR.GS.rp0; CUR.GS.rp2 = point; if ( ( CUR.opcode & 1 ) != 0 ) CUR.GS.rp0 = point; } /*************************************************************************/ /* */ /* MDAP[a]: Move Direct Absolute Point */ /* Opcode range: 0x2E-0x2F */ /* Stack: uint32 --> */ /* */ static void Ins_MDAP( INS_ARG ) { FT_UShort point; FT_F26Dot6 cur_dist, distance; point = (FT_UShort)args[0]; if ( BOUNDS( point, CUR.zp0.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } if ( ( CUR.opcode & 1 ) != 0 ) { cur_dist = CUR_fast_project( &CUR.zp0.cur[point] ); distance = CUR_Func_round( cur_dist, CUR.tt_metrics.compensations[0] ) - cur_dist; } else distance = 0; CUR_Func_move( &CUR.zp0, point, distance ); CUR.GS.rp0 = point; CUR.GS.rp1 = point; } /*************************************************************************/ /* */ /* MIAP[a]: Move Indirect Absolute Point */ /* Opcode range: 0x3E-0x3F */ /* Stack: uint32 uint32 --> */ /* */ static void Ins_MIAP( INS_ARG ) { FT_ULong cvtEntry; FT_UShort point; FT_F26Dot6 distance, org_dist; cvtEntry = (FT_ULong)args[1]; point = (FT_UShort)args[0]; if ( BOUNDS( point, CUR.zp0.n_points ) || BOUNDSL( cvtEntry, CUR.cvtSize ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; goto Fail; } /* UNDOCUMENTED! */ /* */ /* The behaviour of an MIAP instruction is quite different when used */ /* in the twilight zone. */ /* */ /* First, no control value cut-in test is performed as it would fail */ /* anyway. Second, the original point, i.e. (org_x,org_y) of */ /* zp0.point, is set to the absolute, unrounded distance found in the */ /* CVT. */ /* */ /* This is used in the CVT programs of the Microsoft fonts Arial, */ /* Times, etc., in order to re-adjust some key font heights. It */ /* allows the use of the IP instruction in the twilight zone, which */ /* otherwise would be invalid according to the specification. */ /* */ /* We implement it with a special sequence for the twilight zone. */ /* This is a bad hack, but it seems to work. */ /* */ /* Confirmed by Greg Hitchcock. */ distance = CUR_Func_read_cvt( cvtEntry ); if ( CUR.GS.gep0 == 0 ) /* If in twilight zone */ { CUR.zp0.org[point].x = TT_MulFix14( (FT_UInt32)distance, CUR.GS.freeVector.x ); CUR.zp0.org[point].y = TT_MulFix14( (FT_UInt32)distance, CUR.GS.freeVector.y ), CUR.zp0.cur[point] = CUR.zp0.org[point]; } org_dist = CUR_fast_project( &CUR.zp0.cur[point] ); if ( ( CUR.opcode & 1 ) != 0 ) /* rounding and control cutin flag */ { if ( FT_ABS( distance - org_dist ) > CUR.GS.control_value_cutin ) distance = org_dist; distance = CUR_Func_round( distance, CUR.tt_metrics.compensations[0] ); } CUR_Func_move( &CUR.zp0, point, distance - org_dist ); Fail: CUR.GS.rp0 = point; CUR.GS.rp1 = point; } /*************************************************************************/ /* */ /* MDRP[abcde]: Move Direct Relative Point */ /* Opcode range: 0xC0-0xDF */ /* Stack: uint32 --> */ /* */ static void Ins_MDRP( INS_ARG ) { FT_UShort point; FT_F26Dot6 org_dist, distance; point = (FT_UShort)args[0]; if ( BOUNDS( point, CUR.zp1.n_points ) || BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; goto Fail; } /* XXX: Is there some undocumented feature while in the */ /* twilight zone? */ /* XXX: UNDOCUMENTED: twilight zone special case */ if ( CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0 ) { FT_Vector* vec1 = &CUR.zp1.org[point]; FT_Vector* vec2 = &CUR.zp0.org[CUR.GS.rp0]; org_dist = CUR_Func_dualproj( vec1, vec2 ); } else { FT_Vector* vec1 = &CUR.zp1.orus[point]; FT_Vector* vec2 = &CUR.zp0.orus[CUR.GS.rp0]; if ( CUR.metrics.x_scale == CUR.metrics.y_scale ) { /* this should be faster */ org_dist = CUR_Func_dualproj( vec1, vec2 ); org_dist = TT_MULFIX( org_dist, CUR.metrics.x_scale ); } else { FT_Vector vec; vec.x = TT_MULFIX( vec1->x - vec2->x, CUR.metrics.x_scale ); vec.y = TT_MULFIX( vec1->y - vec2->y, CUR.metrics.y_scale ); org_dist = CUR_fast_dualproj( &vec ); } } /* single width cut-in test */ if ( FT_ABS( org_dist - CUR.GS.single_width_value ) < CUR.GS.single_width_cutin ) { if ( org_dist >= 0 ) org_dist = CUR.GS.single_width_value; else org_dist = -CUR.GS.single_width_value; } /* round flag */ if ( ( CUR.opcode & 4 ) != 0 ) distance = CUR_Func_round( org_dist, CUR.tt_metrics.compensations[CUR.opcode & 3] ); else distance = ROUND_None( org_dist, CUR.tt_metrics.compensations[CUR.opcode & 3] ); /* minimum distance flag */ if ( ( CUR.opcode & 8 ) != 0 ) { if ( org_dist >= 0 ) { if ( distance < CUR.GS.minimum_distance ) distance = CUR.GS.minimum_distance; } else { if ( distance > -CUR.GS.minimum_distance ) distance = -CUR.GS.minimum_distance; } } /* now move the point */ org_dist = CUR_Func_project( CUR.zp1.cur + point, CUR.zp0.cur + CUR.GS.rp0 ); CUR_Func_move( &CUR.zp1, point, distance - org_dist ); Fail: CUR.GS.rp1 = CUR.GS.rp0; CUR.GS.rp2 = point; if ( ( CUR.opcode & 16 ) != 0 ) CUR.GS.rp0 = point; } /*************************************************************************/ /* */ /* MIRP[abcde]: Move Indirect Relative Point */ /* Opcode range: 0xE0-0xFF */ /* Stack: int32? uint32 --> */ /* */ static void Ins_MIRP( INS_ARG ) { FT_UShort point; FT_ULong cvtEntry; FT_F26Dot6 cvt_dist, distance, cur_dist, org_dist; point = (FT_UShort)args[0]; cvtEntry = (FT_ULong)( args[1] + 1 ); /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */ if ( BOUNDS( point, CUR.zp1.n_points ) || BOUNDSL( cvtEntry, CUR.cvtSize + 1 ) || BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; goto Fail; } if ( !cvtEntry ) cvt_dist = 0; else cvt_dist = CUR_Func_read_cvt( cvtEntry - 1 ); /* single width test */ if ( FT_ABS( cvt_dist - CUR.GS.single_width_value ) < CUR.GS.single_width_cutin ) { if ( cvt_dist >= 0 ) cvt_dist = CUR.GS.single_width_value; else cvt_dist = -CUR.GS.single_width_value; } /* UNDOCUMENTED! The MS rasterizer does that with */ /* twilight points (confirmed by Greg Hitchcock) */ if ( CUR.GS.gep1 == 0 ) { CUR.zp1.org[point].x = CUR.zp0.org[CUR.GS.rp0].x + TT_MulFix14( (FT_UInt32)cvt_dist, CUR.GS.freeVector.x ); CUR.zp1.org[point].y = CUR.zp0.org[CUR.GS.rp0].y + TT_MulFix14( (FT_UInt32)cvt_dist, CUR.GS.freeVector.y ); CUR.zp1.cur[point] = CUR.zp0.cur[point]; } org_dist = CUR_Func_dualproj( &CUR.zp1.org[point], &CUR.zp0.org[CUR.GS.rp0] ); cur_dist = CUR_Func_project ( &CUR.zp1.cur[point], &CUR.zp0.cur[CUR.GS.rp0] ); /* auto-flip test */ if ( CUR.GS.auto_flip ) { if ( ( org_dist ^ cvt_dist ) < 0 ) cvt_dist = -cvt_dist; } /* control value cutin and round */ if ( ( CUR.opcode & 4 ) != 0 ) { /* XXX: UNDOCUMENTED! Only perform cut-in test when both points */ /* refer to the same zone. */ if ( CUR.GS.gep0 == CUR.GS.gep1 ) { /* XXX: According to Greg Hitchcock, the following wording is */ /* the right one: */ /* */ /* When the absolute difference between the value in */ /* the table [CVT] and the measurement directly from */ /* the outline is _greater_ than the cut_in value, the */ /* outline measurement is used. */ /* */ /* This is from `instgly.doc'. The description in */ /* `ttinst2.doc', version 1.66, is thus incorrect since */ /* it implies `>=' instead of `>'. */ if ( FT_ABS( cvt_dist - org_dist ) > CUR.GS.control_value_cutin ) cvt_dist = org_dist; } distance = CUR_Func_round( cvt_dist, CUR.tt_metrics.compensations[CUR.opcode & 3] ); } else distance = ROUND_None( cvt_dist, CUR.tt_metrics.compensations[CUR.opcode & 3] ); /* minimum distance test */ if ( ( CUR.opcode & 8 ) != 0 ) { if ( org_dist >= 0 ) { if ( distance < CUR.GS.minimum_distance ) distance = CUR.GS.minimum_distance; } else { if ( distance > -CUR.GS.minimum_distance ) distance = -CUR.GS.minimum_distance; } } CUR_Func_move( &CUR.zp1, point, distance - cur_dist ); Fail: CUR.GS.rp1 = CUR.GS.rp0; if ( ( CUR.opcode & 16 ) != 0 ) CUR.GS.rp0 = point; CUR.GS.rp2 = point; } /*************************************************************************/ /* */ /* ALIGNRP[]: ALIGN Relative Point */ /* Opcode range: 0x3C */ /* Stack: uint32 uint32... --> */ /* */ static void Ins_ALIGNRP( INS_ARG ) { FT_UShort point; FT_F26Dot6 distance; FT_UNUSED_ARG; if ( CUR.top < CUR.GS.loop || BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; goto Fail; } while ( CUR.GS.loop > 0 ) { CUR.args--; point = (FT_UShort)CUR.stack[CUR.args]; if ( BOUNDS( point, CUR.zp1.n_points ) ) { if ( CUR.pedantic_hinting ) { CUR.error = TT_Err_Invalid_Reference; return; } } else { distance = CUR_Func_project( CUR.zp1.cur + point, CUR.zp0.cur + CUR.GS.rp0 ); CUR_Func_move( &CUR.zp1, point, -distance ); } CUR.GS.loop--; } Fail: CUR.GS.loop = 1; CUR.new_top = CUR.args; } /*************************************************************************/ /* */ /* ISECT[]: moves point to InterSECTion */ /* Opcode range: 0x0F */ /* Stack: 5 * uint32 --> */ /* */ static void Ins_ISECT( INS_ARG ) { FT_UShort point, a0, a1, b0, b1; FT_F26Dot6 discriminant; FT_F26Dot6 dx, dy, dax, day, dbx, dby; FT_F26Dot6 val; FT_Vector R; point = (FT_UShort)args[0]; a0 = (FT_UShort)args[1]; a1 = (FT_UShort)args[2]; b0 = (FT_UShort)args[3]; b1 = (FT_UShort)args[4]; if ( BOUNDS( b0, CUR.zp0.n_points ) || BOUNDS( b1, CUR.zp0.n_points ) || BOUNDS( a0, CUR.zp1.n_points ) || BOUNDS( a1, CUR.zp1.n_points ) || BOUNDS( point, CUR.zp2.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } dbx = CUR.zp0.cur[b1].x - CUR.zp0.cur[b0].x; dby = CUR.zp0.cur[b1].y - CUR.zp0.cur[b0].y; dax = CUR.zp1.cur[a1].x - CUR.zp1.cur[a0].x; day = CUR.zp1.cur[a1].y - CUR.zp1.cur[a0].y; dx = CUR.zp0.cur[b0].x - CUR.zp1.cur[a0].x; dy = CUR.zp0.cur[b0].y - CUR.zp1.cur[a0].y; CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_BOTH; discriminant = TT_MULDIV( dax, -dby, 0x40 ) + TT_MULDIV( day, dbx, 0x40 ); if ( FT_ABS( discriminant ) >= 0x40 ) { val = TT_MULDIV( dx, -dby, 0x40 ) + TT_MULDIV( dy, dbx, 0x40 ); R.x = TT_MULDIV( val, dax, discriminant ); R.y = TT_MULDIV( val, day, discriminant ); CUR.zp2.cur[point].x = CUR.zp1.cur[a0].x + R.x; CUR.zp2.cur[point].y = CUR.zp1.cur[a0].y + R.y; } else { /* else, take the middle of the middles of A and B */ CUR.zp2.cur[point].x = ( CUR.zp1.cur[a0].x + CUR.zp1.cur[a1].x + CUR.zp0.cur[b0].x + CUR.zp0.cur[b1].x ) / 4; CUR.zp2.cur[point].y = ( CUR.zp1.cur[a0].y + CUR.zp1.cur[a1].y + CUR.zp0.cur[b0].y + CUR.zp0.cur[b1].y ) / 4; } } /*************************************************************************/ /* */ /* ALIGNPTS[]: ALIGN PoinTS */ /* Opcode range: 0x27 */ /* Stack: uint32 uint32 --> */ /* */ static void Ins_ALIGNPTS( INS_ARG ) { FT_UShort p1, p2; FT_F26Dot6 distance; p1 = (FT_UShort)args[0]; p2 = (FT_UShort)args[1]; if ( BOUNDS( p1, CUR.zp1.n_points ) || BOUNDS( p2, CUR.zp0.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } distance = CUR_Func_project( CUR.zp0.cur + p2, CUR.zp1.cur + p1 ) / 2; CUR_Func_move( &CUR.zp1, p1, distance ); CUR_Func_move( &CUR.zp0, p2, -distance ); } /*************************************************************************/ /* */ /* IP[]: Interpolate Point */ /* Opcode range: 0x39 */ /* Stack: uint32... --> */ /* */ /* SOMETIMES, DUMBER CODE IS BETTER CODE */ static void Ins_IP( INS_ARG ) { FT_F26Dot6 old_range, cur_range; FT_Vector* orus_base; FT_Vector* cur_base; FT_Int twilight; FT_UNUSED_ARG; if ( CUR.top < CUR.GS.loop ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; goto Fail; } /* * We need to deal in a special way with the twilight zone. * Otherwise, by definition, the value of CUR.twilight.orus[n] is (0,0), * for every n. */ twilight = CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0 || CUR.GS.gep2 == 0; if ( BOUNDS( CUR.GS.rp1, CUR.zp0.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; goto Fail; } if ( twilight ) orus_base = &CUR.zp0.org[CUR.GS.rp1]; else orus_base = &CUR.zp0.orus[CUR.GS.rp1]; cur_base = &CUR.zp0.cur[CUR.GS.rp1]; /* XXX: There are some glyphs in some braindead but popular */ /* fonts out there (e.g. [aeu]grave in monotype.ttf) */ /* calling IP[] with bad values of rp[12]. */ /* Do something sane when this odd thing happens. */ if ( BOUNDS( CUR.GS.rp1, CUR.zp0.n_points ) || BOUNDS( CUR.GS.rp2, CUR.zp1.n_points ) ) { old_range = 0; cur_range = 0; } else { if ( twilight ) old_range = CUR_Func_dualproj( &CUR.zp1.org[CUR.GS.rp2], orus_base ); else old_range = CUR_Func_dualproj( &CUR.zp1.orus[CUR.GS.rp2], orus_base ); cur_range = CUR_Func_project ( &CUR.zp1.cur[CUR.GS.rp2], cur_base ); } for ( ; CUR.GS.loop > 0; --CUR.GS.loop ) { FT_UInt point = (FT_UInt)CUR.stack[--CUR.args]; FT_F26Dot6 org_dist, cur_dist, new_dist; /* check point bounds */ if ( BOUNDS( point, CUR.zp2.n_points ) ) { if ( CUR.pedantic_hinting ) { CUR.error = TT_Err_Invalid_Reference; return; } continue; } if ( twilight ) org_dist = CUR_Func_dualproj( &CUR.zp2.org[point], orus_base ); else org_dist = CUR_Func_dualproj( &CUR.zp2.orus[point], orus_base ); cur_dist = CUR_Func_project ( &CUR.zp2.cur[point], cur_base ); if ( org_dist ) new_dist = ( old_range != 0 ) ? TT_MULDIV( org_dist, cur_range, old_range ) : cur_dist; else new_dist = 0; CUR_Func_move( &CUR.zp2, (FT_UShort)point, new_dist - cur_dist ); } Fail: CUR.GS.loop = 1; CUR.new_top = CUR.args; } /*************************************************************************/ /* */ /* UTP[a]: UnTouch Point */ /* Opcode range: 0x29 */ /* Stack: uint32 --> */ /* */ static void Ins_UTP( INS_ARG ) { FT_UShort point; FT_Byte mask; point = (FT_UShort)args[0]; if ( BOUNDS( point, CUR.zp0.n_points ) ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; return; } mask = 0xFF; if ( CUR.GS.freeVector.x != 0 ) mask &= ~FT_CURVE_TAG_TOUCH_X; if ( CUR.GS.freeVector.y != 0 ) mask &= ~FT_CURVE_TAG_TOUCH_Y; CUR.zp0.tags[point] &= mask; } /* Local variables for Ins_IUP: */ typedef struct IUP_WorkerRec_ { FT_Vector* orgs; /* original and current coordinate */ FT_Vector* curs; /* arrays */ FT_Vector* orus; FT_UInt max_points; } IUP_WorkerRec, *IUP_Worker; static void _iup_worker_shift( IUP_Worker worker, FT_UInt p1, FT_UInt p2, FT_UInt p ) { FT_UInt i; FT_F26Dot6 dx; dx = worker->curs[p].x - worker->orgs[p].x; if ( dx != 0 ) { for ( i = p1; i < p; i++ ) worker->curs[i].x += dx; for ( i = p + 1; i <= p2; i++ ) worker->curs[i].x += dx; } } static void _iup_worker_interpolate( IUP_Worker worker, FT_UInt p1, FT_UInt p2, FT_UInt ref1, FT_UInt ref2 ) { FT_UInt i; FT_F26Dot6 orus1, orus2, org1, org2, delta1, delta2; if ( p1 > p2 ) return; if ( BOUNDS( ref1, worker->max_points ) || BOUNDS( ref2, worker->max_points ) ) return; orus1 = worker->orus[ref1].x; orus2 = worker->orus[ref2].x; if ( orus1 > orus2 ) { FT_F26Dot6 tmp_o; FT_UInt tmp_r; tmp_o = orus1; orus1 = orus2; orus2 = tmp_o; tmp_r = ref1; ref1 = ref2; ref2 = tmp_r; } org1 = worker->orgs[ref1].x; org2 = worker->orgs[ref2].x; delta1 = worker->curs[ref1].x - org1; delta2 = worker->curs[ref2].x - org2; if ( orus1 == orus2 ) { /* simple shift of untouched points */ for ( i = p1; i <= p2; i++ ) { FT_F26Dot6 x = worker->orgs[i].x; if ( x <= org1 ) x += delta1; else x += delta2; worker->curs[i].x = x; } } else { FT_Fixed scale = 0; FT_Bool scale_valid = 0; /* interpolation */ for ( i = p1; i <= p2; i++ ) { FT_F26Dot6 x = worker->orgs[i].x; if ( x <= org1 ) x += delta1; else if ( x >= org2 ) x += delta2; else { if ( !scale_valid ) { scale_valid = 1; scale = TT_MULDIV( org2 + delta2 - ( org1 + delta1 ), 0x10000L, orus2 - orus1 ); } x = ( org1 + delta1 ) + TT_MULFIX( worker->orus[i].x - orus1, scale ); } worker->curs[i].x = x; } } } /*************************************************************************/ /* */ /* IUP[a]: Interpolate Untouched Points */ /* Opcode range: 0x30-0x31 */ /* Stack: --> */ /* */ static void Ins_IUP( INS_ARG ) { IUP_WorkerRec V; FT_Byte mask; FT_UInt first_point; /* first point of contour */ FT_UInt end_point; /* end point (last+1) of contour */ FT_UInt first_touched; /* first touched point in contour */ FT_UInt cur_touched; /* current touched point in contour */ FT_UInt point; /* current point */ FT_Short contour; /* current contour */ FT_UNUSED_ARG; /* ignore empty outlines */ if ( CUR.pts.n_contours == 0 ) return; if ( CUR.opcode & 1 ) { mask = FT_CURVE_TAG_TOUCH_X; V.orgs = CUR.pts.org; V.curs = CUR.pts.cur; V.orus = CUR.pts.orus; } else { mask = FT_CURVE_TAG_TOUCH_Y; V.orgs = (FT_Vector*)( (FT_Pos*)CUR.pts.org + 1 ); V.curs = (FT_Vector*)( (FT_Pos*)CUR.pts.cur + 1 ); V.orus = (FT_Vector*)( (FT_Pos*)CUR.pts.orus + 1 ); } V.max_points = CUR.pts.n_points; contour = 0; point = 0; do { end_point = CUR.pts.contours[contour] - CUR.pts.first_point; first_point = point; if ( BOUNDS ( end_point, CUR.pts.n_points ) ) end_point = CUR.pts.n_points - 1; while ( point <= end_point && ( CUR.pts.tags[point] & mask ) == 0 ) point++; if ( point <= end_point ) { first_touched = point; cur_touched = point; point++; while ( point <= end_point ) { if ( ( CUR.pts.tags[point] & mask ) != 0 ) { _iup_worker_interpolate( &V, cur_touched + 1, point - 1, cur_touched, point ); cur_touched = point; } point++; } if ( cur_touched == first_touched ) _iup_worker_shift( &V, first_point, end_point, cur_touched ); else { _iup_worker_interpolate( &V, (FT_UShort)( cur_touched + 1 ), end_point, cur_touched, first_touched ); if ( first_touched > 0 ) _iup_worker_interpolate( &V, first_point, first_touched - 1, cur_touched, first_touched ); } } contour++; } while ( contour < CUR.pts.n_contours ); } /*************************************************************************/ /* */ /* DELTAPn[]: DELTA exceptions P1, P2, P3 */ /* Opcode range: 0x5D,0x71,0x72 */ /* Stack: uint32 (2 * uint32)... --> */ /* */ static void Ins_DELTAP( INS_ARG ) { FT_ULong k, nump; FT_UShort A; FT_ULong C; FT_Long B; #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING /* Delta hinting is covered by US Patent 5159668. */ if ( CUR.face->unpatented_hinting ) { FT_Long n = args[0] * 2; if ( CUR.args < n ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Too_Few_Arguments; n = CUR.args; } CUR.args -= n; CUR.new_top = CUR.args; return; } #endif nump = (FT_ULong)args[0]; /* some points theoretically may occur more than once, thus UShort isn't enough */ for ( k = 1; k <= nump; k++ ) { if ( CUR.args < 2 ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Too_Few_Arguments; CUR.args = 0; goto Fail; } CUR.args -= 2; A = (FT_UShort)CUR.stack[CUR.args + 1]; B = CUR.stack[CUR.args]; /* XXX: Because some popular fonts contain some invalid DeltaP */ /* instructions, we simply ignore them when the stacked */ /* point reference is off limit, rather than returning an */ /* error. As a delta instruction doesn't change a glyph */ /* in great ways, this shouldn't be a problem. */ if ( !BOUNDS( A, CUR.zp0.n_points ) ) { C = ( (FT_ULong)B & 0xF0 ) >> 4; switch ( CUR.opcode ) { case 0x5D: break; case 0x71: C += 16; break; case 0x72: C += 32; break; } C += CUR.GS.delta_base; if ( CURRENT_Ppem() == (FT_Long)C ) { B = ( (FT_ULong)B & 0xF ) - 8; if ( B >= 0 ) B++; B = B * 64 / ( 1L << CUR.GS.delta_shift ); CUR_Func_move( &CUR.zp0, A, B ); } } else if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Invalid_Reference; } Fail: CUR.new_top = CUR.args; } /*************************************************************************/ /* */ /* DELTACn[]: DELTA exceptions C1, C2, C3 */ /* Opcode range: 0x73,0x74,0x75 */ /* Stack: uint32 (2 * uint32)... --> */ /* */ static void Ins_DELTAC( INS_ARG ) { FT_ULong nump, k; FT_ULong A, C; FT_Long B; #ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING /* Delta hinting is covered by US Patent 5159668. */ if ( CUR.face->unpatented_hinting ) { FT_Long n = args[0] * 2; if ( CUR.args < n ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Too_Few_Arguments; n = CUR.args; } CUR.args -= n; CUR.new_top = CUR.args; return; } #endif nump = (FT_ULong)args[0]; for ( k = 1; k <= nump; k++ ) { if ( CUR.args < 2 ) { if ( CUR.pedantic_hinting ) CUR.error = TT_Err_Too_Few_Arguments; CUR.args = 0; goto Fail; } CUR.args -= 2; A = (FT_ULong)CUR.stack[CUR.args + 1]; B = CUR.stack[CUR.args]; if ( BOUNDSL( A, CUR.cvtSize ) ) { if ( CUR.pedantic_hinting ) { CUR.error = TT_Err_Invalid_Reference; return; } } else { C = ( (FT_ULong)B & 0xF0 ) >> 4; switch ( CUR.opcode ) { case 0x73: break; case 0x74: C += 16; break; case 0x75: C += 32; break; } C += CUR.GS.delta_base; if ( CURRENT_Ppem() == (FT_Long)C ) { B = ( (FT_ULong)B & 0xF ) - 8; if ( B >= 0 ) B++; B = B * 64 / ( 1L << CUR.GS.delta_shift ); CUR_Func_move_cvt( A, B ); } } } Fail: CUR.new_top = CUR.args; } /*************************************************************************/ /* */ /* MISC. INSTRUCTIONS */ /* */ /*************************************************************************/ /*************************************************************************/ /* */ /* GETINFO[]: GET INFOrmation */ /* Opcode range: 0x88 */ /* Stack: uint32 --> uint32 */ /* */ static void Ins_GETINFO( INS_ARG ) { FT_Long K; K = 0; /* We return MS rasterizer version 1.7 for the font scaler. */ if ( ( args[0] & 1 ) != 0 ) K = 35; /* Has the glyph been rotated? */ if ( ( args[0] & 2 ) != 0 && CUR.tt_metrics.rotated ) K |= 0x80; /* Has the glyph been stretched? */ if ( ( args[0] & 4 ) != 0 && CUR.tt_metrics.stretched ) K |= 1 << 8; /* Are we hinting for grayscale? */ if ( ( args[0] & 32 ) != 0 && CUR.grayscale ) K |= 1 << 12; args[0] = K; } static void Ins_UNKNOWN( INS_ARG ) { TT_DefRecord* def = CUR.IDefs; TT_DefRecord* limit = def + CUR.numIDefs; FT_UNUSED_ARG; for ( ; def < limit; def++ ) { if ( (FT_Byte)def->opc == CUR.opcode && def->active ) { TT_CallRec* call; if ( CUR.callTop >= CUR.callSize ) { CUR.error = TT_Err_Stack_Overflow; return; } call = CUR.callStack + CUR.callTop++; call->Caller_Range = CUR.curRange; call->Caller_IP = CUR.IP + 1; call->Cur_Count = 1; call->Cur_Restart = def->start; INS_Goto_CodeRange( def->range, def->start ); CUR.step_ins = FALSE; return; } } CUR.error = TT_Err_Invalid_Opcode; } #ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH static TInstruction_Function Instruct_Dispatch[256] = { /* Opcodes are gathered in groups of 16. */ /* Please keep the spaces as they are. */ /* SVTCA y */ Ins_SVTCA, /* SVTCA x */ Ins_SVTCA, /* SPvTCA y */ Ins_SPVTCA, /* SPvTCA x */ Ins_SPVTCA, /* SFvTCA y */ Ins_SFVTCA, /* SFvTCA x */ Ins_SFVTCA, /* SPvTL // */ Ins_SPVTL, /* SPvTL + */ Ins_SPVTL, /* SFvTL // */ Ins_SFVTL, /* SFvTL + */ Ins_SFVTL, /* SPvFS */ Ins_SPVFS, /* SFvFS */ Ins_SFVFS, /* GPV */ Ins_GPV, /* GFV */ Ins_GFV, /* SFvTPv */ Ins_SFVTPV, /* ISECT */ Ins_ISECT, /* SRP0 */ Ins_SRP0, /* SRP1 */ Ins_SRP1, /* SRP2 */ Ins_SRP2, /* SZP0 */ Ins_SZP0, /* SZP1 */ Ins_SZP1, /* SZP2 */ Ins_SZP2, /* SZPS */ Ins_SZPS, /* SLOOP */ Ins_SLOOP, /* RTG */ Ins_RTG, /* RTHG */ Ins_RTHG, /* SMD */ Ins_SMD, /* ELSE */ Ins_ELSE, /* JMPR */ Ins_JMPR, /* SCvTCi */ Ins_SCVTCI, /* SSwCi */ Ins_SSWCI, /* SSW */ Ins_SSW, /* DUP */ Ins_DUP, /* POP */ Ins_POP, /* CLEAR */ Ins_CLEAR, /* SWAP */ Ins_SWAP, /* DEPTH */ Ins_DEPTH, /* CINDEX */ Ins_CINDEX, /* MINDEX */ Ins_MINDEX, /* AlignPTS */ Ins_ALIGNPTS, /* INS_0x28 */ Ins_UNKNOWN, /* UTP */ Ins_UTP, /* LOOPCALL */ Ins_LOOPCALL, /* CALL */ Ins_CALL, /* FDEF */ Ins_FDEF, /* ENDF */ Ins_ENDF, /* MDAP[0] */ Ins_MDAP, /* MDAP[1] */ Ins_MDAP, /* IUP[0] */ Ins_IUP, /* IUP[1] */ Ins_IUP, /* SHP[0] */ Ins_SHP, /* SHP[1] */ Ins_SHP, /* SHC[0] */ Ins_SHC, /* SHC[1] */ Ins_SHC, /* SHZ[0] */ Ins_SHZ, /* SHZ[1] */ Ins_SHZ, /* SHPIX */ Ins_SHPIX, /* IP */ Ins_IP, /* MSIRP[0] */ Ins_MSIRP, /* MSIRP[1] */ Ins_MSIRP, /* AlignRP */ Ins_ALIGNRP, /* RTDG */ Ins_RTDG, /* MIAP[0] */ Ins_MIAP, /* MIAP[1] */ Ins_MIAP, /* NPushB */ Ins_NPUSHB, /* NPushW */ Ins_NPUSHW, /* WS */ Ins_WS, /* RS */ Ins_RS, /* WCvtP */ Ins_WCVTP, /* RCvt */ Ins_RCVT, /* GC[0] */ Ins_GC, /* GC[1] */ Ins_GC, /* SCFS */ Ins_SCFS, /* MD[0] */ Ins_MD, /* MD[1] */ Ins_MD, /* MPPEM */ Ins_MPPEM, /* MPS */ Ins_MPS, /* FlipON */ Ins_FLIPON, /* FlipOFF */ Ins_FLIPOFF, /* DEBUG */ Ins_DEBUG, /* LT */ Ins_LT, /* LTEQ */ Ins_LTEQ, /* GT */ Ins_GT, /* GTEQ */ Ins_GTEQ, /* EQ */ Ins_EQ, /* NEQ */ Ins_NEQ, /* ODD */ Ins_ODD, /* EVEN */ Ins_EVEN, /* IF */ Ins_IF, /* EIF */ Ins_EIF, /* AND */ Ins_AND, /* OR */ Ins_OR, /* NOT */ Ins_NOT, /* DeltaP1 */ Ins_DELTAP, /* SDB */ Ins_SDB, /* SDS */ Ins_SDS, /* ADD */ Ins_ADD, /* SUB */ Ins_SUB, /* DIV */ Ins_DIV, /* MUL */ Ins_MUL, /* ABS */ Ins_ABS, /* NEG */ Ins_NEG, /* FLOOR */ Ins_FLOOR, /* CEILING */ Ins_CEILING, /* ROUND[0] */ Ins_ROUND, /* ROUND[1] */ Ins_ROUND, /* ROUND[2] */ Ins_ROUND, /* ROUND[3] */ Ins_ROUND, /* NROUND[0] */ Ins_NROUND, /* NROUND[1] */ Ins_NROUND, /* NROUND[2] */ Ins_NROUND, /* NROUND[3] */ Ins_NROUND, /* WCvtF */ Ins_WCVTF, /* DeltaP2 */ Ins_DELTAP, /* DeltaP3 */ Ins_DELTAP, /* DeltaCn[0] */ Ins_DELTAC, /* DeltaCn[1] */ Ins_DELTAC, /* DeltaCn[2] */ Ins_DELTAC, /* SROUND */ Ins_SROUND, /* S45Round */ Ins_S45ROUND, /* JROT */ Ins_JROT, /* JROF */ Ins_JROF, /* ROFF */ Ins_ROFF, /* INS_0x7B */ Ins_UNKNOWN, /* RUTG */ Ins_RUTG, /* RDTG */ Ins_RDTG, /* SANGW */ Ins_SANGW, /* AA */ Ins_AA, /* FlipPT */ Ins_FLIPPT, /* FlipRgON */ Ins_FLIPRGON, /* FlipRgOFF */ Ins_FLIPRGOFF, /* INS_0x83 */ Ins_UNKNOWN, /* INS_0x84 */ Ins_UNKNOWN, /* ScanCTRL */ Ins_SCANCTRL, /* SDPVTL[0] */ Ins_SDPVTL, /* SDPVTL[1] */ Ins_SDPVTL, /* GetINFO */ Ins_GETINFO, /* IDEF */ Ins_IDEF, /* ROLL */ Ins_ROLL, /* MAX */ Ins_MAX, /* MIN */ Ins_MIN, /* ScanTYPE */ Ins_SCANTYPE, /* InstCTRL */ Ins_INSTCTRL, /* INS_0x8F */ Ins_UNKNOWN, /* INS_0x90 */ Ins_UNKNOWN, /* INS_0x91 */ Ins_UNKNOWN, /* INS_0x92 */ Ins_UNKNOWN, /* INS_0x93 */ Ins_UNKNOWN, /* INS_0x94 */ Ins_UNKNOWN, /* INS_0x95 */ Ins_UNKNOWN, /* INS_0x96 */ Ins_UNKNOWN, /* INS_0x97 */ Ins_UNKNOWN, /* INS_0x98 */ Ins_UNKNOWN, /* INS_0x99 */ Ins_UNKNOWN, /* INS_0x9A */ Ins_UNKNOWN, /* INS_0x9B */ Ins_UNKNOWN, /* INS_0x9C */ Ins_UNKNOWN, /* INS_0x9D */ Ins_UNKNOWN, /* INS_0x9E */ Ins_UNKNOWN, /* INS_0x9F */ Ins_UNKNOWN, /* INS_0xA0 */ Ins_UNKNOWN, /* INS_0xA1 */ Ins_UNKNOWN, /* INS_0xA2 */ Ins_UNKNOWN, /* INS_0xA3 */ Ins_UNKNOWN, /* INS_0xA4 */ Ins_UNKNOWN, /* INS_0xA5 */ Ins_UNKNOWN, /* INS_0xA6 */ Ins_UNKNOWN, /* INS_0xA7 */ Ins_UNKNOWN, /* INS_0xA8 */ Ins_UNKNOWN, /* INS_0xA9 */ Ins_UNKNOWN, /* INS_0xAA */ Ins_UNKNOWN, /* INS_0xAB */ Ins_UNKNOWN, /* INS_0xAC */ Ins_UNKNOWN, /* INS_0xAD */ Ins_UNKNOWN, /* INS_0xAE */ Ins_UNKNOWN, /* INS_0xAF */ Ins_UNKNOWN, /* PushB[0] */ Ins_PUSHB, /* PushB[1] */ Ins_PUSHB, /* PushB[2] */ Ins_PUSHB, /* PushB[3] */ Ins_PUSHB, /* PushB[4] */ Ins_PUSHB, /* PushB[5] */ Ins_PUSHB, /* PushB[6] */ Ins_PUSHB, /* PushB[7] */ Ins_PUSHB, /* PushW[0] */ Ins_PUSHW, /* PushW[1] */ Ins_PUSHW, /* PushW[2] */ Ins_PUSHW, /* PushW[3] */ Ins_PUSHW, /* PushW[4] */ Ins_PUSHW, /* PushW[5] */ Ins_PUSHW, /* PushW[6] */ Ins_PUSHW, /* PushW[7] */ Ins_PUSHW, /* MDRP[00] */ Ins_MDRP, /* MDRP[01] */ Ins_MDRP, /* MDRP[02] */ Ins_MDRP, /* MDRP[03] */ Ins_MDRP, /* MDRP[04] */ Ins_MDRP, /* MDRP[05] */ Ins_MDRP, /* MDRP[06] */ Ins_MDRP, /* MDRP[07] */ Ins_MDRP, /* MDRP[08] */ Ins_MDRP, /* MDRP[09] */ Ins_MDRP, /* MDRP[10] */ Ins_MDRP, /* MDRP[11] */ Ins_MDRP, /* MDRP[12] */ Ins_MDRP, /* MDRP[13] */ Ins_MDRP, /* MDRP[14] */ Ins_MDRP, /* MDRP[15] */ Ins_MDRP, /* MDRP[16] */ Ins_MDRP, /* MDRP[17] */ Ins_MDRP, /* MDRP[18] */ Ins_MDRP, /* MDRP[19] */ Ins_MDRP, /* MDRP[20] */ Ins_MDRP, /* MDRP[21] */ Ins_MDRP, /* MDRP[22] */ Ins_MDRP, /* MDRP[23] */ Ins_MDRP, /* MDRP[24] */ Ins_MDRP, /* MDRP[25] */ Ins_MDRP, /* MDRP[26] */ Ins_MDRP, /* MDRP[27] */ Ins_MDRP, /* MDRP[28] */ Ins_MDRP, /* MDRP[29] */ Ins_MDRP, /* MDRP[30] */ Ins_MDRP, /* MDRP[31] */ Ins_MDRP, /* MIRP[00] */ Ins_MIRP, /* MIRP[01] */ Ins_MIRP, /* MIRP[02] */ Ins_MIRP, /* MIRP[03] */ Ins_MIRP, /* MIRP[04] */ Ins_MIRP, /* MIRP[05] */ Ins_MIRP, /* MIRP[06] */ Ins_MIRP, /* MIRP[07] */ Ins_MIRP, /* MIRP[08] */ Ins_MIRP, /* MIRP[09] */ Ins_MIRP, /* MIRP[10] */ Ins_MIRP, /* MIRP[11] */ Ins_MIRP, /* MIRP[12] */ Ins_MIRP, /* MIRP[13] */ Ins_MIRP, /* MIRP[14] */ Ins_MIRP, /* MIRP[15] */ Ins_MIRP, /* MIRP[16] */ Ins_MIRP, /* MIRP[17] */ Ins_MIRP, /* MIRP[18] */ Ins_MIRP, /* MIRP[19] */ Ins_MIRP, /* MIRP[20] */ Ins_MIRP, /* MIRP[21] */ Ins_MIRP, /* MIRP[22] */ Ins_MIRP, /* MIRP[23] */ Ins_MIRP, /* MIRP[24] */ Ins_MIRP, /* MIRP[25] */ Ins_MIRP, /* MIRP[26] */ Ins_MIRP, /* MIRP[27] */ Ins_MIRP, /* MIRP[28] */ Ins_MIRP, /* MIRP[29] */ Ins_MIRP, /* MIRP[30] */ Ins_MIRP, /* MIRP[31] */ Ins_MIRP }; #endif /* !TT_CONFIG_OPTION_INTERPRETER_SWITCH */ /*************************************************************************/ /* */ /* RUN */ /* */ /* This function executes a run of opcodes. It will exit in the */ /* following cases: */ /* */ /* - Errors (in which case it returns FALSE). */ /* */ /* - Reaching the end of the main code range (returns TRUE). */ /* Reaching the end of a code range within a function call is an */ /* error. */ /* */ /* - After executing one single opcode, if the flag `Instruction_Trap' */ /* is set to TRUE (returns TRUE). */ /* */ /* On exit with TRUE, test IP < CodeSize to know whether it comes from */ /* an instruction trap or a normal termination. */ /* */ /* */ /* Note: The documented DEBUG opcode pops a value from the stack. This */ /* behaviour is unsupported; here a DEBUG opcode is always an */ /* error. */ /* */ /* */ /* THIS IS THE INTERPRETER'S MAIN LOOP. */ /* */ /* Instructions appear in the specification's order. */ /* */ /*************************************************************************/ /* documentation is in ttinterp.h */ FT_EXPORT_DEF( FT_Error ) TT_RunIns( TT_ExecContext exc ) { FT_Long ins_counter = 0; /* executed instructions counter */ #ifdef TT_CONFIG_OPTION_STATIC_RASTER cur = *exc; #endif /* set CVT functions */ CUR.tt_metrics.ratio = 0; if ( CUR.metrics.x_ppem != CUR.metrics.y_ppem ) { /* non-square pixels, use the stretched routines */ CUR.func_read_cvt = Read_CVT_Stretched; CUR.func_write_cvt = Write_CVT_Stretched; CUR.func_move_cvt = Move_CVT_Stretched; } else { /* square pixels, use normal routines */ CUR.func_read_cvt = Read_CVT; CUR.func_write_cvt = Write_CVT; CUR.func_move_cvt = Move_CVT; } COMPUTE_Funcs(); COMPUTE_Round( (FT_Byte)exc->GS.round_state ); do { CUR.opcode = CUR.code[CUR.IP]; FT_TRACE7(( " " )); FT_TRACE7(( opcode_name[CUR.opcode] )); FT_TRACE7(( "\n" )); if ( ( CUR.length = opcode_length[CUR.opcode] ) < 0 ) { if ( CUR.IP + 1 > CUR.codeSize ) goto LErrorCodeOverflow_; CUR.length = 2 - CUR.length * CUR.code[CUR.IP + 1]; } if ( CUR.IP + CUR.length > CUR.codeSize ) goto LErrorCodeOverflow_; /* First, let's check for empty stack and overflow */ CUR.args = CUR.top - ( Pop_Push_Count[CUR.opcode] >> 4 ); /* `args' is the top of the stack once arguments have been popped. */ /* One can also interpret it as the index of the last argument. */ if ( CUR.args < 0 ) { FT_UShort i; if ( CUR.pedantic_hinting ) { CUR.error = TT_Err_Too_Few_Arguments; goto LErrorLabel_; } /* push zeroes onto the stack */ for ( i = 0; i < Pop_Push_Count[CUR.opcode] >> 4; i++ ) CUR.stack[i] = 0; CUR.args = 0; } CUR.new_top = CUR.args + ( Pop_Push_Count[CUR.opcode] & 15 ); /* `new_top' is the new top of the stack, after the instruction's */ /* execution. `top' will be set to `new_top' after the `switch' */ /* statement. */ if ( CUR.new_top > CUR.stackSize ) { CUR.error = TT_Err_Stack_Overflow; goto LErrorLabel_; } CUR.step_ins = TRUE; CUR.error = TT_Err_Ok; #ifdef TT_CONFIG_OPTION_INTERPRETER_SWITCH { FT_Long* args = CUR.stack + CUR.args; FT_Byte opcode = CUR.opcode; #undef ARRAY_BOUND_ERROR #define ARRAY_BOUND_ERROR goto Set_Invalid_Ref switch ( opcode ) { case 0x00: /* SVTCA y */ case 0x01: /* SVTCA x */ case 0x02: /* SPvTCA y */ case 0x03: /* SPvTCA x */ case 0x04: /* SFvTCA y */ case 0x05: /* SFvTCA x */ { FT_Short AA, BB; AA = (FT_Short)( ( opcode & 1 ) << 14 ); BB = (FT_Short)( AA ^ 0x4000 ); if ( opcode < 4 ) { CUR.GS.projVector.x = AA; CUR.GS.projVector.y = BB; CUR.GS.dualVector.x = AA; CUR.GS.dualVector.y = BB; } else { GUESS_VECTOR( projVector ); } if ( ( opcode & 2 ) == 0 ) { CUR.GS.freeVector.x = AA; CUR.GS.freeVector.y = BB; } else { GUESS_VECTOR( freeVector ); } COMPUTE_Funcs(); } break; case 0x06: /* SPvTL // */ case 0x07: /* SPvTL + */ DO_SPVTL break; case 0x08: /* SFvTL // */ case 0x09: /* SFvTL + */ DO_SFVTL break; case 0x0A: /* SPvFS */ DO_SPVFS break; case 0x0B: /* SFvFS */ DO_SFVFS break; case 0x0C: /* GPV */ DO_GPV break; case 0x0D: /* GFV */ DO_GFV break; case 0x0E: /* SFvTPv */ DO_SFVTPV break; case 0x0F: /* ISECT */ Ins_ISECT( EXEC_ARG_ args ); break; case 0x10: /* SRP0 */ DO_SRP0 break; case 0x11: /* SRP1 */ DO_SRP1 break; case 0x12: /* SRP2 */ DO_SRP2 break; case 0x13: /* SZP0 */ Ins_SZP0( EXEC_ARG_ args ); break; case 0x14: /* SZP1 */ Ins_SZP1( EXEC_ARG_ args ); break; case 0x15: /* SZP2 */ Ins_SZP2( EXEC_ARG_ args ); break; case 0x16: /* SZPS */ Ins_SZPS( EXEC_ARG_ args ); break; case 0x17: /* SLOOP */ DO_SLOOP break; case 0x18: /* RTG */ DO_RTG break; case 0x19: /* RTHG */ DO_RTHG break; case 0x1A: /* SMD */ DO_SMD break; case 0x1B: /* ELSE */ Ins_ELSE( EXEC_ARG_ args ); break; case 0x1C: /* JMPR */ DO_JMPR break; case 0x1D: /* SCVTCI */ DO_SCVTCI break; case 0x1E: /* SSWCI */ DO_SSWCI break; case 0x1F: /* SSW */ DO_SSW break; case 0x20: /* DUP */ DO_DUP break; case 0x21: /* POP */ /* nothing :-) */ break; case 0x22: /* CLEAR */ DO_CLEAR break; case 0x23: /* SWAP */ DO_SWAP break; case 0x24: /* DEPTH */ DO_DEPTH break; case 0x25: /* CINDEX */ DO_CINDEX break; case 0x26: /* MINDEX */ Ins_MINDEX( EXEC_ARG_ args ); break; case 0x27: /* ALIGNPTS */ Ins_ALIGNPTS( EXEC_ARG_ args ); break; case 0x28: /* ???? */ Ins_UNKNOWN( EXEC_ARG_ args ); break; case 0x29: /* UTP */ Ins_UTP( EXEC_ARG_ args ); break; case 0x2A: /* LOOPCALL */ Ins_LOOPCALL( EXEC_ARG_ args ); break; case 0x2B: /* CALL */ Ins_CALL( EXEC_ARG_ args ); break; case 0x2C: /* FDEF */ Ins_FDEF( EXEC_ARG_ args ); break; case 0x2D: /* ENDF */ Ins_ENDF( EXEC_ARG_ args ); break; case 0x2E: /* MDAP */ case 0x2F: /* MDAP */ Ins_MDAP( EXEC_ARG_ args ); break; case 0x30: /* IUP */ case 0x31: /* IUP */ Ins_IUP( EXEC_ARG_ args ); break; case 0x32: /* SHP */ case 0x33: /* SHP */ Ins_SHP( EXEC_ARG_ args ); break; case 0x34: /* SHC */ case 0x35: /* SHC */ Ins_SHC( EXEC_ARG_ args ); break; case 0x36: /* SHZ */ case 0x37: /* SHZ */ Ins_SHZ( EXEC_ARG_ args ); break; case 0x38: /* SHPIX */ Ins_SHPIX( EXEC_ARG_ args ); break; case 0x39: /* IP */ Ins_IP( EXEC_ARG_ args ); break; case 0x3A: /* MSIRP */ case 0x3B: /* MSIRP */ Ins_MSIRP( EXEC_ARG_ args ); break; case 0x3C: /* AlignRP */ Ins_ALIGNRP( EXEC_ARG_ args ); break; case 0x3D: /* RTDG */ DO_RTDG break; case 0x3E: /* MIAP */ case 0x3F: /* MIAP */ Ins_MIAP( EXEC_ARG_ args ); break; case 0x40: /* NPUSHB */ Ins_NPUSHB( EXEC_ARG_ args ); break; case 0x41: /* NPUSHW */ Ins_NPUSHW( EXEC_ARG_ args ); break; case 0x42: /* WS */ DO_WS break; Set_Invalid_Ref: CUR.error = TT_Err_Invalid_Reference; break; case 0x43: /* RS */ DO_RS break; case 0x44: /* WCVTP */ DO_WCVTP break; case 0x45: /* RCVT */ DO_RCVT break; case 0x46: /* GC */ case 0x47: /* GC */ Ins_GC( EXEC_ARG_ args ); break; case 0x48: /* SCFS */ Ins_SCFS( EXEC_ARG_ args ); break; case 0x49: /* MD */ case 0x4A: /* MD */ Ins_MD( EXEC_ARG_ args ); break; case 0x4B: /* MPPEM */ DO_MPPEM break; case 0x4C: /* MPS */ DO_MPS break; case 0x4D: /* FLIPON */ DO_FLIPON break; case 0x4E: /* FLIPOFF */ DO_FLIPOFF break; case 0x4F: /* DEBUG */ DO_DEBUG break; case 0x50: /* LT */ DO_LT break; case 0x51: /* LTEQ */ DO_LTEQ break; case 0x52: /* GT */ DO_GT break; case 0x53: /* GTEQ */ DO_GTEQ break; case 0x54: /* EQ */ DO_EQ break; case 0x55: /* NEQ */ DO_NEQ break; case 0x56: /* ODD */ DO_ODD break; case 0x57: /* EVEN */ DO_EVEN break; case 0x58: /* IF */ Ins_IF( EXEC_ARG_ args ); break; case 0x59: /* EIF */ /* do nothing */ break; case 0x5A: /* AND */ DO_AND break; case 0x5B: /* OR */ DO_OR break; case 0x5C: /* NOT */ DO_NOT break; case 0x5D: /* DELTAP1 */ Ins_DELTAP( EXEC_ARG_ args ); break; case 0x5E: /* SDB */ DO_SDB break; case 0x5F: /* SDS */ DO_SDS break; case 0x60: /* ADD */ DO_ADD break; case 0x61: /* SUB */ DO_SUB break; case 0x62: /* DIV */ DO_DIV break; case 0x63: /* MUL */ DO_MUL break; case 0x64: /* ABS */ DO_ABS break; case 0x65: /* NEG */ DO_NEG break; case 0x66: /* FLOOR */ DO_FLOOR break; case 0x67: /* CEILING */ DO_CEILING break; case 0x68: /* ROUND */ case 0x69: /* ROUND */ case 0x6A: /* ROUND */ case 0x6B: /* ROUND */ DO_ROUND break; case 0x6C: /* NROUND */ case 0x6D: /* NROUND */ case 0x6E: /* NRRUND */ case 0x6F: /* NROUND */ DO_NROUND break; case 0x70: /* WCVTF */ DO_WCVTF break; case 0x71: /* DELTAP2 */ case 0x72: /* DELTAP3 */ Ins_DELTAP( EXEC_ARG_ args ); break; case 0x73: /* DELTAC0 */ case 0x74: /* DELTAC1 */ case 0x75: /* DELTAC2 */ Ins_DELTAC( EXEC_ARG_ args ); break; case 0x76: /* SROUND */ DO_SROUND break; case 0x77: /* S45Round */ DO_S45ROUND break; case 0x78: /* JROT */ DO_JROT break; case 0x79: /* JROF */ DO_JROF break; case 0x7A: /* ROFF */ DO_ROFF break; case 0x7B: /* ???? */ Ins_UNKNOWN( EXEC_ARG_ args ); break; case 0x7C: /* RUTG */ DO_RUTG break; case 0x7D: /* RDTG */ DO_RDTG break; case 0x7E: /* SANGW */ case 0x7F: /* AA */ /* nothing - obsolete */ break; case 0x80: /* FLIPPT */ Ins_FLIPPT( EXEC_ARG_ args ); break; case 0x81: /* FLIPRGON */ Ins_FLIPRGON( EXEC_ARG_ args ); break; case 0x82: /* FLIPRGOFF */ Ins_FLIPRGOFF( EXEC_ARG_ args ); break; case 0x83: /* UNKNOWN */ case 0x84: /* UNKNOWN */ Ins_UNKNOWN( EXEC_ARG_ args ); break; case 0x85: /* SCANCTRL */ Ins_SCANCTRL( EXEC_ARG_ args ); break; case 0x86: /* SDPVTL */ case 0x87: /* SDPVTL */ Ins_SDPVTL( EXEC_ARG_ args ); break; case 0x88: /* GETINFO */ Ins_GETINFO( EXEC_ARG_ args ); break; case 0x89: /* IDEF */ Ins_IDEF( EXEC_ARG_ args ); break; case 0x8A: /* ROLL */ Ins_ROLL( EXEC_ARG_ args ); break; case 0x8B: /* MAX */ DO_MAX break; case 0x8C: /* MIN */ DO_MIN break; case 0x8D: /* SCANTYPE */ Ins_SCANTYPE( EXEC_ARG_ args ); break; case 0x8E: /* INSTCTRL */ Ins_INSTCTRL( EXEC_ARG_ args ); break; case 0x8F: Ins_UNKNOWN( EXEC_ARG_ args ); break; default: if ( opcode >= 0xE0 ) Ins_MIRP( EXEC_ARG_ args ); else if ( opcode >= 0xC0 ) Ins_MDRP( EXEC_ARG_ args ); else if ( opcode >= 0xB8 ) Ins_PUSHW( EXEC_ARG_ args ); else if ( opcode >= 0xB0 ) Ins_PUSHB( EXEC_ARG_ args ); else Ins_UNKNOWN( EXEC_ARG_ args ); } } #else Instruct_Dispatch[CUR.opcode]( EXEC_ARG_ &CUR.stack[CUR.args] ); #endif /* TT_CONFIG_OPTION_INTERPRETER_SWITCH */ if ( CUR.error != TT_Err_Ok ) { switch ( CUR.error ) { case TT_Err_Invalid_Opcode: /* looking for redefined instructions */ { TT_DefRecord* def = CUR.IDefs; TT_DefRecord* limit = def + CUR.numIDefs; for ( ; def < limit; def++ ) { if ( def->active && CUR.opcode == (FT_Byte)def->opc ) { TT_CallRec* callrec; if ( CUR.callTop >= CUR.callSize ) { CUR.error = TT_Err_Invalid_Reference; goto LErrorLabel_; } callrec = &CUR.callStack[CUR.callTop]; callrec->Caller_Range = CUR.curRange; callrec->Caller_IP = CUR.IP + 1; callrec->Cur_Count = 1; callrec->Cur_Restart = def->start; if ( INS_Goto_CodeRange( def->range, def->start ) == FAILURE ) goto LErrorLabel_; goto LSuiteLabel_; } } } CUR.error = TT_Err_Invalid_Opcode; goto LErrorLabel_; #if 0 break; /* Unreachable code warning suppression. */ /* Leave to remind in case a later change the editor */ /* to consider break; */ #endif default: goto LErrorLabel_; #if 0 break; #endif } } CUR.top = CUR.new_top; if ( CUR.step_ins ) CUR.IP += CUR.length; /* increment instruction counter and check if we didn't */ /* run this program for too long (e.g. infinite loops). */ if ( ++ins_counter > MAX_RUNNABLE_OPCODES ) return TT_Err_Execution_Too_Long; LSuiteLabel_: if ( CUR.IP >= CUR.codeSize ) { if ( CUR.callTop > 0 ) { CUR.error = TT_Err_Code_Overflow; goto LErrorLabel_; } else goto LNo_Error_; } } while ( !CUR.instruction_trap ); LNo_Error_: #ifdef TT_CONFIG_OPTION_STATIC_RASTER *exc = cur; #endif return TT_Err_Ok; LErrorCodeOverflow_: CUR.error = TT_Err_Code_Overflow; LErrorLabel_: #ifdef TT_CONFIG_OPTION_STATIC_RASTER *exc = cur; #endif /* If any errors have occurred, function tables may be broken. */ /* Force a re-execution of `prep' and `fpgm' tables if no */ /* bytecode debugger is run. */ if ( CUR.error && !CUR.instruction_trap ) { FT_TRACE1(( " The interpreter returned error 0x%x\n", CUR.error )); exc->size->cvt_ready = FALSE; } return CUR.error; } #endif /* TT_USE_BYTECODE_INTERPRETER */ /* END */