| /***************************************************************************/ |
| /* */ |
| /* ttinterp.c */ |
| /* */ |
| /* TrueType bytecode interpreter (body). */ |
| /* */ |
| /* Copyright 1996-2017 by */ |
| /* David Turner, Robert Wilhelm, and Werner Lemberg. */ |
| /* */ |
| /* This file is part of the FreeType project, and may only be used, */ |
| /* modified, and distributed under the terms of the FreeType project */ |
| /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ |
| /* this file you indicate that you have read the license and */ |
| /* understand and accept it fully. */ |
| /* */ |
| /***************************************************************************/ |
| |
| |
| /* Greg Hitchcock from Microsoft has helped a lot in resolving unclear */ |
| /* issues; many thanks! */ |
| |
| |
| #include <ft2build.h> |
| #include FT_INTERNAL_DEBUG_H |
| #include FT_INTERNAL_CALC_H |
| #include FT_TRIGONOMETRY_H |
| #include FT_SYSTEM_H |
| #include FT_TRUETYPE_DRIVER_H |
| #include FT_MULTIPLE_MASTERS_H |
| |
| #include "ttinterp.h" |
| #include "tterrors.h" |
| #include "ttsubpix.h" |
| #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
| #include "ttgxvar.h" |
| #endif |
| |
| |
| #ifdef TT_USE_BYTECODE_INTERPRETER |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
| /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ |
| /* messages during execution. */ |
| /* */ |
| #undef FT_COMPONENT |
| #define FT_COMPONENT trace_ttinterp |
| |
| |
| #define NO_SUBPIXEL_HINTING \ |
| ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \ |
| TT_INTERPRETER_VERSION_35 ) |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| #define SUBPIXEL_HINTING_INFINALITY \ |
| ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \ |
| TT_INTERPRETER_VERSION_38 ) |
| #endif |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| #define SUBPIXEL_HINTING_MINIMAL \ |
| ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \ |
| TT_INTERPRETER_VERSION_40 ) |
| #endif |
| |
| #define PROJECT( v1, v2 ) \ |
| exc->func_project( exc, \ |
| SUB_LONG( (v1)->x, (v2)->x ), \ |
| SUB_LONG( (v1)->y, (v2)->y ) ) |
| |
| #define DUALPROJ( v1, v2 ) \ |
| exc->func_dualproj( exc, \ |
| SUB_LONG( (v1)->x, (v2)->x ), \ |
| SUB_LONG( (v1)->y, (v2)->y ) ) |
| |
| #define FAST_PROJECT( v ) \ |
| exc->func_project( exc, (v)->x, (v)->y ) |
| |
| #define FAST_DUALPROJ( v ) \ |
| exc->func_dualproj( exc, (v)->x, (v)->y ) |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* 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 |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* CODERANGE FUNCTIONS */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_Goto_CodeRange */ |
| /* */ |
| /* <Description> */ |
| /* Switches to a new code range (updates the code related elements in */ |
| /* `exec', and `IP'). */ |
| /* */ |
| /* <Input> */ |
| /* range :: The new execution code range. */ |
| /* */ |
| /* IP :: The new IP in the new code range. */ |
| /* */ |
| /* <InOut> */ |
| /* exec :: The target execution context. */ |
| /* */ |
| FT_LOCAL_DEF( void ) |
| TT_Goto_CodeRange( TT_ExecContext exec, |
| FT_Int range, |
| FT_Long IP ) |
| { |
| TT_CodeRange* coderange; |
| |
| |
| FT_ASSERT( range >= 1 && range <= 3 ); |
| |
| coderange = &exec->codeRangeTable[range - 1]; |
| |
| FT_ASSERT( coderange->base ); |
| |
| /* NOTE: Because the last instruction of a program may be a CALL */ |
| /* which will return to the first byte *after* the code */ |
| /* range, we test for IP <= Size instead of IP < Size. */ |
| /* */ |
| FT_ASSERT( IP <= coderange->size ); |
| |
| exec->code = coderange->base; |
| exec->codeSize = coderange->size; |
| exec->IP = IP; |
| exec->curRange = range; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_Set_CodeRange */ |
| /* */ |
| /* <Description> */ |
| /* Sets a code range. */ |
| /* */ |
| /* <Input> */ |
| /* range :: The code range index. */ |
| /* */ |
| /* base :: The new code base. */ |
| /* */ |
| /* length :: The range size in bytes. */ |
| /* */ |
| /* <InOut> */ |
| /* exec :: The target execution context. */ |
| /* */ |
| FT_LOCAL_DEF( void ) |
| TT_Set_CodeRange( TT_ExecContext exec, |
| FT_Int range, |
| void* base, |
| FT_Long length ) |
| { |
| FT_ASSERT( range >= 1 && range <= 3 ); |
| |
| exec->codeRangeTable[range - 1].base = (FT_Byte*)base; |
| exec->codeRangeTable[range - 1].size = length; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_Clear_CodeRange */ |
| /* */ |
| /* <Description> */ |
| /* Clears a code range. */ |
| /* */ |
| /* <Input> */ |
| /* range :: The code range index. */ |
| /* */ |
| /* <InOut> */ |
| /* exec :: The target execution context. */ |
| /* */ |
| FT_LOCAL_DEF( void ) |
| TT_Clear_CodeRange( TT_ExecContext exec, |
| FT_Int range ) |
| { |
| FT_ASSERT( range >= 1 && range <= 3 ); |
| |
| exec->codeRangeTable[range - 1].base = NULL; |
| exec->codeRangeTable[range - 1].size = 0; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* EXECUTION CONTEXT ROUTINES */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_Done_Context */ |
| /* */ |
| /* <Description> */ |
| /* Destroys a given context. */ |
| /* */ |
| /* <Input> */ |
| /* exec :: A handle to the target execution context. */ |
| /* */ |
| /* memory :: A handle to the parent memory object. */ |
| /* */ |
| /* <Note> */ |
| /* Only the glyph loader and debugger should call this function. */ |
| /* */ |
| FT_LOCAL_DEF( void ) |
| TT_Done_Context( TT_ExecContext exec ) |
| { |
| FT_Memory memory = exec->memory; |
| |
| |
| /* points zone */ |
| exec->maxPoints = 0; |
| exec->maxContours = 0; |
| |
| /* free stack */ |
| FT_FREE( exec->stack ); |
| exec->stackSize = 0; |
| |
| /* free call stack */ |
| FT_FREE( exec->callStack ); |
| exec->callSize = 0; |
| exec->callTop = 0; |
| |
| /* free glyph code range */ |
| FT_FREE( exec->glyphIns ); |
| exec->glyphSize = 0; |
| |
| exec->size = NULL; |
| exec->face = NULL; |
| |
| FT_FREE( exec ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Init_Context */ |
| /* */ |
| /* <Description> */ |
| /* Initializes a context object. */ |
| /* */ |
| /* <Input> */ |
| /* memory :: A handle to the parent memory object. */ |
| /* */ |
| /* <InOut> */ |
| /* exec :: A handle to the target execution context. */ |
| /* */ |
| /* <Return> */ |
| /* FreeType error code. 0 means success. */ |
| /* */ |
| static FT_Error |
| Init_Context( TT_ExecContext exec, |
| FT_Memory memory ) |
| { |
| FT_Error error; |
| |
| |
| FT_TRACE1(( "Init_Context: new object at 0x%08p\n", exec )); |
| |
| exec->memory = memory; |
| exec->callSize = 32; |
| |
| if ( FT_NEW_ARRAY( exec->callStack, exec->callSize ) ) |
| goto Fail_Memory; |
| |
| /* all values in the context are set to 0 already, but this is */ |
| /* here as a remainder */ |
| exec->maxPoints = 0; |
| exec->maxContours = 0; |
| |
| exec->stackSize = 0; |
| exec->glyphSize = 0; |
| |
| exec->stack = NULL; |
| exec->glyphIns = NULL; |
| |
| exec->face = NULL; |
| exec->size = NULL; |
| |
| return FT_Err_Ok; |
| |
| Fail_Memory: |
| FT_ERROR(( "Init_Context: not enough memory for %p\n", exec )); |
| TT_Done_Context( exec ); |
| |
| return error; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Update_Max */ |
| /* */ |
| /* <Description> */ |
| /* Checks the size of a buffer and reallocates it if necessary. */ |
| /* */ |
| /* <Input> */ |
| /* memory :: A handle to the parent memory object. */ |
| /* */ |
| /* multiplier :: The size in bytes of each element in the buffer. */ |
| /* */ |
| /* new_max :: The new capacity (size) of the buffer. */ |
| /* */ |
| /* <InOut> */ |
| /* size :: The address of the buffer's current size expressed */ |
| /* in elements. */ |
| /* */ |
| /* buff :: The address of the buffer base pointer. */ |
| /* */ |
| /* <Return> */ |
| /* FreeType error code. 0 means success. */ |
| /* */ |
| FT_LOCAL_DEF( FT_Error ) |
| Update_Max( FT_Memory memory, |
| FT_ULong* size, |
| FT_ULong multiplier, |
| void* _pbuff, |
| FT_ULong new_max ) |
| { |
| FT_Error error; |
| void** pbuff = (void**)_pbuff; |
| |
| |
| if ( *size < new_max ) |
| { |
| if ( FT_REALLOC( *pbuff, *size * multiplier, new_max * multiplier ) ) |
| return error; |
| *size = new_max; |
| } |
| |
| return FT_Err_Ok; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_Load_Context */ |
| /* */ |
| /* <Description> */ |
| /* Prepare an execution context for glyph hinting. */ |
| /* */ |
| /* <Input> */ |
| /* face :: A handle to the source face object. */ |
| /* */ |
| /* size :: A handle to the source size object. */ |
| /* */ |
| /* <InOut> */ |
| /* exec :: A handle to the target execution context. */ |
| /* */ |
| /* <Return> */ |
| /* FreeType error code. 0 means success. */ |
| /* */ |
| /* <Note> */ |
| /* Only the glyph loader and debugger should call this function. */ |
| /* */ |
| FT_LOCAL_DEF( FT_Error ) |
| TT_Load_Context( TT_ExecContext exec, |
| TT_Face face, |
| TT_Size size ) |
| { |
| FT_Int i; |
| FT_ULong tmp; |
| TT_MaxProfile* maxp; |
| FT_Error error; |
| |
| |
| exec->face = face; |
| maxp = &face->max_profile; |
| exec->size = size; |
| |
| if ( size ) |
| { |
| exec->numFDefs = size->num_function_defs; |
| exec->maxFDefs = size->max_function_defs; |
| exec->numIDefs = size->num_instruction_defs; |
| exec->maxIDefs = size->max_instruction_defs; |
| exec->FDefs = size->function_defs; |
| exec->IDefs = size->instruction_defs; |
| exec->pointSize = size->point_size; |
| 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_ZERO( &exec->zp0 ); |
| exec->zp1 = exec->zp0; |
| exec->zp2 = exec->zp0; |
| } |
| |
| /* XXX: We reserve a little more elements on the stack to deal safely */ |
| /* with broken fonts like arialbs, courbs, timesbs, etc. */ |
| tmp = (FT_ULong)exec->stackSize; |
| error = Update_Max( exec->memory, |
| &tmp, |
| sizeof ( FT_F26Dot6 ), |
| (void*)&exec->stack, |
| maxp->maxStackElements + 32 ); |
| exec->stackSize = (FT_Long)tmp; |
| if ( error ) |
| return error; |
| |
| tmp = exec->glyphSize; |
| error = Update_Max( exec->memory, |
| &tmp, |
| sizeof ( FT_Byte ), |
| (void*)&exec->glyphIns, |
| maxp->maxSizeOfInstructions ); |
| exec->glyphSize = (FT_UShort)tmp; |
| if ( error ) |
| return error; |
| |
| exec->pts.n_points = 0; |
| exec->pts.n_contours = 0; |
| |
| exec->zp1 = exec->pts; |
| exec->zp2 = exec->pts; |
| exec->zp0 = exec->pts; |
| |
| exec->instruction_trap = FALSE; |
| |
| return FT_Err_Ok; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_Save_Context */ |
| /* */ |
| /* <Description> */ |
| /* Saves the code ranges in a `size' object. */ |
| /* */ |
| /* <Input> */ |
| /* exec :: A handle to the source execution context. */ |
| /* */ |
| /* <InOut> */ |
| /* size :: A handle to the target size object. */ |
| /* */ |
| /* <Note> */ |
| /* Only the glyph loader and debugger should call this function. */ |
| /* */ |
| FT_LOCAL_DEF( void ) |
| TT_Save_Context( TT_ExecContext exec, |
| TT_Size size ) |
| { |
| FT_Int i; |
| |
| |
| /* XXX: Will probably disappear soon with all the code range */ |
| /* management, which is now rather obsolete. */ |
| /* */ |
| size->num_function_defs = exec->numFDefs; |
| size->num_instruction_defs = exec->numIDefs; |
| |
| size->max_func = exec->maxFunc; |
| size->max_ins = exec->maxIns; |
| |
| for ( i = 0; i < TT_MAX_CODE_RANGES; i++ ) |
| size->codeRangeTable[i] = exec->codeRangeTable[i]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* TT_Run_Context */ |
| /* */ |
| /* <Description> */ |
| /* Executes one or more instructions in the execution context. */ |
| /* */ |
| /* <Input> */ |
| /* debug :: A Boolean flag. If set, the function sets some internal */ |
| /* variables and returns immediately, otherwise TT_RunIns() */ |
| /* is called. */ |
| /* */ |
| /* This is commented out currently. */ |
| /* */ |
| /* <Input> */ |
| /* exec :: A handle to the target execution context. */ |
| /* */ |
| /* <Return> */ |
| /* TrueType error code. 0 means success. */ |
| /* */ |
| FT_LOCAL_DEF( FT_Error ) |
| TT_Run_Context( TT_ExecContext exec ) |
| { |
| TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 ); |
| |
| exec->zp0 = exec->pts; |
| exec->zp1 = exec->pts; |
| exec->zp2 = exec->pts; |
| |
| exec->GS.gep0 = 1; |
| exec->GS.gep1 = 1; |
| exec->GS.gep2 = 1; |
| |
| exec->GS.projVector.x = 0x4000; |
| exec->GS.projVector.y = 0x0000; |
| |
| exec->GS.freeVector = exec->GS.projVector; |
| exec->GS.dualVector = exec->GS.projVector; |
| |
| exec->GS.round_state = 1; |
| exec->GS.loop = 1; |
| |
| /* some glyphs leave something on the stack. so we clean it */ |
| /* before a new execution. */ |
| exec->top = 0; |
| exec->callTop = 0; |
| |
| return exec->face->interpreter( exec ); |
| } |
| |
| |
| /* The default value for `scan_control' is documented as FALSE in the */ |
| /* TrueType specification. This is confusing since it implies a */ |
| /* Boolean value. However, this is not the case, thus both the */ |
| /* default values of our `scan_type' and `scan_control' fields (which */ |
| /* the documentation's `scan_control' variable is split into) are */ |
| /* zero. */ |
| |
| const TT_GraphicsState tt_default_graphics_state = |
| { |
| 0, 0, 0, |
| { 0x4000, 0 }, |
| { 0x4000, 0 }, |
| { 0x4000, 0 }, |
| |
| 1, 64, 1, |
| TRUE, 68, 0, 0, 9, 3, |
| 0, FALSE, 0, 1, 1, 1 |
| }; |
| |
| |
| /* documentation is in ttinterp.h */ |
| |
| FT_EXPORT_DEF( TT_ExecContext ) |
| TT_New_Context( TT_Driver driver ) |
| { |
| FT_Memory memory; |
| FT_Error error; |
| |
| TT_ExecContext exec = NULL; |
| |
| |
| if ( !driver ) |
| goto Fail; |
| |
| memory = driver->root.root.memory; |
| |
| /* allocate object */ |
| if ( FT_NEW( exec ) ) |
| goto Fail; |
| |
| /* initialize it; in case of error this deallocates `exec' too */ |
| error = Init_Context( exec, memory ); |
| if ( error ) |
| goto Fail; |
| |
| return exec; |
| |
| Fail: |
| return NULL; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* Before an opcode is executed, the interpreter verifies that there are */ |
| /* enough arguments on the stack, with the help of the `Pop_Push_Count' */ |
| /* table. */ |
| /* */ |
| /* For each opcode, the first column gives the number of arguments that */ |
| /* are popped from the stack; the second one gives the number of those */ |
| /* that are pushed in result. */ |
| /* */ |
| /* Opcodes which have a varying number of parameters in the data stream */ |
| /* (NPUSHB, NPUSHW) are handled specially; they have a negative value in */ |
| /* the `opcode_length' table, and the value in `Pop_Push_Count' is set */ |
| /* to zero. */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| #undef PACK |
| #define PACK( x, y ) ( ( x << 4 ) | y ) |
| |
| |
| static |
| const FT_Byte Pop_Push_Count[256] = |
| { |
| /* opcodes are gathered in groups of 16 */ |
| /* please keep the spaces as they are */ |
| |
| /* SVTCA y */ PACK( 0, 0 ), |
| /* SVTCA x */ PACK( 0, 0 ), |
| /* SPvTCA y */ PACK( 0, 0 ), |
| /* SPvTCA x */ PACK( 0, 0 ), |
| /* SFvTCA y */ PACK( 0, 0 ), |
| /* SFvTCA x */ PACK( 0, 0 ), |
| /* SPvTL // */ PACK( 2, 0 ), |
| /* SPvTL + */ PACK( 2, 0 ), |
| /* SFvTL // */ PACK( 2, 0 ), |
| /* SFvTL + */ PACK( 2, 0 ), |
| /* SPvFS */ PACK( 2, 0 ), |
| /* SFvFS */ PACK( 2, 0 ), |
| /* GPv */ PACK( 0, 2 ), |
| /* GFv */ PACK( 0, 2 ), |
| /* SFvTPv */ PACK( 0, 0 ), |
| /* ISECT */ PACK( 5, 0 ), |
| |
| /* SRP0 */ PACK( 1, 0 ), |
| /* SRP1 */ PACK( 1, 0 ), |
| /* SRP2 */ PACK( 1, 0 ), |
| /* SZP0 */ PACK( 1, 0 ), |
| /* SZP1 */ PACK( 1, 0 ), |
| /* SZP2 */ PACK( 1, 0 ), |
| /* SZPS */ PACK( 1, 0 ), |
| /* SLOOP */ PACK( 1, 0 ), |
| /* RTG */ PACK( 0, 0 ), |
| /* RTHG */ PACK( 0, 0 ), |
| /* SMD */ PACK( 1, 0 ), |
| /* ELSE */ PACK( 0, 0 ), |
| /* JMPR */ PACK( 1, 0 ), |
| /* SCvTCi */ PACK( 1, 0 ), |
| /* SSwCi */ PACK( 1, 0 ), |
| /* SSW */ PACK( 1, 0 ), |
| |
| /* DUP */ PACK( 1, 2 ), |
| /* POP */ PACK( 1, 0 ), |
| /* CLEAR */ PACK( 0, 0 ), |
| /* SWAP */ PACK( 2, 2 ), |
| /* DEPTH */ PACK( 0, 1 ), |
| /* CINDEX */ PACK( 1, 1 ), |
| /* MINDEX */ PACK( 1, 0 ), |
| /* AlignPTS */ PACK( 2, 0 ), |
| /* INS_$28 */ PACK( 0, 0 ), |
| /* UTP */ PACK( 1, 0 ), |
| /* LOOPCALL */ PACK( 2, 0 ), |
| /* CALL */ PACK( 1, 0 ), |
| /* FDEF */ PACK( 1, 0 ), |
| /* ENDF */ PACK( 0, 0 ), |
| /* MDAP[0] */ PACK( 1, 0 ), |
| /* MDAP[1] */ PACK( 1, 0 ), |
| |
| /* IUP[0] */ PACK( 0, 0 ), |
| /* IUP[1] */ PACK( 0, 0 ), |
| /* SHP[0] */ PACK( 0, 0 ), /* loops */ |
| /* SHP[1] */ PACK( 0, 0 ), /* loops */ |
| /* SHC[0] */ PACK( 1, 0 ), |
| /* SHC[1] */ PACK( 1, 0 ), |
| /* SHZ[0] */ PACK( 1, 0 ), |
| /* SHZ[1] */ PACK( 1, 0 ), |
| /* SHPIX */ PACK( 1, 0 ), /* loops */ |
| /* IP */ PACK( 0, 0 ), /* loops */ |
| /* MSIRP[0] */ PACK( 2, 0 ), |
| /* MSIRP[1] */ PACK( 2, 0 ), |
| /* AlignRP */ PACK( 0, 0 ), /* loops */ |
| /* 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 ), /* loops */ |
| /* 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 ), |
| /* GETVAR */ PACK( 0, 0 ), /* will be handled specially */ |
| /* GETDATA */ PACK( 0, 1 ), |
| /* INS_$93 */ PACK( 0, 0 ), |
| /* INS_$94 */ PACK( 0, 0 ), |
| /* INS_$95 */ PACK( 0, 0 ), |
| /* INS_$96 */ PACK( 0, 0 ), |
| /* INS_$97 */ PACK( 0, 0 ), |
| /* INS_$98 */ PACK( 0, 0 ), |
| /* INS_$99 */ PACK( 0, 0 ), |
| /* INS_$9A */ PACK( 0, 0 ), |
| /* INS_$9B */ PACK( 0, 0 ), |
| /* INS_$9C */ PACK( 0, 0 ), |
| /* INS_$9D */ PACK( 0, 0 ), |
| /* INS_$9E */ PACK( 0, 0 ), |
| /* INS_$9F */ PACK( 0, 0 ), |
| |
| /* INS_$A0 */ PACK( 0, 0 ), |
| /* INS_$A1 */ PACK( 0, 0 ), |
| /* INS_$A2 */ PACK( 0, 0 ), |
| /* INS_$A3 */ PACK( 0, 0 ), |
| /* INS_$A4 */ PACK( 0, 0 ), |
| /* INS_$A5 */ PACK( 0, 0 ), |
| /* INS_$A6 */ PACK( 0, 0 ), |
| /* INS_$A7 */ PACK( 0, 0 ), |
| /* INS_$A8 */ PACK( 0, 0 ), |
| /* INS_$A9 */ PACK( 0, 0 ), |
| /* INS_$AA */ PACK( 0, 0 ), |
| /* INS_$AB */ PACK( 0, 0 ), |
| /* INS_$AC */ PACK( 0, 0 ), |
| /* INS_$AD */ PACK( 0, 0 ), |
| /* INS_$AE */ PACK( 0, 0 ), |
| /* INS_$AF */ PACK( 0, 0 ), |
| |
| /* PushB[0] */ PACK( 0, 1 ), |
| /* PushB[1] */ PACK( 0, 2 ), |
| /* PushB[2] */ PACK( 0, 3 ), |
| /* PushB[3] */ PACK( 0, 4 ), |
| /* PushB[4] */ PACK( 0, 5 ), |
| /* PushB[5] */ PACK( 0, 6 ), |
| /* PushB[6] */ PACK( 0, 7 ), |
| /* PushB[7] */ PACK( 0, 8 ), |
| /* PushW[0] */ PACK( 0, 1 ), |
| /* PushW[1] */ PACK( 0, 2 ), |
| /* PushW[2] */ PACK( 0, 3 ), |
| /* PushW[3] */ PACK( 0, 4 ), |
| /* PushW[4] */ PACK( 0, 5 ), |
| /* PushW[5] */ PACK( 0, 6 ), |
| /* PushW[6] */ PACK( 0, 7 ), |
| /* PushW[7] */ PACK( 0, 8 ), |
| |
| /* MDRP[00] */ PACK( 1, 0 ), |
| /* MDRP[01] */ PACK( 1, 0 ), |
| /* MDRP[02] */ PACK( 1, 0 ), |
| /* MDRP[03] */ PACK( 1, 0 ), |
| /* MDRP[04] */ PACK( 1, 0 ), |
| /* MDRP[05] */ PACK( 1, 0 ), |
| /* MDRP[06] */ PACK( 1, 0 ), |
| /* MDRP[07] */ PACK( 1, 0 ), |
| /* MDRP[08] */ PACK( 1, 0 ), |
| /* MDRP[09] */ PACK( 1, 0 ), |
| /* MDRP[10] */ PACK( 1, 0 ), |
| /* MDRP[11] */ PACK( 1, 0 ), |
| /* MDRP[12] */ PACK( 1, 0 ), |
| /* MDRP[13] */ PACK( 1, 0 ), |
| /* MDRP[14] */ PACK( 1, 0 ), |
| /* MDRP[15] */ PACK( 1, 0 ), |
| |
| /* MDRP[16] */ PACK( 1, 0 ), |
| /* MDRP[17] */ PACK( 1, 0 ), |
| /* MDRP[18] */ PACK( 1, 0 ), |
| /* MDRP[19] */ PACK( 1, 0 ), |
| /* MDRP[20] */ PACK( 1, 0 ), |
| /* MDRP[21] */ PACK( 1, 0 ), |
| /* MDRP[22] */ PACK( 1, 0 ), |
| /* MDRP[23] */ PACK( 1, 0 ), |
| /* MDRP[24] */ PACK( 1, 0 ), |
| /* MDRP[25] */ PACK( 1, 0 ), |
| /* MDRP[26] */ PACK( 1, 0 ), |
| /* MDRP[27] */ PACK( 1, 0 ), |
| /* MDRP[28] */ PACK( 1, 0 ), |
| /* MDRP[29] */ PACK( 1, 0 ), |
| /* MDRP[30] */ PACK( 1, 0 ), |
| /* MDRP[31] */ PACK( 1, 0 ), |
| |
| /* MIRP[00] */ PACK( 2, 0 ), |
| /* MIRP[01] */ PACK( 2, 0 ), |
| /* MIRP[02] */ PACK( 2, 0 ), |
| /* MIRP[03] */ PACK( 2, 0 ), |
| /* MIRP[04] */ PACK( 2, 0 ), |
| /* MIRP[05] */ PACK( 2, 0 ), |
| /* MIRP[06] */ PACK( 2, 0 ), |
| /* MIRP[07] */ PACK( 2, 0 ), |
| /* MIRP[08] */ PACK( 2, 0 ), |
| /* MIRP[09] */ PACK( 2, 0 ), |
| /* MIRP[10] */ PACK( 2, 0 ), |
| /* MIRP[11] */ PACK( 2, 0 ), |
| /* MIRP[12] */ PACK( 2, 0 ), |
| /* MIRP[13] */ PACK( 2, 0 ), |
| /* MIRP[14] */ PACK( 2, 0 ), |
| /* MIRP[15] */ PACK( 2, 0 ), |
| |
| /* MIRP[16] */ PACK( 2, 0 ), |
| /* MIRP[17] */ PACK( 2, 0 ), |
| /* MIRP[18] */ PACK( 2, 0 ), |
| /* MIRP[19] */ PACK( 2, 0 ), |
| /* MIRP[20] */ PACK( 2, 0 ), |
| /* MIRP[21] */ PACK( 2, 0 ), |
| /* MIRP[22] */ PACK( 2, 0 ), |
| /* MIRP[23] */ PACK( 2, 0 ), |
| /* MIRP[24] */ PACK( 2, 0 ), |
| /* MIRP[25] */ PACK( 2, 0 ), |
| /* MIRP[26] */ PACK( 2, 0 ), |
| /* MIRP[27] */ PACK( 2, 0 ), |
| /* MIRP[28] */ PACK( 2, 0 ), |
| /* MIRP[29] */ PACK( 2, 0 ), |
| /* MIRP[30] */ PACK( 2, 0 ), |
| /* MIRP[31] */ PACK( 2, 0 ) |
| }; |
| |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| |
| /* the first hex digit gives the length of the opcode name; the space */ |
| /* after the digit is here just to increase readability of the source */ |
| /* code */ |
| |
| static |
| const char* const opcode_name[256] = |
| { |
| "7 SVTCA y", |
| "7 SVTCA x", |
| "8 SPvTCA y", |
| "8 SPvTCA x", |
| "8 SFvTCA y", |
| "8 SFvTCA x", |
| "8 SPvTL ||", |
| "7 SPvTL +", |
| "8 SFvTL ||", |
| "7 SFvTL +", |
| "5 SPvFS", |
| "5 SFvFS", |
| "3 GPv", |
| "3 GFv", |
| "6 SFvTPv", |
| "5 ISECT", |
| |
| "4 SRP0", |
| "4 SRP1", |
| "4 SRP2", |
| "4 SZP0", |
| "4 SZP1", |
| "4 SZP2", |
| "4 SZPS", |
| "5 SLOOP", |
| "3 RTG", |
| "4 RTHG", |
| "3 SMD", |
| "4 ELSE", |
| "4 JMPR", |
| "6 SCvTCi", |
| "5 SSwCi", |
| "3 SSW", |
| |
| "3 DUP", |
| "3 POP", |
| "5 CLEAR", |
| "4 SWAP", |
| "5 DEPTH", |
| "6 CINDEX", |
| "6 MINDEX", |
| "8 AlignPTS", |
| "7 INS_$28", |
| "3 UTP", |
| "8 LOOPCALL", |
| "4 CALL", |
| "4 FDEF", |
| "4 ENDF", |
| "7 MDAP[0]", |
| "7 MDAP[1]", |
| |
| "6 IUP[0]", |
| "6 IUP[1]", |
| "6 SHP[0]", |
| "6 SHP[1]", |
| "6 SHC[0]", |
| "6 SHC[1]", |
| "6 SHZ[0]", |
| "6 SHZ[1]", |
| "5 SHPIX", |
| "2 IP", |
| "8 MSIRP[0]", |
| "8 MSIRP[1]", |
| "7 AlignRP", |
| "4 RTDG", |
| "7 MIAP[0]", |
| "7 MIAP[1]", |
| |
| "6 NPushB", |
| "6 NPushW", |
| "2 WS", |
| "2 RS", |
| "5 WCvtP", |
| "4 RCvt", |
| "5 GC[0]", |
| "5 GC[1]", |
| "4 SCFS", |
| "5 MD[0]", |
| "5 MD[1]", |
| "5 MPPEM", |
| "3 MPS", |
| "6 FlipON", |
| "7 FlipOFF", |
| "5 DEBUG", |
| |
| "2 LT", |
| "4 LTEQ", |
| "2 GT", |
| "4 GTEQ", |
| "2 EQ", |
| "3 NEQ", |
| "3 ODD", |
| "4 EVEN", |
| "2 IF", |
| "3 EIF", |
| "3 AND", |
| "2 OR", |
| "3 NOT", |
| "7 DeltaP1", |
| "3 SDB", |
| "3 SDS", |
| |
| "3 ADD", |
| "3 SUB", |
| "3 DIV", |
| "3 MUL", |
| "3 ABS", |
| "3 NEG", |
| "5 FLOOR", |
| "7 CEILING", |
| "8 ROUND[0]", |
| "8 ROUND[1]", |
| "8 ROUND[2]", |
| "8 ROUND[3]", |
| "9 NROUND[0]", |
| "9 NROUND[1]", |
| "9 NROUND[2]", |
| "9 NROUND[3]", |
| |
| "5 WCvtF", |
| "7 DeltaP2", |
| "7 DeltaP3", |
| "A DeltaCn[0]", |
| "A DeltaCn[1]", |
| "A DeltaCn[2]", |
| "6 SROUND", |
| "8 S45Round", |
| "4 JROT", |
| "4 JROF", |
| "4 ROFF", |
| "7 INS_$7B", |
| "4 RUTG", |
| "4 RDTG", |
| "5 SANGW", |
| "2 AA", |
| |
| "6 FlipPT", |
| "8 FlipRgON", |
| "9 FlipRgOFF", |
| "7 INS_$83", |
| "7 INS_$84", |
| "8 ScanCTRL", |
| "9 SDPvTL[0]", |
| "9 SDPvTL[1]", |
| "7 GetINFO", |
| "4 IDEF", |
| "4 ROLL", |
| "3 MAX", |
| "3 MIN", |
| "8 ScanTYPE", |
| "8 InstCTRL", |
| "7 INS_$8F", |
| |
| "7 INS_$90", |
| #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
| "6 GETVAR", |
| "7 GETDATA", |
| #else |
| "7 INS_$91", |
| "7 INS_$92", |
| #endif |
| "7 INS_$93", |
| "7 INS_$94", |
| "7 INS_$95", |
| "7 INS_$96", |
| "7 INS_$97", |
| "7 INS_$98", |
| "7 INS_$99", |
| "7 INS_$9A", |
| "7 INS_$9B", |
| "7 INS_$9C", |
| "7 INS_$9D", |
| "7 INS_$9E", |
| "7 INS_$9F", |
| |
| "7 INS_$A0", |
| "7 INS_$A1", |
| "7 INS_$A2", |
| "7 INS_$A3", |
| "7 INS_$A4", |
| "7 INS_$A5", |
| "7 INS_$A6", |
| "7 INS_$A7", |
| "7 INS_$A8", |
| "7 INS_$A9", |
| "7 INS_$AA", |
| "7 INS_$AB", |
| "7 INS_$AC", |
| "7 INS_$AD", |
| "7 INS_$AE", |
| "7 INS_$AF", |
| |
| "8 PushB[0]", |
| "8 PushB[1]", |
| "8 PushB[2]", |
| "8 PushB[3]", |
| "8 PushB[4]", |
| "8 PushB[5]", |
| "8 PushB[6]", |
| "8 PushB[7]", |
| "8 PushW[0]", |
| "8 PushW[1]", |
| "8 PushW[2]", |
| "8 PushW[3]", |
| "8 PushW[4]", |
| "8 PushW[5]", |
| "8 PushW[6]", |
| "8 PushW[7]", |
| |
| "8 MDRP[00]", |
| "8 MDRP[01]", |
| "8 MDRP[02]", |
| "8 MDRP[03]", |
| "8 MDRP[04]", |
| "8 MDRP[05]", |
| "8 MDRP[06]", |
| "8 MDRP[07]", |
| "8 MDRP[08]", |
| "8 MDRP[09]", |
| "8 MDRP[10]", |
| "8 MDRP[11]", |
| "8 MDRP[12]", |
| "8 MDRP[13]", |
| "8 MDRP[14]", |
| "8 MDRP[15]", |
| |
| "8 MDRP[16]", |
| "8 MDRP[17]", |
| "8 MDRP[18]", |
| "8 MDRP[19]", |
| "8 MDRP[20]", |
| "8 MDRP[21]", |
| "8 MDRP[22]", |
| "8 MDRP[23]", |
| "8 MDRP[24]", |
| "8 MDRP[25]", |
| "8 MDRP[26]", |
| "8 MDRP[27]", |
| "8 MDRP[28]", |
| "8 MDRP[29]", |
| "8 MDRP[30]", |
| "8 MDRP[31]", |
| |
| "8 MIRP[00]", |
| "8 MIRP[01]", |
| "8 MIRP[02]", |
| "8 MIRP[03]", |
| "8 MIRP[04]", |
| "8 MIRP[05]", |
| "8 MIRP[06]", |
| "8 MIRP[07]", |
| "8 MIRP[08]", |
| "8 MIRP[09]", |
| "8 MIRP[10]", |
| "8 MIRP[11]", |
| "8 MIRP[12]", |
| "8 MIRP[13]", |
| "8 MIRP[14]", |
| "8 MIRP[15]", |
| |
| "8 MIRP[16]", |
| "8 MIRP[17]", |
| "8 MIRP[18]", |
| "8 MIRP[19]", |
| "8 MIRP[20]", |
| "8 MIRP[21]", |
| "8 MIRP[22]", |
| "8 MIRP[23]", |
| "8 MIRP[24]", |
| "8 MIRP[25]", |
| "8 MIRP[26]", |
| "8 MIRP[27]", |
| "8 MIRP[28]", |
| "8 MIRP[29]", |
| "8 MIRP[30]", |
| "8 MIRP[31]" |
| }; |
| |
| #endif /* FT_DEBUG_LEVEL_TRACE */ |
| |
| |
| static |
| const FT_Char opcode_length[256] = |
| { |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| |
| -1,-2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 2, 3, 4, 5, 6, 7, 8, 9, 3, 5, 7, 9, 11,13,15,17, |
| |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 |
| }; |
| |
| #undef PACK |
| |
| |
| #ifndef FT_CONFIG_OPTION_NO_ASSEMBLER |
| |
| #if defined( __arm__ ) && \ |
| ( defined( __thumb2__ ) || !defined( __thumb__ ) ) |
| |
| #define TT_MulFix14 TT_MulFix14_arm |
| |
| static FT_Int32 |
| TT_MulFix14_arm( FT_Int32 a, |
| FT_Int b ) |
| { |
| FT_Int32 t, t2; |
| |
| |
| #if defined( __CC_ARM ) || defined( __ARMCC__ ) |
| |
| __asm |
| { |
| smull t2, t, b, a /* (lo=t2,hi=t) = a*b */ |
| mov a, t, asr #31 /* a = (hi >> 31) */ |
| add a, a, #0x2000 /* a += 0x2000 */ |
| adds t2, t2, a /* t2 += a */ |
| adc t, t, #0 /* t += carry */ |
| mov a, t2, lsr #14 /* a = t2 >> 14 */ |
| orr a, a, t, lsl #18 /* a |= t << 18 */ |
| } |
| |
| #elif defined( __GNUC__ ) |
| |
| __asm__ __volatile__ ( |
| "smull %1, %2, %4, %3\n\t" /* (lo=%1,hi=%2) = a*b */ |
| "mov %0, %2, asr #31\n\t" /* %0 = (hi >> 31) */ |
| #if defined( __clang__ ) && defined( __thumb2__ ) |
| "add.w %0, %0, #0x2000\n\t" /* %0 += 0x2000 */ |
| #else |
| "add %0, %0, #0x2000\n\t" /* %0 += 0x2000 */ |
| #endif |
| "adds %1, %1, %0\n\t" /* %1 += %0 */ |
| "adc %2, %2, #0\n\t" /* %2 += carry */ |
| "mov %0, %1, lsr #14\n\t" /* %0 = %1 >> 16 */ |
| "orr %0, %0, %2, lsl #18\n\t" /* %0 |= %2 << 16 */ |
| : "=r"(a), "=&r"(t2), "=&r"(t) |
| : "r"(a), "r"(b) |
| : "cc" ); |
| |
| #endif |
| |
| return a; |
| } |
| |
| #endif /* __arm__ && ( __thumb2__ || !__thumb__ ) */ |
| |
| #endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ |
| |
| |
| #if defined( __GNUC__ ) && \ |
| ( defined( __i386__ ) || defined( __x86_64__ ) ) |
| |
| #define TT_MulFix14 TT_MulFix14_long_long |
| |
| /* Temporarily disable the warning that C90 doesn't support `long long'. */ |
| #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 |
| #pragma GCC diagnostic push |
| #endif |
| #pragma GCC diagnostic ignored "-Wlong-long" |
| |
| /* This is declared `noinline' because inlining the function results */ |
| /* in slower code. The `pure' attribute indicates that the result */ |
| /* only depends on the parameters. */ |
| static __attribute__(( noinline )) |
| __attribute__(( pure )) FT_Int32 |
| TT_MulFix14_long_long( FT_Int32 a, |
| FT_Int b ) |
| { |
| |
| long long ret = (long long)a * b; |
| |
| /* The following line assumes that right shifting of signed values */ |
| /* will actually preserve the sign bit. The exact behaviour is */ |
| /* undefined, but this is true on x86 and x86_64. */ |
| long long tmp = ret >> 63; |
| |
| |
| ret += 0x2000 + tmp; |
| |
| return (FT_Int32)( ret >> 14 ); |
| } |
| |
| #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 |
| #pragma GCC diagnostic pop |
| #endif |
| |
| #endif /* __GNUC__ && ( __i386__ || __x86_64__ ) */ |
| |
| |
| #ifndef TT_MulFix14 |
| |
| /* Compute (a*b)/2^14 with maximum accuracy and rounding. */ |
| /* This is optimized to be faster than calling FT_MulFix() */ |
| /* for platforms where sizeof(int) == 2. */ |
| static FT_Int32 |
| TT_MulFix14( FT_Int32 a, |
| FT_Int b ) |
| { |
| FT_Int32 sign; |
| FT_UInt32 ah, al, mid, lo, hi; |
| |
| |
| sign = a ^ b; |
| |
| if ( a < 0 ) |
| a = -a; |
| if ( b < 0 ) |
| b = -b; |
| |
| ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU ); |
| al = (FT_UInt32)( a & 0xFFFFU ); |
| |
| lo = al * b; |
| mid = ah * b; |
| hi = mid >> 16; |
| mid = ( mid << 16 ) + ( 1 << 13 ); /* rounding */ |
| lo += mid; |
| if ( lo < mid ) |
| hi += 1; |
| |
| mid = ( lo >> 14 ) | ( hi << 18 ); |
| |
| return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid; |
| } |
| |
| #endif /* !TT_MulFix14 */ |
| |
| |
| #if defined( __GNUC__ ) && \ |
| ( defined( __i386__ ) || \ |
| defined( __x86_64__ ) || \ |
| defined( __arm__ ) ) |
| |
| #define TT_DotFix14 TT_DotFix14_long_long |
| |
| #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 |
| #pragma GCC diagnostic push |
| #endif |
| #pragma GCC diagnostic ignored "-Wlong-long" |
| |
| static __attribute__(( pure )) FT_Int32 |
| TT_DotFix14_long_long( FT_Int32 ax, |
| FT_Int32 ay, |
| FT_Int bx, |
| FT_Int by ) |
| { |
| /* Temporarily disable the warning that C90 doesn't support */ |
| /* `long long'. */ |
| |
| long long temp1 = (long long)ax * bx; |
| long long temp2 = (long long)ay * by; |
| |
| |
| temp1 += temp2; |
| temp2 = temp1 >> 63; |
| temp1 += 0x2000 + temp2; |
| |
| return (FT_Int32)( temp1 >> 14 ); |
| |
| } |
| |
| #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 |
| #pragma GCC diagnostic pop |
| #endif |
| |
| #endif /* __GNUC__ && (__arm__ || __i386__ || __x86_64__) */ |
| |
| |
| #ifndef TT_DotFix14 |
| |
| /* compute (ax*bx+ay*by)/2^14 with maximum accuracy and rounding */ |
| static FT_Int32 |
| TT_DotFix14( FT_Int32 ax, |
| FT_Int32 ay, |
| FT_Int bx, |
| FT_Int by ) |
| { |
| FT_Int32 m, s, hi1, hi2, hi; |
| FT_UInt32 l, lo1, lo2, lo; |
| |
| |
| /* compute ax*bx as 64-bit value */ |
| l = (FT_UInt32)( ( ax & 0xFFFFU ) * bx ); |
| m = ( ax >> 16 ) * bx; |
| |
| lo1 = l + ( (FT_UInt32)m << 16 ); |
| hi1 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo1 < l ); |
| |
| /* compute ay*by as 64-bit value */ |
| l = (FT_UInt32)( ( ay & 0xFFFFU ) * by ); |
| m = ( ay >> 16 ) * by; |
| |
| lo2 = l + ( (FT_UInt32)m << 16 ); |
| hi2 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo2 < l ); |
| |
| /* add them */ |
| lo = lo1 + lo2; |
| hi = hi1 + hi2 + ( lo < lo1 ); |
| |
| /* divide the result by 2^14 with rounding */ |
| s = hi >> 31; |
| l = lo + (FT_UInt32)s; |
| hi += s + ( l < lo ); |
| lo = l; |
| |
| l = lo + 0x2000U; |
| hi += ( l < lo ); |
| |
| return (FT_Int32)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) ); |
| } |
| |
| #endif /* TT_DotFix14 */ |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Current_Ratio */ |
| /* */ |
| /* <Description> */ |
| /* Returns the current aspect ratio scaling factor depending on the */ |
| /* projection vector's state and device resolutions. */ |
| /* */ |
| /* <Return> */ |
| /* The aspect ratio in 16.16 format, always <= 1.0 . */ |
| /* */ |
| static FT_Long |
| Current_Ratio( TT_ExecContext exc ) |
| { |
| if ( !exc->tt_metrics.ratio ) |
| { |
| if ( exc->GS.projVector.y == 0 ) |
| exc->tt_metrics.ratio = exc->tt_metrics.x_ratio; |
| |
| else if ( exc->GS.projVector.x == 0 ) |
| exc->tt_metrics.ratio = exc->tt_metrics.y_ratio; |
| |
| else |
| { |
| FT_F26Dot6 x, y; |
| |
| |
| x = TT_MulFix14( exc->tt_metrics.x_ratio, |
| exc->GS.projVector.x ); |
| y = TT_MulFix14( exc->tt_metrics.y_ratio, |
| exc->GS.projVector.y ); |
| exc->tt_metrics.ratio = FT_Hypot( x, y ); |
| } |
| } |
| return exc->tt_metrics.ratio; |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_Long ) |
| Current_Ppem( TT_ExecContext exc ) |
| { |
| return exc->tt_metrics.ppem; |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_Long ) |
| Current_Ppem_Stretched( TT_ExecContext exc ) |
| { |
| return FT_MulFix( exc->tt_metrics.ppem, Current_Ratio( exc ) ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* Functions related to the control value table (CVT). */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| FT_CALLBACK_DEF( FT_F26Dot6 ) |
| Read_CVT( TT_ExecContext exc, |
| FT_ULong idx ) |
| { |
| return exc->cvt[idx]; |
| } |
| |
| |
| FT_CALLBACK_DEF( FT_F26Dot6 ) |
| Read_CVT_Stretched( TT_ExecContext exc, |
| FT_ULong idx ) |
| { |
| return FT_MulFix( exc->cvt[idx], Current_Ratio( exc ) ); |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| Write_CVT( TT_ExecContext exc, |
| FT_ULong idx, |
| FT_F26Dot6 value ) |
| { |
| exc->cvt[idx] = value; |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| Write_CVT_Stretched( TT_ExecContext exc, |
| FT_ULong idx, |
| FT_F26Dot6 value ) |
| { |
| exc->cvt[idx] = FT_DivFix( value, Current_Ratio( exc ) ); |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| Move_CVT( TT_ExecContext exc, |
| FT_ULong idx, |
| FT_F26Dot6 value ) |
| { |
| exc->cvt[idx] += value; |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| Move_CVT_Stretched( TT_ExecContext exc, |
| FT_ULong idx, |
| FT_F26Dot6 value ) |
| { |
| exc->cvt[idx] += FT_DivFix( value, Current_Ratio( exc ) ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* GetShortIns */ |
| /* */ |
| /* <Description> */ |
| /* Returns a short integer taken from the instruction stream at */ |
| /* address IP. */ |
| /* */ |
| /* <Return> */ |
| /* Short read at code[IP]. */ |
| /* */ |
| /* <Note> */ |
| /* This one could become a macro. */ |
| /* */ |
| static FT_Short |
| GetShortIns( TT_ExecContext exc ) |
| { |
| /* Reading a byte stream so there is no endianness (DaveP) */ |
| exc->IP += 2; |
| return (FT_Short)( ( exc->code[exc->IP - 2] << 8 ) + |
| exc->code[exc->IP - 1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Ins_Goto_CodeRange */ |
| /* */ |
| /* <Description> */ |
| /* Goes to a certain code range in the instruction stream. */ |
| /* */ |
| /* <Input> */ |
| /* aRange :: The index of the code range. */ |
| /* */ |
| /* aIP :: The new IP address in the code range. */ |
| /* */ |
| /* <Return> */ |
| /* SUCCESS or FAILURE. */ |
| /* */ |
| static FT_Bool |
| Ins_Goto_CodeRange( TT_ExecContext exc, |
| FT_Int aRange, |
| FT_Long aIP ) |
| { |
| TT_CodeRange* range; |
| |
| |
| if ( aRange < 1 || aRange > 3 ) |
| { |
| exc->error = FT_THROW( Bad_Argument ); |
| return FAILURE; |
| } |
| |
| range = &exc->codeRangeTable[aRange - 1]; |
| |
| if ( !range->base ) /* invalid coderange */ |
| { |
| exc->error = FT_THROW( Invalid_CodeRange ); |
| return FAILURE; |
| } |
| |
| /* NOTE: Because the last instruction of a program may be a CALL */ |
| /* which will return to the first byte *after* the code */ |
| /* range, we test for aIP <= Size, instead of aIP < Size. */ |
| |
| if ( aIP > range->size ) |
| { |
| exc->error = FT_THROW( Code_Overflow ); |
| return FAILURE; |
| } |
| |
| exc->code = range->base; |
| exc->codeSize = range->size; |
| exc->IP = aIP; |
| exc->curRange = aRange; |
| |
| return SUCCESS; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Direct_Move */ |
| /* */ |
| /* <Description> */ |
| /* Moves a point by a given distance along the freedom vector. The */ |
| /* point will be `touched'. */ |
| /* */ |
| /* <Input> */ |
| /* point :: The index of the point to move. */ |
| /* */ |
| /* distance :: The distance to apply. */ |
| /* */ |
| /* <InOut> */ |
| /* zone :: The affected glyph zone. */ |
| /* */ |
| /* <Note> */ |
| /* See `ttinterp.h' for details on backward compatibility mode. */ |
| /* `Touches' the point. */ |
| /* */ |
| static void |
| Direct_Move( TT_ExecContext exc, |
| TT_GlyphZone zone, |
| FT_UShort point, |
| FT_F26Dot6 distance ) |
| { |
| FT_F26Dot6 v; |
| |
| |
| v = exc->GS.freeVector.x; |
| |
| if ( v != 0 ) |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| ( !exc->ignore_x_mode || |
| ( exc->sph_tweak_flags & SPH_TWEAK_ALLOW_X_DMOVE ) ) ) |
| zone->cur[point].x = ADD_LONG( zone->cur[point].x, |
| FT_MulDiv( distance, |
| v, |
| exc->F_dot_P ) ); |
| else |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* Exception to the post-IUP curfew: Allow the x component of */ |
| /* diagonal moves, but only post-IUP. DejaVu tries to adjust */ |
| /* diagonal stems like on `Z' and `z' post-IUP. */ |
| if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility ) |
| zone->cur[point].x = ADD_LONG( zone->cur[point].x, |
| FT_MulDiv( distance, |
| v, |
| exc->F_dot_P ) ); |
| else |
| #endif |
| |
| if ( NO_SUBPIXEL_HINTING ) |
| zone->cur[point].x = ADD_LONG( zone->cur[point].x, |
| FT_MulDiv( distance, |
| v, |
| exc->F_dot_P ) ); |
| |
| zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; |
| } |
| |
| v = exc->GS.freeVector.y; |
| |
| if ( v != 0 ) |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| if ( !( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility && |
| exc->iupx_called && |
| exc->iupy_called ) ) |
| #endif |
| zone->cur[point].y = ADD_LONG( zone->cur[point].y, |
| FT_MulDiv( distance, |
| v, |
| exc->F_dot_P ) ); |
| |
| zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Direct_Move_Orig */ |
| /* */ |
| /* <Description> */ |
| /* Moves the *original* position of a point by a given distance along */ |
| /* the freedom vector. Obviously, the point will not be `touched'. */ |
| /* */ |
| /* <Input> */ |
| /* point :: The index of the point to move. */ |
| /* */ |
| /* distance :: The distance to apply. */ |
| /* */ |
| /* <InOut> */ |
| /* zone :: The affected glyph zone. */ |
| /* */ |
| static void |
| Direct_Move_Orig( TT_ExecContext exc, |
| TT_GlyphZone zone, |
| FT_UShort point, |
| FT_F26Dot6 distance ) |
| { |
| FT_F26Dot6 v; |
| |
| |
| v = exc->GS.freeVector.x; |
| |
| if ( v != 0 ) |
| zone->org[point].x = ADD_LONG( zone->org[point].x, |
| FT_MulDiv( distance, |
| v, |
| exc->F_dot_P ) ); |
| |
| v = exc->GS.freeVector.y; |
| |
| if ( v != 0 ) |
| zone->org[point].y = ADD_LONG( zone->org[point].y, |
| FT_MulDiv( distance, |
| v, |
| exc->F_dot_P ) ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* Special versions of Direct_Move() */ |
| /* */ |
| /* The following versions are used whenever both vectors are both */ |
| /* along one of the coordinate unit vectors, i.e. in 90% of the cases. */ |
| /* See `ttinterp.h' for details on backward compatibility mode. */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| static void |
| Direct_Move_X( TT_ExecContext exc, |
| TT_GlyphZone zone, |
| FT_UShort point, |
| FT_F26Dot6 distance ) |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && !exc->ignore_x_mode ) |
| zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance ); |
| else |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility ) |
| zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance ); |
| else |
| #endif |
| |
| if ( NO_SUBPIXEL_HINTING ) |
| zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance ); |
| |
| zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; |
| } |
| |
| |
| static void |
| Direct_Move_Y( TT_ExecContext exc, |
| TT_GlyphZone zone, |
| FT_UShort point, |
| FT_F26Dot6 distance ) |
| { |
| FT_UNUSED( exc ); |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| if ( !( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility && |
| exc->iupx_called && exc->iupy_called ) ) |
| #endif |
| zone->cur[point].y = ADD_LONG( zone->cur[point].y, distance ); |
| |
| zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* Special versions of Direct_Move_Orig() */ |
| /* */ |
| /* The following versions are used whenever both vectors are both */ |
| /* along one of the coordinate unit vectors, i.e. in 90% of the cases. */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| static void |
| Direct_Move_Orig_X( TT_ExecContext exc, |
| TT_GlyphZone zone, |
| FT_UShort point, |
| FT_F26Dot6 distance ) |
| { |
| FT_UNUSED( exc ); |
| |
| zone->org[point].x = ADD_LONG( zone->org[point].x, distance ); |
| } |
| |
| |
| static void |
| Direct_Move_Orig_Y( TT_ExecContext exc, |
| TT_GlyphZone zone, |
| FT_UShort point, |
| FT_F26Dot6 distance ) |
| { |
| FT_UNUSED( exc ); |
| |
| zone->org[point].y = ADD_LONG( zone->org[point].y, distance ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Round_None */ |
| /* */ |
| /* <Description> */ |
| /* Does not round, but adds engine compensation. */ |
| /* */ |
| /* <Input> */ |
| /* distance :: The distance (not) to round. */ |
| /* */ |
| /* compensation :: The engine compensation. */ |
| /* */ |
| /* <Return> */ |
| /* The compensated distance. */ |
| /* */ |
| /* <Note> */ |
| /* The TrueType specification says very few about the relationship */ |
| /* between rounding and engine compensation. However, it seems from */ |
| /* the description of super round that we should add the compensation */ |
| /* before rounding. */ |
| /* */ |
| static FT_F26Dot6 |
| Round_None( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_F26Dot6 compensation ) |
| { |
| FT_F26Dot6 val; |
| |
| FT_UNUSED( exc ); |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = ADD_LONG( distance, compensation ); |
| if ( val < 0 ) |
| val = 0; |
| } |
| else |
| { |
| val = SUB_LONG( distance, compensation ); |
| if ( val > 0 ) |
| val = 0; |
| } |
| return val; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Round_To_Grid */ |
| /* */ |
| /* <Description> */ |
| /* Rounds value to grid after adding engine compensation. */ |
| /* */ |
| /* <Input> */ |
| /* distance :: The distance to round. */ |
| /* */ |
| /* compensation :: The engine compensation. */ |
| /* */ |
| /* <Return> */ |
| /* Rounded distance. */ |
| /* */ |
| static FT_F26Dot6 |
| Round_To_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_F26Dot6 compensation ) |
| { |
| FT_F26Dot6 val; |
| |
| FT_UNUSED( exc ); |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = FT_PIX_ROUND_LONG( ADD_LONG( distance, compensation ) ); |
| if ( val < 0 ) |
| val = 0; |
| } |
| else |
| { |
| val = NEG_LONG( FT_PIX_ROUND_LONG( SUB_LONG( compensation, |
| distance ) ) ); |
| if ( val > 0 ) |
| val = 0; |
| } |
| |
| return val; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Round_To_Half_Grid */ |
| /* */ |
| /* <Description> */ |
| /* Rounds value to half grid after adding engine compensation. */ |
| /* */ |
| /* <Input> */ |
| /* distance :: The distance to round. */ |
| /* */ |
| /* compensation :: The engine compensation. */ |
| /* */ |
| /* <Return> */ |
| /* Rounded distance. */ |
| /* */ |
| static FT_F26Dot6 |
| Round_To_Half_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_F26Dot6 compensation ) |
| { |
| FT_F26Dot6 val; |
| |
| FT_UNUSED( exc ); |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = ADD_LONG( FT_PIX_FLOOR( ADD_LONG( distance, compensation ) ), |
| 32 ); |
| if ( val < 0 ) |
| val = 32; |
| } |
| else |
| { |
| val = NEG_LONG( ADD_LONG( FT_PIX_FLOOR( SUB_LONG( compensation, |
| distance ) ), |
| 32 ) ); |
| if ( val > 0 ) |
| val = -32; |
| } |
| |
| return val; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Round_Down_To_Grid */ |
| /* */ |
| /* <Description> */ |
| /* Rounds value down to grid after adding engine compensation. */ |
| /* */ |
| /* <Input> */ |
| /* distance :: The distance to round. */ |
| /* */ |
| /* compensation :: The engine compensation. */ |
| /* */ |
| /* <Return> */ |
| /* Rounded distance. */ |
| /* */ |
| static FT_F26Dot6 |
| Round_Down_To_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_F26Dot6 compensation ) |
| { |
| FT_F26Dot6 val; |
| |
| FT_UNUSED( exc ); |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = FT_PIX_FLOOR( ADD_LONG( distance, compensation ) ); |
| if ( val < 0 ) |
| val = 0; |
| } |
| else |
| { |
| val = NEG_LONG( FT_PIX_FLOOR( SUB_LONG( compensation, distance ) ) ); |
| if ( val > 0 ) |
| val = 0; |
| } |
| |
| return val; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Round_Up_To_Grid */ |
| /* */ |
| /* <Description> */ |
| /* Rounds value up to grid after adding engine compensation. */ |
| /* */ |
| /* <Input> */ |
| /* distance :: The distance to round. */ |
| /* */ |
| /* compensation :: The engine compensation. */ |
| /* */ |
| /* <Return> */ |
| /* Rounded distance. */ |
| /* */ |
| static FT_F26Dot6 |
| Round_Up_To_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_F26Dot6 compensation ) |
| { |
| FT_F26Dot6 val; |
| |
| FT_UNUSED( exc ); |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = FT_PIX_CEIL_LONG( ADD_LONG( distance, compensation ) ); |
| if ( val < 0 ) |
| val = 0; |
| } |
| else |
| { |
| val = NEG_LONG( FT_PIX_CEIL_LONG( SUB_LONG( compensation, |
| distance ) ) ); |
| if ( val > 0 ) |
| val = 0; |
| } |
| |
| return val; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Round_To_Double_Grid */ |
| /* */ |
| /* <Description> */ |
| /* Rounds value to double grid after adding engine compensation. */ |
| /* */ |
| /* <Input> */ |
| /* distance :: The distance to round. */ |
| /* */ |
| /* compensation :: The engine compensation. */ |
| /* */ |
| /* <Return> */ |
| /* Rounded distance. */ |
| /* */ |
| static FT_F26Dot6 |
| Round_To_Double_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_F26Dot6 compensation ) |
| { |
| FT_F26Dot6 val; |
| |
| FT_UNUSED( exc ); |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = FT_PAD_ROUND_LONG( ADD_LONG( distance, compensation ), 32 ); |
| if ( val < 0 ) |
| val = 0; |
| } |
| else |
| { |
| val = NEG_LONG( FT_PAD_ROUND_LONG( SUB_LONG( compensation, distance ), |
| 32 ) ); |
| if ( val > 0 ) |
| val = 0; |
| } |
| |
| return val; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Round_Super */ |
| /* */ |
| /* <Description> */ |
| /* Super-rounds value to grid after adding engine compensation. */ |
| /* */ |
| /* <Input> */ |
| /* distance :: The distance to round. */ |
| /* */ |
| /* compensation :: The engine compensation. */ |
| /* */ |
| /* <Return> */ |
| /* Rounded distance. */ |
| /* */ |
| /* <Note> */ |
| /* The TrueType specification says very little about the relationship */ |
| /* between rounding and engine compensation. However, it seems from */ |
| /* the description of super round that we should add the compensation */ |
| /* before rounding. */ |
| /* */ |
| static FT_F26Dot6 |
| Round_Super( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_F26Dot6 compensation ) |
| { |
| FT_F26Dot6 val; |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = ADD_LONG( distance, |
| exc->threshold - exc->phase + compensation ) & |
| -exc->period; |
| val += exc->phase; |
| if ( val < 0 ) |
| val = exc->phase; |
| } |
| else |
| { |
| val = NEG_LONG( SUB_LONG( exc->threshold - exc->phase + compensation, |
| distance ) & |
| -exc->period ); |
| val -= exc->phase; |
| if ( val > 0 ) |
| val = -exc->phase; |
| } |
| |
| return val; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Round_Super_45 */ |
| /* */ |
| /* <Description> */ |
| /* Super-rounds value to grid after adding engine compensation. */ |
| /* */ |
| /* <Input> */ |
| /* distance :: The distance to round. */ |
| /* */ |
| /* compensation :: The engine compensation. */ |
| /* */ |
| /* <Return> */ |
| /* Rounded distance. */ |
| /* */ |
| /* <Note> */ |
| /* There is a separate function for Round_Super_45() as we may need */ |
| /* greater precision. */ |
| /* */ |
| static FT_F26Dot6 |
| Round_Super_45( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_F26Dot6 compensation ) |
| { |
| FT_F26Dot6 val; |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = ( ADD_LONG( distance, |
| exc->threshold - exc->phase + compensation ) / |
| exc->period ) * exc->period; |
| val += exc->phase; |
| if ( val < 0 ) |
| val = exc->phase; |
| } |
| else |
| { |
| val = NEG_LONG( ( SUB_LONG( exc->threshold - exc->phase + compensation, |
| distance ) / |
| exc->period ) * exc->period ); |
| val -= exc->phase; |
| if ( val > 0 ) |
| val = -exc->phase; |
| } |
| |
| return val; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Compute_Round */ |
| /* */ |
| /* <Description> */ |
| /* Sets the rounding mode. */ |
| /* */ |
| /* <Input> */ |
| /* round_mode :: The rounding mode to be used. */ |
| /* */ |
| static void |
| Compute_Round( TT_ExecContext exc, |
| FT_Byte round_mode ) |
| { |
| switch ( round_mode ) |
| { |
| case TT_Round_Off: |
| exc->func_round = (TT_Round_Func)Round_None; |
| break; |
| |
| case TT_Round_To_Grid: |
| exc->func_round = (TT_Round_Func)Round_To_Grid; |
| break; |
| |
| case TT_Round_Up_To_Grid: |
| exc->func_round = (TT_Round_Func)Round_Up_To_Grid; |
| break; |
| |
| case TT_Round_Down_To_Grid: |
| exc->func_round = (TT_Round_Func)Round_Down_To_Grid; |
| break; |
| |
| case TT_Round_To_Half_Grid: |
| exc->func_round = (TT_Round_Func)Round_To_Half_Grid; |
| break; |
| |
| case TT_Round_To_Double_Grid: |
| exc->func_round = (TT_Round_Func)Round_To_Double_Grid; |
| break; |
| |
| case TT_Round_Super: |
| exc->func_round = (TT_Round_Func)Round_Super; |
| break; |
| |
| case TT_Round_Super_45: |
| exc->func_round = (TT_Round_Func)Round_Super_45; |
| break; |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* SetSuperRound */ |
| /* */ |
| /* <Description> */ |
| /* Sets Super Round parameters. */ |
| /* */ |
| /* <Input> */ |
| /* GridPeriod :: The grid period. */ |
| /* */ |
| /* selector :: The SROUND opcode. */ |
| /* */ |
| static void |
| SetSuperRound( TT_ExecContext exc, |
| FT_F2Dot14 GridPeriod, |
| FT_Long selector ) |
| { |
| switch ( (FT_Int)( selector & 0xC0 ) ) |
| { |
| case 0: |
| exc->period = GridPeriod / 2; |
| break; |
| |
| case 0x40: |
| exc->period = GridPeriod; |
| break; |
| |
| case 0x80: |
| exc->period = GridPeriod * 2; |
| break; |
| |
| /* This opcode is reserved, but... */ |
| case 0xC0: |
| exc->period = GridPeriod; |
| break; |
| } |
| |
| switch ( (FT_Int)( selector & 0x30 ) ) |
| { |
| case 0: |
| exc->phase = 0; |
| break; |
| |
| case 0x10: |
| exc->phase = exc->period / 4; |
| break; |
| |
| case 0x20: |
| exc->phase = exc->period / 2; |
| break; |
| |
| case 0x30: |
| exc->phase = exc->period * 3 / 4; |
| break; |
| } |
| |
| if ( ( selector & 0x0F ) == 0 ) |
| exc->threshold = exc->period - 1; |
| else |
| exc->threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * exc->period / 8; |
| |
| /* convert to F26Dot6 format */ |
| exc->period >>= 8; |
| exc->phase >>= 8; |
| exc->threshold >>= 8; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Project */ |
| /* */ |
| /* <Description> */ |
| /* Computes the projection of vector given by (v2-v1) along the */ |
| /* current projection vector. */ |
| /* */ |
| /* <Input> */ |
| /* v1 :: First input vector. */ |
| /* v2 :: Second input vector. */ |
| /* */ |
| /* <Return> */ |
| /* The distance in F26dot6 format. */ |
| /* */ |
| static FT_F26Dot6 |
| Project( TT_ExecContext exc, |
| FT_Pos dx, |
| FT_Pos dy ) |
| { |
| return TT_DotFix14( dx, dy, |
| exc->GS.projVector.x, |
| exc->GS.projVector.y ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Dual_Project */ |
| /* */ |
| /* <Description> */ |
| /* Computes the projection of the vector given by (v2-v1) along the */ |
| /* current dual vector. */ |
| /* */ |
| /* <Input> */ |
| /* v1 :: First input vector. */ |
| /* v2 :: Second input vector. */ |
| /* */ |
| /* <Return> */ |
| /* The distance in F26dot6 format. */ |
| /* */ |
| static FT_F26Dot6 |
| Dual_Project( TT_ExecContext exc, |
| FT_Pos dx, |
| FT_Pos dy ) |
| { |
| return TT_DotFix14( dx, dy, |
| exc->GS.dualVector.x, |
| exc->GS.dualVector.y ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Project_x */ |
| /* */ |
| /* <Description> */ |
| /* Computes the projection of the vector given by (v2-v1) along the */ |
| /* horizontal axis. */ |
| /* */ |
| /* <Input> */ |
| /* v1 :: First input vector. */ |
| /* v2 :: Second input vector. */ |
| /* */ |
| /* <Return> */ |
| /* The distance in F26dot6 format. */ |
| /* */ |
| static FT_F26Dot6 |
| Project_x( TT_ExecContext exc, |
| FT_Pos dx, |
| FT_Pos dy ) |
| { |
| FT_UNUSED( exc ); |
| FT_UNUSED( dy ); |
| |
| return dx; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Project_y */ |
| /* */ |
| /* <Description> */ |
| /* Computes the projection of the vector given by (v2-v1) along the */ |
| /* vertical axis. */ |
| /* */ |
| /* <Input> */ |
| /* v1 :: First input vector. */ |
| /* v2 :: Second input vector. */ |
| /* */ |
| /* <Return> */ |
| /* The distance in F26dot6 format. */ |
| /* */ |
| static FT_F26Dot6 |
| Project_y( TT_ExecContext exc, |
| FT_Pos dx, |
| FT_Pos dy ) |
| { |
| FT_UNUSED( exc ); |
| FT_UNUSED( dx ); |
| |
| return dy; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Compute_Funcs */ |
| /* */ |
| /* <Description> */ |
| /* Computes the projection and movement function pointers according */ |
| /* to the current graphics state. */ |
| /* */ |
| static void |
| Compute_Funcs( TT_ExecContext exc ) |
| { |
| if ( exc->GS.freeVector.x == 0x4000 ) |
| exc->F_dot_P = exc->GS.projVector.x; |
| else if ( exc->GS.freeVector.y == 0x4000 ) |
| exc->F_dot_P = exc->GS.projVector.y; |
| else |
| exc->F_dot_P = |
| ( (FT_Long)exc->GS.projVector.x * exc->GS.freeVector.x + |
| (FT_Long)exc->GS.projVector.y * exc->GS.freeVector.y ) >> 14; |
| |
| if ( exc->GS.projVector.x == 0x4000 ) |
| exc->func_project = (TT_Project_Func)Project_x; |
| else if ( exc->GS.projVector.y == 0x4000 ) |
| exc->func_project = (TT_Project_Func)Project_y; |
| else |
| exc->func_project = (TT_Project_Func)Project; |
| |
| if ( exc->GS.dualVector.x == 0x4000 ) |
| exc->func_dualproj = (TT_Project_Func)Project_x; |
| else if ( exc->GS.dualVector.y == 0x4000 ) |
| exc->func_dualproj = (TT_Project_Func)Project_y; |
| else |
| exc->func_dualproj = (TT_Project_Func)Dual_Project; |
| |
| exc->func_move = (TT_Move_Func)Direct_Move; |
| exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig; |
| |
| if ( exc->F_dot_P == 0x4000L ) |
| { |
| if ( exc->GS.freeVector.x == 0x4000 ) |
| { |
| exc->func_move = (TT_Move_Func)Direct_Move_X; |
| exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_X; |
| } |
| else if ( exc->GS.freeVector.y == 0x4000 ) |
| { |
| exc->func_move = (TT_Move_Func)Direct_Move_Y; |
| exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y; |
| } |
| } |
| |
| /* at small sizes, F_dot_P can become too small, resulting */ |
| /* in overflows and `spikes' in a number of glyphs like `w'. */ |
| |
| if ( FT_ABS( exc->F_dot_P ) < 0x400L ) |
| exc->F_dot_P = 0x4000L; |
| |
| /* Disable cached aspect ratio */ |
| exc->tt_metrics.ratio = 0; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> */ |
| /* Normalize */ |
| /* */ |
| /* <Description> */ |
| /* Norms a vector. */ |
| /* */ |
| /* <Input> */ |
| /* Vx :: The horizontal input vector coordinate. */ |
| /* Vy :: The vertical input vector coordinate. */ |
| /* */ |
| /* <Output> */ |
| /* R :: The normed unit vector. */ |
| /* */ |
| /* <Return> */ |
| /* Returns FAILURE if a vector parameter is zero. */ |
| /* */ |
| /* <Note> */ |
| /* In case Vx and Vy are both zero, `Normalize' returns SUCCESS, and */ |
| /* R is undefined. */ |
| /* */ |
| static FT_Bool |
| Normalize( FT_F26Dot6 Vx, |
| FT_F26Dot6 Vy, |
| FT_UnitVector* R ) |
| { |
| FT_Vector V; |
| |
| |
| if ( Vx == 0 && Vy == 0 ) |
| { |
| /* XXX: UNDOCUMENTED! It seems that it is possible to try */ |
| /* to normalize the vector (0,0). Return immediately. */ |
| return SUCCESS; |
| } |
| |
| V.x = Vx; |
| V.y = Vy; |
| |
| FT_Vector_NormLen( &V ); |
| |
| R->x = (FT_F2Dot14)( V.x / 4 ); |
| R->y = (FT_F2Dot14)( V.y / 4 ); |
| |
| return SUCCESS; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* Here we start with the implementation of the various opcodes. */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| #define ARRAY_BOUND_ERROR \ |
| do \ |
| { \ |
| exc->error = FT_THROW( Invalid_Reference ); \ |
| return; \ |
| } while (0) |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MPPEM[]: Measure Pixel Per EM */ |
| /* Opcode range: 0x4B */ |
| /* Stack: --> Euint16 */ |
| /* */ |
| static void |
| Ins_MPPEM( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| args[0] = exc->func_cur_ppem( exc ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MPS[]: Measure Point Size */ |
| /* Opcode range: 0x4C */ |
| /* Stack: --> Euint16 */ |
| /* */ |
| static void |
| Ins_MPS( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( NO_SUBPIXEL_HINTING ) |
| { |
| /* Microsoft's GDI bytecode interpreter always returns value 12; */ |
| /* we return the current PPEM value instead. */ |
| args[0] = exc->func_cur_ppem( exc ); |
| } |
| else |
| { |
| /* A possible practical application of the MPS instruction is to */ |
| /* implement optical scaling and similar features, which should be */ |
| /* based on perceptual attributes, thus independent of the */ |
| /* resolution. */ |
| args[0] = exc->pointSize; |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* DUP[]: DUPlicate the stack's top element */ |
| /* Opcode range: 0x20 */ |
| /* Stack: StkElt --> StkElt StkElt */ |
| /* */ |
| static void |
| Ins_DUP( FT_Long* args ) |
| { |
| args[1] = args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* POP[]: POP the stack's top element */ |
| /* Opcode range: 0x21 */ |
| /* Stack: StkElt --> */ |
| /* */ |
| static void |
| Ins_POP( void ) |
| { |
| /* nothing to do */ |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* CLEAR[]: CLEAR the entire stack */ |
| /* Opcode range: 0x22 */ |
| /* Stack: StkElt... --> */ |
| /* */ |
| static void |
| Ins_CLEAR( TT_ExecContext exc ) |
| { |
| exc->new_top = 0; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SWAP[]: SWAP the stack's top two elements */ |
| /* Opcode range: 0x23 */ |
| /* Stack: 2 * StkElt --> 2 * StkElt */ |
| /* */ |
| static void |
| Ins_SWAP( FT_Long* args ) |
| { |
| FT_Long L; |
| |
| |
| L = args[0]; |
| args[0] = args[1]; |
| args[1] = L; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* DEPTH[]: return the stack DEPTH */ |
| /* Opcode range: 0x24 */ |
| /* Stack: --> uint32 */ |
| /* */ |
| static void |
| Ins_DEPTH( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| args[0] = exc->top; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* LT[]: Less Than */ |
| /* Opcode range: 0x50 */ |
| /* Stack: int32? int32? --> bool */ |
| /* */ |
| static void |
| Ins_LT( FT_Long* args ) |
| { |
| args[0] = ( args[0] < args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* LTEQ[]: Less Than or EQual */ |
| /* Opcode range: 0x51 */ |
| /* Stack: int32? int32? --> bool */ |
| /* */ |
| static void |
| Ins_LTEQ( FT_Long* args ) |
| { |
| args[0] = ( args[0] <= args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* GT[]: Greater Than */ |
| /* Opcode range: 0x52 */ |
| /* Stack: int32? int32? --> bool */ |
| /* */ |
| static void |
| Ins_GT( FT_Long* args ) |
| { |
| args[0] = ( args[0] > args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* GTEQ[]: Greater Than or EQual */ |
| /* Opcode range: 0x53 */ |
| /* Stack: int32? int32? --> bool */ |
| /* */ |
| static void |
| Ins_GTEQ( FT_Long* args ) |
| { |
| args[0] = ( args[0] >= args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* EQ[]: EQual */ |
| /* Opcode range: 0x54 */ |
| /* Stack: StkElt StkElt --> bool */ |
| /* */ |
| static void |
| Ins_EQ( FT_Long* args ) |
| { |
| args[0] = ( args[0] == args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* NEQ[]: Not EQual */ |
| /* Opcode range: 0x55 */ |
| /* Stack: StkElt StkElt --> bool */ |
| /* */ |
| static void |
| Ins_NEQ( FT_Long* args ) |
| { |
| args[0] = ( args[0] != args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ODD[]: Is ODD */ |
| /* Opcode range: 0x56 */ |
| /* Stack: f26.6 --> bool */ |
| /* */ |
| static void |
| Ins_ODD( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 64 ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* EVEN[]: Is EVEN */ |
| /* Opcode range: 0x57 */ |
| /* Stack: f26.6 --> bool */ |
| /* */ |
| static void |
| Ins_EVEN( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 0 ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* AND[]: logical AND */ |
| /* Opcode range: 0x5A */ |
| /* Stack: uint32 uint32 --> uint32 */ |
| /* */ |
| static void |
| Ins_AND( FT_Long* args ) |
| { |
| args[0] = ( args[0] && args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* OR[]: logical OR */ |
| /* Opcode range: 0x5B */ |
| /* Stack: uint32 uint32 --> uint32 */ |
| /* */ |
| static void |
| Ins_OR( FT_Long* args ) |
| { |
| args[0] = ( args[0] || args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* NOT[]: logical NOT */ |
| /* Opcode range: 0x5C */ |
| /* Stack: StkElt --> uint32 */ |
| /* */ |
| static void |
| Ins_NOT( FT_Long* args ) |
| { |
| args[0] = !args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ADD[]: ADD */ |
| /* Opcode range: 0x60 */ |
| /* Stack: f26.6 f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_ADD( FT_Long* args ) |
| { |
| args[0] = ADD_LONG( args[0], args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SUB[]: SUBtract */ |
| /* Opcode range: 0x61 */ |
| /* Stack: f26.6 f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_SUB( FT_Long* args ) |
| { |
| args[0] = SUB_LONG( args[0], args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* DIV[]: DIVide */ |
| /* Opcode range: 0x62 */ |
| /* Stack: f26.6 f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_DIV( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( args[1] == 0 ) |
| exc->error = FT_THROW( Divide_By_Zero ); |
| else |
| args[0] = FT_MulDiv_No_Round( args[0], 64L, args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MUL[]: MULtiply */ |
| /* Opcode range: 0x63 */ |
| /* Stack: f26.6 f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_MUL( FT_Long* args ) |
| { |
| args[0] = FT_MulDiv( args[0], args[1], 64L ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ABS[]: ABSolute value */ |
| /* Opcode range: 0x64 */ |
| /* Stack: f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_ABS( FT_Long* args ) |
| { |
| if ( args[0] < 0 ) |
| args[0] = NEG_LONG( args[0] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* NEG[]: NEGate */ |
| /* Opcode range: 0x65 */ |
| /* Stack: f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_NEG( FT_Long* args ) |
| { |
| args[0] = NEG_LONG( args[0] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* FLOOR[]: FLOOR */ |
| /* Opcode range: 0x66 */ |
| /* Stack: f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_FLOOR( FT_Long* args ) |
| { |
| args[0] = FT_PIX_FLOOR( args[0] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* CEILING[]: CEILING */ |
| /* Opcode range: 0x67 */ |
| /* Stack: f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_CEILING( FT_Long* args ) |
| { |
| args[0] = FT_PIX_CEIL( args[0] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* RS[]: Read Store */ |
| /* Opcode range: 0x43 */ |
| /* Stack: uint32 --> uint32 */ |
| /* */ |
| static void |
| Ins_RS( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong I = (FT_ULong)args[0]; |
| |
| |
| if ( BOUNDSL( I, exc->storeSize ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| ARRAY_BOUND_ERROR; |
| else |
| args[0] = 0; |
| } |
| else |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* subpixel hinting - avoid Typeman Dstroke and */ |
| /* IStroke and Vacuform rounds */ |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| ( ( I == 24 && |
| ( exc->face->sph_found_func_flags & |
| ( SPH_FDEF_SPACING_1 | |
| SPH_FDEF_SPACING_2 ) ) ) || |
| ( I == 22 && |
| ( exc->sph_in_func_flags & |
| SPH_FDEF_TYPEMAN_STROKES ) ) || |
| ( I == 8 && |
| ( exc->face->sph_found_func_flags & |
| SPH_FDEF_VACUFORM_ROUND_1 ) && |
| exc->iup_called ) ) ) |
| args[0] = 0; |
| else |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| args[0] = exc->storage[I]; |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* WS[]: Write Store */ |
| /* Opcode range: 0x42 */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_WS( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong I = (FT_ULong)args[0]; |
| |
| |
| if ( BOUNDSL( I, exc->storeSize ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| ARRAY_BOUND_ERROR; |
| } |
| else |
| exc->storage[I] = args[1]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* WCVTP[]: Write CVT in Pixel units */ |
| /* Opcode range: 0x44 */ |
| /* Stack: f26.6 uint32 --> */ |
| /* */ |
| static void |
| Ins_WCVTP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong I = (FT_ULong)args[0]; |
| |
| |
| if ( BOUNDSL( I, exc->cvtSize ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| ARRAY_BOUND_ERROR; |
| } |
| else |
| exc->func_write_cvt( exc, I, args[1] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* WCVTF[]: Write CVT in Funits */ |
| /* Opcode range: 0x70 */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_WCVTF( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong I = (FT_ULong)args[0]; |
| |
| |
| if ( BOUNDSL( I, exc->cvtSize ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| ARRAY_BOUND_ERROR; |
| } |
| else |
| exc->cvt[I] = FT_MulFix( args[1], exc->tt_metrics.scale ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* RCVT[]: Read CVT */ |
| /* Opcode range: 0x45 */ |
| /* Stack: uint32 --> f26.6 */ |
| /* */ |
| static void |
| Ins_RCVT( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong I = (FT_ULong)args[0]; |
| |
| |
| if ( BOUNDSL( I, exc->cvtSize ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| ARRAY_BOUND_ERROR; |
| else |
| args[0] = 0; |
| } |
| else |
| args[0] = exc->func_read_cvt( exc, I ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* AA[]: Adjust Angle */ |
| /* Opcode range: 0x7F */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_AA( void ) |
| { |
| /* 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( TT_ExecContext exc ) |
| { |
| exc->error = FT_THROW( Debug_OpCode ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ROUND[ab]: ROUND value */ |
| /* Opcode range: 0x68-0x6B */ |
| /* Stack: f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_ROUND( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| args[0] = exc->func_round( |
| exc, |
| args[0], |
| exc->tt_metrics.compensations[exc->opcode - 0x68] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* NROUND[ab]: No ROUNDing of value */ |
| /* Opcode range: 0x6C-0x6F */ |
| /* Stack: f26.6 --> f26.6 */ |
| /* */ |
| static void |
| Ins_NROUND( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| args[0] = Round_None( |
| exc, |
| args[0], |
| exc->tt_metrics.compensations[exc->opcode - 0x6C] ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MAX[]: MAXimum */ |
| /* Opcode range: 0x8B */ |
| /* Stack: int32? int32? --> int32 */ |
| /* */ |
| static void |
| Ins_MAX( FT_Long* args ) |
| { |
| if ( args[1] > args[0] ) |
| args[0] = args[1]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MIN[]: MINimum */ |
| /* Opcode range: 0x8C */ |
| /* Stack: int32? int32? --> int32 */ |
| /* */ |
| static void |
| Ins_MIN( FT_Long* args ) |
| { |
| if ( args[1] < args[0] ) |
| args[0] = args[1]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MINDEX[]: Move INDEXed element */ |
| /* Opcode range: 0x26 */ |
| /* Stack: int32? --> StkElt */ |
| /* */ |
| static void |
| Ins_MINDEX( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_Long L, K; |
| |
| |
| L = args[0]; |
| |
| if ( L <= 0 || L > exc->args ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| } |
| else |
| { |
| K = exc->stack[exc->args - L]; |
| |
| FT_ARRAY_MOVE( &exc->stack[exc->args - L ], |
| &exc->stack[exc->args - L + 1], |
| ( L - 1 ) ); |
| |
| exc->stack[exc->args - 1] = K; |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* CINDEX[]: Copy INDEXed element */ |
| /* Opcode range: 0x25 */ |
| /* Stack: int32 --> StkElt */ |
| /* */ |
| static void |
| Ins_CINDEX( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_Long L; |
| |
| |
| L = args[0]; |
| |
| if ( L <= 0 || L > exc->args ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| args[0] = 0; |
| } |
| else |
| args[0] = exc->stack[exc->args - L]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ROLL[]: ROLL top three elements */ |
| /* Opcode range: 0x8A */ |
| /* Stack: 3 * StkElt --> 3 * StkElt */ |
| /* */ |
| static void |
| Ins_ROLL( FT_Long* args ) |
| { |
| FT_Long A, B, C; |
| |
| |
| A = args[2]; |
| B = args[1]; |
| C = args[0]; |
| |
| args[2] = C; |
| args[1] = A; |
| args[0] = B; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MANAGING THE FLOW OF CONTROL */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SLOOP[]: Set LOOP variable */ |
| /* Opcode range: 0x17 */ |
| /* Stack: int32? --> */ |
| /* */ |
| static void |
| Ins_SLOOP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( args[0] < 0 ) |
| exc->error = FT_THROW( Bad_Argument ); |
| else |
| exc->GS.loop = args[0]; |
| } |
| |
| |
| static FT_Bool |
| SkipCode( TT_ExecContext exc ) |
| { |
| exc->IP += exc->length; |
| |
| if ( exc->IP < exc->codeSize ) |
| { |
| exc->opcode = exc->code[exc->IP]; |
| |
| exc->length = opcode_length[exc->opcode]; |
| if ( exc->length < 0 ) |
| { |
| if ( exc->IP + 1 >= exc->codeSize ) |
| goto Fail_Overflow; |
| exc->length = 2 - exc->length * exc->code[exc->IP + 1]; |
| } |
| |
| if ( exc->IP + exc->length <= exc->codeSize ) |
| return SUCCESS; |
| } |
| |
| Fail_Overflow: |
| exc->error = FT_THROW( Code_Overflow ); |
| return FAILURE; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* IF[]: IF test */ |
| /* Opcode range: 0x58 */ |
| /* Stack: StkElt --> */ |
| /* */ |
| static void |
| Ins_IF( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_Int nIfs; |
| FT_Bool Out; |
| |
| |
| if ( args[0] != 0 ) |
| return; |
| |
| nIfs = 1; |
| Out = 0; |
| |
| do |
| { |
| if ( SkipCode( exc ) == FAILURE ) |
| return; |
| |
| switch ( exc->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( TT_ExecContext exc ) |
| { |
| FT_Int nIfs; |
| |
| |
| nIfs = 1; |
| |
| do |
| { |
| if ( SkipCode( exc ) == FAILURE ) |
| return; |
| |
| switch ( exc->opcode ) |
| { |
| case 0x58: /* IF */ |
| nIfs++; |
| break; |
| |
| case 0x59: /* EIF */ |
| nIfs--; |
| break; |
| } |
| } while ( nIfs != 0 ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* EIF[]: End IF */ |
| /* Opcode range: 0x59 */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_EIF( void ) |
| { |
| /* nothing to do */ |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* JMPR[]: JuMP Relative */ |
| /* Opcode range: 0x1C */ |
| /* Stack: int32 --> */ |
| /* */ |
| static void |
| Ins_JMPR( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( args[0] == 0 && exc->args == 0 ) |
| { |
| exc->error = FT_THROW( Bad_Argument ); |
| return; |
| } |
| |
| exc->IP += args[0]; |
| if ( exc->IP < 0 || |
| ( exc->callTop > 0 && |
| exc->IP > exc->callStack[exc->callTop - 1].Def->end ) ) |
| { |
| exc->error = FT_THROW( Bad_Argument ); |
| return; |
| } |
| |
| exc->step_ins = FALSE; |
| |
| if ( args[0] < 0 ) |
| { |
| if ( ++exc->neg_jump_counter > exc->neg_jump_counter_max ) |
| exc->error = FT_THROW( Execution_Too_Long ); |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* JROT[]: Jump Relative On True */ |
| /* Opcode range: 0x78 */ |
| /* Stack: StkElt int32 --> */ |
| /* */ |
| static void |
| Ins_JROT( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( args[1] != 0 ) |
| Ins_JMPR( exc, args ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* JROF[]: Jump Relative On False */ |
| /* Opcode range: 0x79 */ |
| /* Stack: StkElt int32 --> */ |
| /* */ |
| static void |
| Ins_JROF( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( args[1] == 0 ) |
| Ins_JMPR( exc, args ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* FDEF[]: Function DEFinition */ |
| /* Opcode range: 0x2C */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_FDEF( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong n; |
| TT_DefRecord* rec; |
| TT_DefRecord* limit; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* arguments to opcodes are skipped by `SKIP_Code' */ |
| FT_Byte opcode_pattern[9][12] = { |
| /* #0 inline delta function 1 */ |
| { |
| 0x4B, /* PPEM */ |
| 0x53, /* GTEQ */ |
| 0x23, /* SWAP */ |
| 0x4B, /* PPEM */ |
| 0x51, /* LTEQ */ |
| 0x5A, /* AND */ |
| 0x58, /* IF */ |
| 0x38, /* SHPIX */ |
| 0x1B, /* ELSE */ |
| 0x21, /* POP */ |
| 0x21, /* POP */ |
| 0x59 /* EIF */ |
| }, |
| /* #1 inline delta function 2 */ |
| { |
| 0x4B, /* PPEM */ |
| 0x54, /* EQ */ |
| 0x58, /* IF */ |
| 0x38, /* SHPIX */ |
| 0x1B, /* ELSE */ |
| 0x21, /* POP */ |
| 0x21, /* POP */ |
| 0x59 /* EIF */ |
| }, |
| /* #2 diagonal stroke function */ |
| { |
| 0x20, /* DUP */ |
| 0x20, /* DUP */ |
| 0xB0, /* PUSHB_1 */ |
| /* 1 */ |
| 0x60, /* ADD */ |
| 0x46, /* GC_cur */ |
| 0xB0, /* PUSHB_1 */ |
| /* 64 */ |
| 0x23, /* SWAP */ |
| 0x42 /* WS */ |
| }, |
| /* #3 VacuFormRound function */ |
| { |
| 0x45, /* RCVT */ |
| 0x23, /* SWAP */ |
| 0x46, /* GC_cur */ |
| 0x60, /* ADD */ |
| 0x20, /* DUP */ |
| 0xB0 /* PUSHB_1 */ |
| /* 38 */ |
| }, |
| /* #4 TTFautohint bytecode (old) */ |
| { |
| 0x20, /* DUP */ |
| 0x64, /* ABS */ |
| 0xB0, /* PUSHB_1 */ |
| /* 32 */ |
| 0x60, /* ADD */ |
| 0x66, /* FLOOR */ |
| 0x23, /* SWAP */ |
| 0xB0 /* PUSHB_1 */ |
| }, |
| /* #5 spacing function 1 */ |
| { |
| 0x01, /* SVTCA_x */ |
| 0xB0, /* PUSHB_1 */ |
| /* 24 */ |
| 0x43, /* RS */ |
| 0x58 /* IF */ |
| }, |
| /* #6 spacing function 2 */ |
| { |
| 0x01, /* SVTCA_x */ |
| 0x18, /* RTG */ |
| 0xB0, /* PUSHB_1 */ |
| /* 24 */ |
| 0x43, /* RS */ |
| 0x58 /* IF */ |
| }, |
| /* #7 TypeMan Talk DiagEndCtrl function */ |
| { |
| 0x01, /* SVTCA_x */ |
| 0x20, /* DUP */ |
| 0xB0, /* PUSHB_1 */ |
| /* 3 */ |
| 0x25, /* CINDEX */ |
| }, |
| /* #8 TypeMan Talk Align */ |
| { |
| 0x06, /* SPVTL */ |
| 0x7D, /* RDTG */ |
| }, |
| }; |
| FT_UShort opcode_patterns = 9; |
| FT_UShort opcode_pointer[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| FT_UShort opcode_size[9] = { 12, 8, 8, 6, 7, 4, 5, 4, 2 }; |
| FT_UShort i; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| |
| /* FDEF is only allowed in `prep' or `fpgm' */ |
| if ( exc->curRange == tt_coderange_glyph ) |
| { |
| exc->error = FT_THROW( DEF_In_Glyf_Bytecode ); |
| return; |
| } |
| |
| /* some font programs are broken enough to redefine functions! */ |
| /* We will then parse the current table. */ |
| |
| rec = exc->FDefs; |
| limit = rec + exc->numFDefs; |
| n = (FT_ULong)args[0]; |
| |
| for ( ; rec < limit; rec++ ) |
| { |
| if ( rec->opc == n ) |
| break; |
| } |
| |
| if ( rec == limit ) |
| { |
| /* check that there is enough room for new functions */ |
| if ( exc->numFDefs >= exc->maxFDefs ) |
| { |
| exc->error = FT_THROW( Too_Many_Function_Defs ); |
| return; |
| } |
| exc->numFDefs++; |
| } |
| |
| /* Although FDEF takes unsigned 32-bit integer, */ |
| /* func # must be within unsigned 16-bit integer */ |
| if ( n > 0xFFFFU ) |
| { |
| exc->error = FT_THROW( Too_Many_Function_Defs ); |
| return; |
| } |
| |
| rec->range = exc->curRange; |
| rec->opc = (FT_UInt16)n; |
| rec->start = exc->IP + 1; |
| rec->active = TRUE; |
| rec->inline_delta = FALSE; |
| rec->sph_fdef_flags = 0x0000; |
| |
| if ( n > exc->maxFunc ) |
| exc->maxFunc = (FT_UInt16)n; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* We don't know for sure these are typeman functions, */ |
| /* however they are only active when RS 22 is called */ |
| if ( n >= 64 && n <= 66 ) |
| rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_STROKES; |
| #endif |
| |
| /* Now skip the whole function definition. */ |
| /* We don't allow nested IDEFS & FDEFs. */ |
| |
| while ( SkipCode( exc ) == SUCCESS ) |
| { |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| |
| if ( SUBPIXEL_HINTING_INFINALITY ) |
| { |
| for ( i = 0; i < opcode_patterns; i++ ) |
| { |
| if ( opcode_pointer[i] < opcode_size[i] && |
| exc->opcode == opcode_pattern[i][opcode_pointer[i]] ) |
| { |
| opcode_pointer[i] += 1; |
| |
| if ( opcode_pointer[i] == opcode_size[i] ) |
| { |
| FT_TRACE6(( "sph: Function %d, opcode ptrn: %d, %s %s\n", |
| i, n, |
| exc->face->root.family_name, |
| exc->face->root.style_name )); |
| |
| switch ( i ) |
| { |
| case 0: |
| rec->sph_fdef_flags |= SPH_FDEF_INLINE_DELTA_1; |
| exc->face->sph_found_func_flags |= SPH_FDEF_INLINE_DELTA_1; |
| break; |
| |
| case 1: |
| rec->sph_fdef_flags |= SPH_FDEF_INLINE_DELTA_2; |
| exc->face->sph_found_func_flags |= SPH_FDEF_INLINE_DELTA_2; |
| break; |
| |
| case 2: |
| switch ( n ) |
| { |
| /* needs to be implemented still */ |
| case 58: |
| rec->sph_fdef_flags |= SPH_FDEF_DIAGONAL_STROKE; |
| exc->face->sph_found_func_flags |= SPH_FDEF_DIAGONAL_STROKE; |
| } |
| break; |
| |
| case 3: |
| switch ( n ) |
| { |
| case 0: |
| rec->sph_fdef_flags |= SPH_FDEF_VACUFORM_ROUND_1; |
| exc->face->sph_found_func_flags |= SPH_FDEF_VACUFORM_ROUND_1; |
| } |
| break; |
| |
| case 4: |
| /* probably not necessary to detect anymore */ |
| rec->sph_fdef_flags |= SPH_FDEF_TTFAUTOHINT_1; |
| exc->face->sph_found_func_flags |= SPH_FDEF_TTFAUTOHINT_1; |
| break; |
| |
| case 5: |
| switch ( n ) |
| { |
| case 0: |
| case 1: |
| case 2: |
| case 4: |
| case 7: |
| case 8: |
| rec->sph_fdef_flags |= SPH_FDEF_SPACING_1; |
| exc->face->sph_found_func_flags |= SPH_FDEF_SPACING_1; |
| } |
| break; |
| |
| case 6: |
| switch ( n ) |
| { |
| case 0: |
| case 1: |
| case 2: |
| case 4: |
| case 7: |
| case 8: |
| rec->sph_fdef_flags |= SPH_FDEF_SPACING_2; |
| exc->face->sph_found_func_flags |= SPH_FDEF_SPACING_2; |
| } |
| break; |
| |
| case 7: |
| rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; |
| exc->face->sph_found_func_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; |
| break; |
| |
| case 8: |
| #if 0 |
| rec->sph_fdef_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; |
| exc->face->sph_found_func_flags |= SPH_FDEF_TYPEMAN_DIAGENDCTRL; |
| #endif |
| break; |
| } |
| opcode_pointer[i] = 0; |
| } |
| } |
| |
| else |
| opcode_pointer[i] = 0; |
| } |
| |
| /* Set sph_compatibility_mode only when deltas are detected */ |
| exc->face->sph_compatibility_mode = |
| ( ( exc->face->sph_found_func_flags & SPH_FDEF_INLINE_DELTA_1 ) | |
| ( exc->face->sph_found_func_flags & SPH_FDEF_INLINE_DELTA_2 ) ); |
| } |
| |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| switch ( exc->opcode ) |
| { |
| case 0x89: /* IDEF */ |
| case 0x2C: /* FDEF */ |
| exc->error = FT_THROW( Nested_DEFS ); |
| return; |
| |
| case 0x2D: /* ENDF */ |
| rec->end = exc->IP; |
| return; |
| } |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ENDF[]: END Function definition */ |
| /* Opcode range: 0x2D */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_ENDF( TT_ExecContext exc ) |
| { |
| TT_CallRec* pRec; |
| |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| exc->sph_in_func_flags = 0x0000; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| if ( exc->callTop <= 0 ) /* We encountered an ENDF without a call */ |
| { |
| exc->error = FT_THROW( ENDF_In_Exec_Stream ); |
| return; |
| } |
| |
| exc->callTop--; |
| |
| pRec = &exc->callStack[exc->callTop]; |
| |
| pRec->Cur_Count--; |
| |
| exc->step_ins = FALSE; |
| |
| if ( pRec->Cur_Count > 0 ) |
| { |
| exc->callTop++; |
| exc->IP = pRec->Def->start; |
| } |
| else |
| /* Loop through the current function */ |
| Ins_Goto_CodeRange( exc, 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( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong F; |
| TT_CallRec* pCrec; |
| TT_DefRecord* def; |
| |
| |
| /* first of all, check the index */ |
| |
| F = (FT_ULong)args[0]; |
| if ( BOUNDSL( F, exc->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 */ |
| /* */ |
| /* exc->maxFunc+1 == exc->numFDefs */ |
| /* exc->FDefs[n].opc == n for n in 0..exc->maxFunc */ |
| /* */ |
| /* If this isn't true, we need to look up the function table. */ |
| |
| def = exc->FDefs + F; |
| if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F ) |
| { |
| /* look up the FDefs table */ |
| TT_DefRecord* limit; |
| |
| |
| def = exc->FDefs; |
| limit = def + exc->numFDefs; |
| |
| while ( def < limit && def->opc != F ) |
| def++; |
| |
| if ( def == limit ) |
| goto Fail; |
| } |
| |
| /* check that the function is active */ |
| if ( !def->active ) |
| goto Fail; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| ( ( exc->iup_called && |
| ( exc->sph_tweak_flags & SPH_TWEAK_NO_CALL_AFTER_IUP ) ) || |
| ( def->sph_fdef_flags & SPH_FDEF_VACUFORM_ROUND_1 ) ) ) |
| goto Fail; |
| else |
| exc->sph_in_func_flags = def->sph_fdef_flags; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| /* check the call stack */ |
| if ( exc->callTop >= exc->callSize ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| return; |
| } |
| |
| pCrec = exc->callStack + exc->callTop; |
| |
| pCrec->Caller_Range = exc->curRange; |
| pCrec->Caller_IP = exc->IP + 1; |
| pCrec->Cur_Count = 1; |
| pCrec->Def = def; |
| |
| exc->callTop++; |
| |
| Ins_Goto_CodeRange( exc, def->range, def->start ); |
| |
| exc->step_ins = FALSE; |
| |
| return; |
| |
| Fail: |
| exc->error = FT_THROW( Invalid_Reference ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* LOOPCALL[]: LOOP and CALL function */ |
| /* Opcode range: 0x2A */ |
| /* Stack: uint32? Eint16? --> */ |
| /* */ |
| static void |
| Ins_LOOPCALL( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong F; |
| TT_CallRec* pCrec; |
| TT_DefRecord* def; |
| |
| |
| /* first of all, check the index */ |
| F = (FT_ULong)args[1]; |
| if ( BOUNDSL( F, exc->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 */ |
| /* */ |
| /* exc->maxFunc+1 == exc->numFDefs */ |
| /* exc->FDefs[n].opc == n for n in 0..exc->maxFunc */ |
| /* */ |
| /* If this isn't true, we need to look up the function table. */ |
| |
| def = exc->FDefs + F; |
| if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F ) |
| { |
| /* look up the FDefs table */ |
| TT_DefRecord* limit; |
| |
| |
| def = exc->FDefs; |
| limit = def + exc->numFDefs; |
| |
| while ( def < limit && def->opc != F ) |
| def++; |
| |
| if ( def == limit ) |
| goto Fail; |
| } |
| |
| /* check that the function is active */ |
| if ( !def->active ) |
| goto Fail; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| ( def->sph_fdef_flags & SPH_FDEF_VACUFORM_ROUND_1 ) ) |
| goto Fail; |
| else |
| exc->sph_in_func_flags = def->sph_fdef_flags; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| /* check stack */ |
| if ( exc->callTop >= exc->callSize ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| return; |
| } |
| |
| if ( args[0] > 0 ) |
| { |
| pCrec = exc->callStack + exc->callTop; |
| |
| pCrec->Caller_Range = exc->curRange; |
| pCrec->Caller_IP = exc->IP + 1; |
| pCrec->Cur_Count = (FT_Int)args[0]; |
| pCrec->Def = def; |
| |
| exc->callTop++; |
| |
| Ins_Goto_CodeRange( exc, def->range, def->start ); |
| |
| exc->step_ins = FALSE; |
| |
| exc->loopcall_counter += (FT_ULong)args[0]; |
| if ( exc->loopcall_counter > exc->loopcall_counter_max ) |
| exc->error = FT_THROW( Execution_Too_Long ); |
| } |
| |
| return; |
| |
| Fail: |
| exc->error = FT_THROW( Invalid_Reference ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* IDEF[]: Instruction DEFinition */ |
| /* Opcode range: 0x89 */ |
| /* Stack: Eint8 --> */ |
| /* */ |
| static void |
| Ins_IDEF( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| TT_DefRecord* def; |
| TT_DefRecord* limit; |
| |
| |
| /* we enable IDEF only in `prep' or `fpgm' */ |
| if ( exc->curRange == tt_coderange_glyph ) |
| { |
| exc->error = FT_THROW( DEF_In_Glyf_Bytecode ); |
| return; |
| } |
| |
| /* First of all, look for the same function in our table */ |
| |
| def = exc->IDefs; |
| limit = def + exc->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 ( exc->numIDefs >= exc->maxIDefs ) |
| { |
| exc->error = FT_THROW( Too_Many_Instruction_Defs ); |
| return; |
| } |
| exc->numIDefs++; |
| } |
| |
| /* opcode must be unsigned 8-bit integer */ |
| if ( 0 > args[0] || args[0] > 0x00FF ) |
| { |
| exc->error = FT_THROW( Too_Many_Instruction_Defs ); |
| return; |
| } |
| |
| def->opc = (FT_Byte)args[0]; |
| def->start = exc->IP + 1; |
| def->range = exc->curRange; |
| def->active = TRUE; |
| |
| if ( (FT_ULong)args[0] > exc->maxIns ) |
| exc->maxIns = (FT_Byte)args[0]; |
| |
| /* Now skip the whole function definition. */ |
| /* We don't allow nested IDEFs & FDEFs. */ |
| |
| while ( SkipCode( exc ) == SUCCESS ) |
| { |
| switch ( exc->opcode ) |
| { |
| case 0x89: /* IDEF */ |
| case 0x2C: /* FDEF */ |
| exc->error = FT_THROW( Nested_DEFS ); |
| return; |
| case 0x2D: /* ENDF */ |
| def->end = exc->IP; |
| return; |
| } |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* PUSHING DATA ONTO THE INTERPRETER STACK */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* NPUSHB[]: PUSH N Bytes */ |
| /* Opcode range: 0x40 */ |
| /* Stack: --> uint32... */ |
| /* */ |
| static void |
| Ins_NPUSHB( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort L, K; |
| |
| |
| L = (FT_UShort)exc->code[exc->IP + 1]; |
| |
| if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| return; |
| } |
| |
| for ( K = 1; K <= L; K++ ) |
| args[K - 1] = exc->code[exc->IP + K + 1]; |
| |
| exc->new_top += L; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* NPUSHW[]: PUSH N Words */ |
| /* Opcode range: 0x41 */ |
| /* Stack: --> int32... */ |
| /* */ |
| static void |
| Ins_NPUSHW( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort L, K; |
| |
| |
| L = (FT_UShort)exc->code[exc->IP + 1]; |
| |
| if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| return; |
| } |
| |
| exc->IP += 2; |
| |
| for ( K = 0; K < L; K++ ) |
| args[K] = GetShortIns( exc ); |
| |
| exc->step_ins = FALSE; |
| exc->new_top += L; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* PUSHB[abc]: PUSH Bytes */ |
| /* Opcode range: 0xB0-0xB7 */ |
| /* Stack: --> uint32... */ |
| /* */ |
| static void |
| Ins_PUSHB( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort L, K; |
| |
| |
| L = (FT_UShort)( exc->opcode - 0xB0 + 1 ); |
| |
| if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| return; |
| } |
| |
| for ( K = 1; K <= L; K++ ) |
| args[K - 1] = exc->code[exc->IP + K]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* PUSHW[abc]: PUSH Words */ |
| /* Opcode range: 0xB8-0xBF */ |
| /* Stack: --> int32... */ |
| /* */ |
| static void |
| Ins_PUSHW( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort L, K; |
| |
| |
| L = (FT_UShort)( exc->opcode - 0xB8 + 1 ); |
| |
| if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| return; |
| } |
| |
| exc->IP++; |
| |
| for ( K = 0; K < L; K++ ) |
| args[K] = GetShortIns( exc ); |
| |
| exc->step_ins = FALSE; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MANAGING THE GRAPHICS STATE */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| static FT_Bool |
| Ins_SxVTL( TT_ExecContext exc, |
| FT_UShort aIdx1, |
| FT_UShort aIdx2, |
| FT_UnitVector* Vec ) |
| { |
| FT_Long A, B, C; |
| FT_Vector* p1; |
| FT_Vector* p2; |
| |
| FT_Byte opcode = exc->opcode; |
| |
| |
| if ( BOUNDS( aIdx1, exc->zp2.n_points ) || |
| BOUNDS( aIdx2, exc->zp1.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return FAILURE; |
| } |
| |
| p1 = exc->zp1.cur + aIdx2; |
| p2 = exc->zp2.cur + aIdx1; |
| |
| A = SUB_LONG( p1->x, p2->x ); |
| B = SUB_LONG( 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; |
| opcode = 0; |
| } |
| |
| if ( ( opcode & 1 ) != 0 ) |
| { |
| C = B; /* counter clockwise rotation */ |
| B = A; |
| A = NEG_LONG( C ); |
| } |
| |
| Normalize( A, B, Vec ); |
| |
| return SUCCESS; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SVTCA[a]: Set (F and P) Vectors to Coordinate Axis */ |
| /* Opcode range: 0x00-0x01 */ |
| /* Stack: --> */ |
| /* */ |
| /* SPvTCA[a]: Set PVector to Coordinate Axis */ |
| /* Opcode range: 0x02-0x03 */ |
| /* Stack: --> */ |
| /* */ |
| /* SFvTCA[a]: Set FVector to Coordinate Axis */ |
| /* Opcode range: 0x04-0x05 */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_SxyTCA( TT_ExecContext exc ) |
| { |
| FT_Short AA, BB; |
| |
| FT_Byte opcode = exc->opcode; |
| |
| |
| AA = (FT_Short)( ( opcode & 1 ) << 14 ); |
| BB = (FT_Short)( AA ^ 0x4000 ); |
| |
| if ( opcode < 4 ) |
| { |
| exc->GS.projVector.x = AA; |
| exc->GS.projVector.y = BB; |
| |
| exc->GS.dualVector.x = AA; |
| exc->GS.dualVector.y = BB; |
| } |
| |
| if ( ( opcode & 2 ) == 0 ) |
| { |
| exc->GS.freeVector.x = AA; |
| exc->GS.freeVector.y = BB; |
| } |
| |
| Compute_Funcs( exc ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SPvTL[a]: Set PVector To Line */ |
| /* Opcode range: 0x06-0x07 */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_SPVTL( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( Ins_SxVTL( exc, |
| (FT_UShort)args[1], |
| (FT_UShort)args[0], |
| &exc->GS.projVector ) == SUCCESS ) |
| { |
| exc->GS.dualVector = exc->GS.projVector; |
| Compute_Funcs( exc ); |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SFvTL[a]: Set FVector To Line */ |
| /* Opcode range: 0x08-0x09 */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_SFVTL( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( Ins_SxVTL( exc, |
| (FT_UShort)args[1], |
| (FT_UShort)args[0], |
| &exc->GS.freeVector ) == SUCCESS ) |
| { |
| Compute_Funcs( exc ); |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SFvTPv[]: Set FVector To PVector */ |
| /* Opcode range: 0x0E */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_SFVTPV( TT_ExecContext exc ) |
| { |
| exc->GS.freeVector = exc->GS.projVector; |
| Compute_Funcs( exc ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SPvFS[]: Set PVector From Stack */ |
| /* Opcode range: 0x0A */ |
| /* Stack: f2.14 f2.14 --> */ |
| /* */ |
| static void |
| Ins_SPVFS( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| 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, &exc->GS.projVector ); |
| |
| exc->GS.dualVector = exc->GS.projVector; |
| Compute_Funcs( exc ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SFvFS[]: Set FVector From Stack */ |
| /* Opcode range: 0x0B */ |
| /* Stack: f2.14 f2.14 --> */ |
| /* */ |
| static void |
| Ins_SFVFS( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| 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, &exc->GS.freeVector ); |
| Compute_Funcs( exc ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* GPv[]: Get Projection Vector */ |
| /* Opcode range: 0x0C */ |
| /* Stack: ef2.14 --> ef2.14 */ |
| /* */ |
| static void |
| Ins_GPV( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| args[0] = exc->GS.projVector.x; |
| args[1] = exc->GS.projVector.y; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* GFv[]: Get Freedom Vector */ |
| /* Opcode range: 0x0D */ |
| /* Stack: ef2.14 --> ef2.14 */ |
| /* */ |
| static void |
| Ins_GFV( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| args[0] = exc->GS.freeVector.x; |
| args[1] = exc->GS.freeVector.y; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SRP0[]: Set Reference Point 0 */ |
| /* Opcode range: 0x10 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SRP0( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| exc->GS.rp0 = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SRP1[]: Set Reference Point 1 */ |
| /* Opcode range: 0x11 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SRP1( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| exc->GS.rp1 = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SRP2[]: Set Reference Point 2 */ |
| /* Opcode range: 0x12 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SRP2( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| exc->GS.rp2 = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SMD[]: Set Minimum Distance */ |
| /* Opcode range: 0x1A */ |
| /* Stack: f26.6 --> */ |
| /* */ |
| static void |
| Ins_SMD( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| exc->GS.minimum_distance = args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SCVTCI[]: Set Control Value Table Cut In */ |
| /* Opcode range: 0x1D */ |
| /* Stack: f26.6 --> */ |
| /* */ |
| static void |
| Ins_SCVTCI( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| exc->GS.control_value_cutin = (FT_F26Dot6)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SSWCI[]: Set Single Width Cut In */ |
| /* Opcode range: 0x1E */ |
| /* Stack: f26.6 --> */ |
| /* */ |
| static void |
| Ins_SSWCI( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| exc->GS.single_width_cutin = (FT_F26Dot6)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SSW[]: Set Single Width */ |
| /* Opcode range: 0x1F */ |
| /* Stack: int32? --> */ |
| /* */ |
| static void |
| Ins_SSW( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| exc->GS.single_width_value = FT_MulFix( args[0], |
| exc->tt_metrics.scale ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* FLIPON[]: Set auto-FLIP to ON */ |
| /* Opcode range: 0x4D */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_FLIPON( TT_ExecContext exc ) |
| { |
| exc->GS.auto_flip = TRUE; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* FLIPOFF[]: Set auto-FLIP to OFF */ |
| /* Opcode range: 0x4E */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_FLIPOFF( TT_ExecContext exc ) |
| { |
| exc->GS.auto_flip = FALSE; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SANGW[]: Set ANGle Weight */ |
| /* Opcode range: 0x7E */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SANGW( void ) |
| { |
| /* instruction not supported anymore */ |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SDB[]: Set Delta Base */ |
| /* Opcode range: 0x5E */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SDB( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| exc->GS.delta_base = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SDS[]: Set Delta Shift */ |
| /* Opcode range: 0x5F */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SDS( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( (FT_ULong)args[0] > 6UL ) |
| exc->error = FT_THROW( Bad_Argument ); |
| else |
| exc->GS.delta_shift = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* RTHG[]: Round To Half Grid */ |
| /* Opcode range: 0x19 */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_RTHG( TT_ExecContext exc ) |
| { |
| exc->GS.round_state = TT_Round_To_Half_Grid; |
| exc->func_round = (TT_Round_Func)Round_To_Half_Grid; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* RTG[]: Round To Grid */ |
| /* Opcode range: 0x18 */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_RTG( TT_ExecContext exc ) |
| { |
| exc->GS.round_state = TT_Round_To_Grid; |
| exc->func_round = (TT_Round_Func)Round_To_Grid; |
| } |
| |
| |
| /*************************************************************************/ |
| /* RTDG[]: Round To Double Grid */ |
| /* Opcode range: 0x3D */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_RTDG( TT_ExecContext exc ) |
| { |
| exc->GS.round_state = TT_Round_To_Double_Grid; |
| exc->func_round = (TT_Round_Func)Round_To_Double_Grid; |
| } |
| |
| |
| /*************************************************************************/ |
| /* RUTG[]: Round Up To Grid */ |
| /* Opcode range: 0x7C */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_RUTG( TT_ExecContext exc ) |
| { |
| exc->GS.round_state = TT_Round_Up_To_Grid; |
| exc->func_round = (TT_Round_Func)Round_Up_To_Grid; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* RDTG[]: Round Down To Grid */ |
| /* Opcode range: 0x7D */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_RDTG( TT_ExecContext exc ) |
| { |
| exc->GS.round_state = TT_Round_Down_To_Grid; |
| exc->func_round = (TT_Round_Func)Round_Down_To_Grid; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ROFF[]: Round OFF */ |
| /* Opcode range: 0x7A */ |
| /* Stack: --> */ |
| /* */ |
| static void |
| Ins_ROFF( TT_ExecContext exc ) |
| { |
| exc->GS.round_state = TT_Round_Off; |
| exc->func_round = (TT_Round_Func)Round_None; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SROUND[]: Super ROUND */ |
| /* Opcode range: 0x76 */ |
| /* Stack: Eint8 --> */ |
| /* */ |
| static void |
| Ins_SROUND( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| SetSuperRound( exc, 0x4000, args[0] ); |
| |
| exc->GS.round_state = TT_Round_Super; |
| exc->func_round = (TT_Round_Func)Round_Super; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* S45ROUND[]: Super ROUND 45 degrees */ |
| /* Opcode range: 0x77 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_S45ROUND( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| SetSuperRound( exc, 0x2D41, args[0] ); |
| |
| exc->GS.round_state = TT_Round_Super_45; |
| exc->func_round = (TT_Round_Func)Round_Super_45; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* GC[a]: Get Coordinate projected onto */ |
| /* Opcode range: 0x46-0x47 */ |
| /* Stack: uint32 --> f26.6 */ |
| /* */ |
| /* XXX: UNDOCUMENTED: Measures from the original glyph must be taken */ |
| /* along the dual projection vector! */ |
| /* */ |
| static void |
| Ins_GC( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong L; |
| FT_F26Dot6 R; |
| |
| |
| L = (FT_ULong)args[0]; |
| |
| if ( BOUNDSL( L, exc->zp2.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| R = 0; |
| } |
| else |
| { |
| if ( exc->opcode & 1 ) |
| R = FAST_DUALPROJ( &exc->zp2.org[L] ); |
| else |
| R = FAST_PROJECT( &exc->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( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_Long K; |
| FT_UShort L; |
| |
| |
| L = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( L, exc->zp2.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| K = FAST_PROJECT( &exc->zp2.cur[L] ); |
| |
| exc->func_move( exc, &exc->zp2, L, SUB_LONG( args[1], K ) ); |
| |
| /* UNDOCUMENTED! The MS rasterizer does that with */ |
| /* twilight points (confirmed by Greg Hitchcock) */ |
| if ( exc->GS.gep2 == 0 ) |
| exc->zp2.org[L] = exc->zp2.cur[L]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MD[a]: Measure Distance */ |
| /* Opcode range: 0x49-0x4A */ |
| /* Stack: uint32 uint32 --> f26.6 */ |
| /* */ |
| /* XXX: UNDOCUMENTED: Measure taken in the original glyph must be along */ |
| /* the dual projection vector. */ |
| /* */ |
| /* XXX: UNDOCUMENTED: Flag attributes are inverted! */ |
| /* 0 => measure distance in original outline */ |
| /* 1 => measure distance in grid-fitted outline */ |
| /* */ |
| /* XXX: UNDOCUMENTED: `zp0 - zp1', and not `zp2 - zp1! */ |
| /* */ |
| static void |
| Ins_MD( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort K, L; |
| FT_F26Dot6 D; |
| |
| |
| K = (FT_UShort)args[1]; |
| L = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( L, exc->zp0.n_points ) || |
| BOUNDS( K, exc->zp1.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| D = 0; |
| } |
| else |
| { |
| if ( exc->opcode & 1 ) |
| D = PROJECT( exc->zp0.cur + L, exc->zp1.cur + K ); |
| else |
| { |
| /* XXX: UNDOCUMENTED: twilight zone special case */ |
| |
| if ( exc->GS.gep0 == 0 || exc->GS.gep1 == 0 ) |
| { |
| FT_Vector* vec1 = exc->zp0.org + L; |
| FT_Vector* vec2 = exc->zp1.org + K; |
| |
| |
| D = DUALPROJ( vec1, vec2 ); |
| } |
| else |
| { |
| FT_Vector* vec1 = exc->zp0.orus + L; |
| FT_Vector* vec2 = exc->zp1.orus + K; |
| |
| |
| if ( exc->metrics.x_scale == exc->metrics.y_scale ) |
| { |
| /* this should be faster */ |
| D = DUALPROJ( vec1, vec2 ); |
| D = FT_MulFix( D, exc->metrics.x_scale ); |
| } |
| else |
| { |
| FT_Vector vec; |
| |
| |
| vec.x = FT_MulFix( vec1->x - vec2->x, exc->metrics.x_scale ); |
| vec.y = FT_MulFix( vec1->y - vec2->y, exc->metrics.y_scale ); |
| |
| D = FAST_DUALPROJ( &vec ); |
| } |
| } |
| } |
| } |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* Disable Type 2 Vacuform Rounds - e.g. Arial Narrow */ |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| FT_ABS( D ) == 64 ) |
| D += 1; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| args[0] = D; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SDPvTL[a]: Set Dual PVector to Line */ |
| /* Opcode range: 0x86-0x87 */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_SDPVTL( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_Long A, B, C; |
| FT_UShort p1, p2; /* was FT_Int in pas type ERROR */ |
| |
| FT_Byte opcode = exc->opcode; |
| |
| |
| p1 = (FT_UShort)args[1]; |
| p2 = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( p2, exc->zp1.n_points ) || |
| BOUNDS( p1, exc->zp2.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| { |
| FT_Vector* v1 = exc->zp1.org + p2; |
| FT_Vector* v2 = exc->zp2.org + p1; |
| |
| |
| A = SUB_LONG( v1->x, v2->x ); |
| B = SUB_LONG( 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; |
| opcode = 0; |
| } |
| } |
| |
| if ( ( opcode & 1 ) != 0 ) |
| { |
| C = B; /* counter clockwise rotation */ |
| B = A; |
| A = NEG_LONG( C ); |
| } |
| |
| Normalize( A, B, &exc->GS.dualVector ); |
| |
| { |
| FT_Vector* v1 = exc->zp1.cur + p2; |
| FT_Vector* v2 = exc->zp2.cur + p1; |
| |
| |
| A = SUB_LONG( v1->x, v2->x ); |
| B = SUB_LONG( v1->y, v2->y ); |
| |
| if ( A == 0 && B == 0 ) |
| { |
| A = 0x4000; |
| opcode = 0; |
| } |
| } |
| |
| if ( ( opcode & 1 ) != 0 ) |
| { |
| C = B; /* counter clockwise rotation */ |
| B = A; |
| A = NEG_LONG( C ); |
| } |
| |
| Normalize( A, B, &exc->GS.projVector ); |
| Compute_Funcs( exc ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SZP0[]: Set Zone Pointer 0 */ |
| /* Opcode range: 0x13 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SZP0( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| switch ( (FT_Int)args[0] ) |
| { |
| case 0: |
| exc->zp0 = exc->twilight; |
| break; |
| |
| case 1: |
| exc->zp0 = exc->pts; |
| break; |
| |
| default: |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| exc->GS.gep0 = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SZP1[]: Set Zone Pointer 1 */ |
| /* Opcode range: 0x14 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SZP1( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| switch ( (FT_Int)args[0] ) |
| { |
| case 0: |
| exc->zp1 = exc->twilight; |
| break; |
| |
| case 1: |
| exc->zp1 = exc->pts; |
| break; |
| |
| default: |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| exc->GS.gep1 = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SZP2[]: Set Zone Pointer 2 */ |
| /* Opcode range: 0x15 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SZP2( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| switch ( (FT_Int)args[0] ) |
| { |
| case 0: |
| exc->zp2 = exc->twilight; |
| break; |
| |
| case 1: |
| exc->zp2 = exc->pts; |
| break; |
| |
| default: |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| exc->GS.gep2 = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SZPS[]: Set Zone PointerS */ |
| /* Opcode range: 0x16 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SZPS( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| switch ( (FT_Int)args[0] ) |
| { |
| case 0: |
| exc->zp0 = exc->twilight; |
| break; |
| |
| case 1: |
| exc->zp0 = exc->pts; |
| break; |
| |
| default: |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| exc->zp1 = exc->zp0; |
| exc->zp2 = exc->zp0; |
| |
| exc->GS.gep0 = (FT_UShort)args[0]; |
| exc->GS.gep1 = (FT_UShort)args[0]; |
| exc->GS.gep2 = (FT_UShort)args[0]; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* INSTCTRL[]: INSTruction ConTRoL */ |
| /* Opcode range: 0x8E */ |
| /* Stack: int32 int32 --> */ |
| /* */ |
| static void |
| Ins_INSTCTRL( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong K, L, Kf; |
| |
| |
| K = (FT_ULong)args[1]; |
| L = (FT_ULong)args[0]; |
| |
| /* selector values cannot be `OR'ed; */ |
| /* they are indices starting with index 1, not flags */ |
| if ( K < 1 || K > 3 ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| /* convert index to flag value */ |
| Kf = 1 << ( K - 1 ); |
| |
| if ( L != 0 ) |
| { |
| /* arguments to selectors look like flag values */ |
| if ( L != Kf ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| } |
| |
| exc->GS.instruct_control &= ~(FT_Byte)Kf; |
| exc->GS.instruct_control |= (FT_Byte)L; |
| |
| if ( K == 3 ) |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* INSTCTRL modifying flag 3 also has an effect */ |
| /* outside of the CVT program */ |
| if ( SUBPIXEL_HINTING_INFINALITY ) |
| exc->ignore_x_mode = FT_BOOL( L == 4 ); |
| #endif |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* Native ClearType fonts sign a waiver that turns off all backward */ |
| /* compatibility hacks and lets them program points to the grid like */ |
| /* it's 1996. They might sign a waiver for just one glyph, though. */ |
| if ( SUBPIXEL_HINTING_MINIMAL ) |
| exc->backward_compatibility = !FT_BOOL( L == 4 ); |
| #endif |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SCANCTRL[]: SCAN ConTRoL */ |
| /* Opcode range: 0x85 */ |
| /* Stack: uint32? --> */ |
| /* */ |
| static void |
| Ins_SCANCTRL( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_Int A; |
| |
| |
| /* Get Threshold */ |
| A = (FT_Int)( args[0] & 0xFF ); |
| |
| if ( A == 0xFF ) |
| { |
| exc->GS.scan_control = TRUE; |
| return; |
| } |
| else if ( A == 0 ) |
| { |
| exc->GS.scan_control = FALSE; |
| return; |
| } |
| |
| if ( ( args[0] & 0x100 ) != 0 && exc->tt_metrics.ppem <= A ) |
| exc->GS.scan_control = TRUE; |
| |
| if ( ( args[0] & 0x200 ) != 0 && exc->tt_metrics.rotated ) |
| exc->GS.scan_control = TRUE; |
| |
| if ( ( args[0] & 0x400 ) != 0 && exc->tt_metrics.stretched ) |
| exc->GS.scan_control = TRUE; |
| |
| if ( ( args[0] & 0x800 ) != 0 && exc->tt_metrics.ppem > A ) |
| exc->GS.scan_control = FALSE; |
| |
| if ( ( args[0] & 0x1000 ) != 0 && exc->tt_metrics.rotated ) |
| exc->GS.scan_control = FALSE; |
| |
| if ( ( args[0] & 0x2000 ) != 0 && exc->tt_metrics.stretched ) |
| exc->GS.scan_control = FALSE; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SCANTYPE[]: SCAN TYPE */ |
| /* Opcode range: 0x8D */ |
| /* Stack: uint16 --> */ |
| /* */ |
| static void |
| Ins_SCANTYPE( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| if ( args[0] >= 0 ) |
| exc->GS.scan_type = (FT_Int)args[0] & 0xFFFF; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MANAGING OUTLINES */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* FLIPPT[]: FLIP PoinT */ |
| /* Opcode range: 0x80 */ |
| /* Stack: uint32... --> */ |
| /* */ |
| static void |
| Ins_FLIPPT( TT_ExecContext exc ) |
| { |
| FT_UShort point; |
| |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* See `ttinterp.h' for details on backward compatibility mode. */ |
| if ( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility && |
| exc->iupx_called && |
| exc->iupy_called ) |
| goto Fail; |
| #endif |
| |
| if ( exc->top < exc->GS.loop ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Too_Few_Arguments ); |
| goto Fail; |
| } |
| |
| while ( exc->GS.loop > 0 ) |
| { |
| exc->args--; |
| |
| point = (FT_UShort)exc->stack[exc->args]; |
| |
| if ( BOUNDS( point, exc->pts.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| { |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| } |
| else |
| exc->pts.tags[point] ^= FT_CURVE_TAG_ON; |
| |
| exc->GS.loop--; |
| } |
| |
| Fail: |
| exc->GS.loop = 1; |
| exc->new_top = exc->args; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* FLIPRGON[]: FLIP RanGe ON */ |
| /* Opcode range: 0x81 */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_FLIPRGON( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort I, K, L; |
| |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* See `ttinterp.h' for details on backward compatibility mode. */ |
| if ( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility && |
| exc->iupx_called && |
| exc->iupy_called ) |
| return; |
| #endif |
| |
| K = (FT_UShort)args[1]; |
| L = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( K, exc->pts.n_points ) || |
| BOUNDS( L, exc->pts.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| for ( I = L; I <= K; I++ ) |
| exc->pts.tags[I] |= FT_CURVE_TAG_ON; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* FLIPRGOFF: FLIP RanGe OFF */ |
| /* Opcode range: 0x82 */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_FLIPRGOFF( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort I, K, L; |
| |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* See `ttinterp.h' for details on backward compatibility mode. */ |
| if ( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility && |
| exc->iupx_called && |
| exc->iupy_called ) |
| return; |
| #endif |
| |
| K = (FT_UShort)args[1]; |
| L = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( K, exc->pts.n_points ) || |
| BOUNDS( L, exc->pts.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| for ( I = L; I <= K; I++ ) |
| exc->pts.tags[I] &= ~FT_CURVE_TAG_ON; |
| } |
| |
| |
| static FT_Bool |
| Compute_Point_Displacement( TT_ExecContext exc, |
| FT_F26Dot6* x, |
| FT_F26Dot6* y, |
| TT_GlyphZone zone, |
| FT_UShort* refp ) |
| { |
| TT_GlyphZoneRec zp; |
| FT_UShort p; |
| FT_F26Dot6 d; |
| |
| |
| if ( exc->opcode & 1 ) |
| { |
| zp = exc->zp0; |
| p = exc->GS.rp1; |
| } |
| else |
| { |
| zp = exc->zp1; |
| p = exc->GS.rp2; |
| } |
| |
| if ( BOUNDS( p, zp.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| *refp = 0; |
| return FAILURE; |
| } |
| |
| *zone = zp; |
| *refp = p; |
| |
| d = PROJECT( zp.cur + p, zp.org + p ); |
| |
| *x = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.x, exc->F_dot_P ); |
| *y = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.y, exc->F_dot_P ); |
| |
| return SUCCESS; |
| } |
| |
| |
| /* See `ttinterp.h' for details on backward compatibility mode. */ |
| static void |
| Move_Zp2_Point( TT_ExecContext exc, |
| FT_UShort point, |
| FT_F26Dot6 dx, |
| FT_F26Dot6 dy, |
| FT_Bool touch ) |
| { |
| if ( exc->GS.freeVector.x != 0 ) |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| if ( !( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility ) ) |
| #endif |
| exc->zp2.cur[point].x = ADD_LONG( exc->zp2.cur[point].x, dx ); |
| |
| if ( touch ) |
| exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X; |
| } |
| |
| if ( exc->GS.freeVector.y != 0 ) |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| if ( !( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility && |
| exc->iupx_called && |
| exc->iupy_called ) ) |
| #endif |
| exc->zp2.cur[point].y = ADD_LONG( exc->zp2.cur[point].y, dy ); |
| |
| if ( touch ) |
| exc->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( TT_ExecContext exc ) |
| { |
| TT_GlyphZoneRec zp; |
| FT_UShort refp; |
| |
| FT_F26Dot6 dx, dy; |
| FT_UShort point; |
| |
| |
| if ( exc->top < exc->GS.loop ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto Fail; |
| } |
| |
| if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) ) |
| return; |
| |
| while ( exc->GS.loop > 0 ) |
| { |
| exc->args--; |
| point = (FT_UShort)exc->stack[exc->args]; |
| |
| if ( BOUNDS( point, exc->zp2.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| { |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| } |
| else |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* doesn't follow Cleartype spec but produces better result */ |
| if ( SUBPIXEL_HINTING_INFINALITY && exc->ignore_x_mode ) |
| Move_Zp2_Point( exc, point, 0, dy, TRUE ); |
| else |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| Move_Zp2_Point( exc, point, dx, dy, TRUE ); |
| |
| exc->GS.loop--; |
| } |
| |
| Fail: |
| exc->GS.loop = 1; |
| exc->new_top = exc->args; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SHC[a]: SHift Contour */ |
| /* Opcode range: 0x34-35 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| /* UNDOCUMENTED: According to Greg Hitchcock, there is one (virtual) */ |
| /* contour in the twilight zone, namely contour number */ |
| /* zero which includes all points of it. */ |
| /* */ |
| static void |
| Ins_SHC( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| TT_GlyphZoneRec zp; |
| FT_UShort refp; |
| FT_F26Dot6 dx, dy; |
| |
| FT_Short contour, bounds; |
| FT_UShort start, limit, i; |
| |
| |
| contour = (FT_Short)args[0]; |
| bounds = ( exc->GS.gep2 == 0 ) ? 1 : exc->zp2.n_contours; |
| |
| if ( BOUNDS( contour, bounds ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) ) |
| return; |
| |
| if ( contour == 0 ) |
| start = 0; |
| else |
| start = (FT_UShort)( exc->zp2.contours[contour - 1] + 1 - |
| exc->zp2.first_point ); |
| |
| /* we use the number of points if in the twilight zone */ |
| if ( exc->GS.gep2 == 0 ) |
| limit = exc->zp2.n_points; |
| else |
| limit = (FT_UShort)( exc->zp2.contours[contour] - |
| exc->zp2.first_point + 1 ); |
| |
| for ( i = start; i < limit; i++ ) |
| { |
| if ( zp.cur != exc->zp2.cur || refp != i ) |
| Move_Zp2_Point( exc, i, dx, dy, TRUE ); |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SHZ[a]: SHift Zone */ |
| /* Opcode range: 0x36-37 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_SHZ( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| TT_GlyphZoneRec zp; |
| FT_UShort refp; |
| FT_F26Dot6 dx, |
| dy; |
| |
| FT_UShort limit, i; |
| |
| |
| if ( BOUNDS( args[0], 2 ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) ) |
| return; |
| |
| /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points. */ |
| /* Twilight zone has no real contours, so use `n_points'. */ |
| /* Normal zone's `n_points' includes phantoms, so must */ |
| /* use end of last contour. */ |
| if ( exc->GS.gep2 == 0 ) |
| limit = (FT_UShort)exc->zp2.n_points; |
| else if ( exc->GS.gep2 == 1 && exc->zp2.n_contours > 0 ) |
| limit = (FT_UShort)( exc->zp2.contours[exc->zp2.n_contours - 1] + 1 ); |
| else |
| limit = 0; |
| |
| /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */ |
| for ( i = 0; i < limit; i++ ) |
| { |
| if ( zp.cur != exc->zp2.cur || refp != i ) |
| Move_Zp2_Point( exc, i, dx, dy, FALSE ); |
| } |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* SHPIX[]: SHift points by a PIXel amount */ |
| /* Opcode range: 0x38 */ |
| /* Stack: f26.6 uint32... --> */ |
| /* */ |
| static void |
| Ins_SHPIX( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_F26Dot6 dx, dy; |
| FT_UShort point; |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| FT_Int B1, B2; |
| #endif |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| FT_Bool in_twilight = FT_BOOL( exc->GS.gep0 == 0 || |
| exc->GS.gep1 == 0 || |
| exc->GS.gep2 == 0 ); |
| #endif |
| |
| |
| |
| if ( exc->top < exc->GS.loop + 1 ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto Fail; |
| } |
| |
| dx = TT_MulFix14( args[0], exc->GS.freeVector.x ); |
| dy = TT_MulFix14( args[0], exc->GS.freeVector.y ); |
| |
| while ( exc->GS.loop > 0 ) |
| { |
| exc->args--; |
| |
| point = (FT_UShort)exc->stack[exc->args]; |
| |
| if ( BOUNDS( point, exc->zp2.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| { |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| } |
| else |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY ) |
| { |
| /* If not using ignore_x_mode rendering, allow ZP2 move. */ |
| /* If inline deltas aren't allowed, skip ZP2 move. */ |
| /* If using ignore_x_mode rendering, allow ZP2 point move if: */ |
| /* - freedom vector is y and sph_compatibility_mode is off */ |
| /* - the glyph is composite and the move is in the Y direction */ |
| /* - the glyph is specifically set to allow SHPIX moves */ |
| /* - the move is on a previously Y-touched point */ |
| |
| if ( exc->ignore_x_mode ) |
| { |
| /* save point for later comparison */ |
| if ( exc->GS.freeVector.y != 0 ) |
| B1 = exc->zp2.cur[point].y; |
| else |
| B1 = exc->zp2.cur[point].x; |
| |
| if ( !exc->face->sph_compatibility_mode && |
| exc->GS.freeVector.y != 0 ) |
| { |
| Move_Zp2_Point( exc, point, dx, dy, TRUE ); |
| |
| /* save new point */ |
| if ( exc->GS.freeVector.y != 0 ) |
| { |
| B2 = exc->zp2.cur[point].y; |
| |
| /* reverse any disallowed moves */ |
| if ( ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES ) && |
| ( B1 & 63 ) != 0 && |
| ( B2 & 63 ) != 0 && |
| B1 != B2 ) |
| Move_Zp2_Point( exc, |
| point, |
| NEG_LONG( dx ), |
| NEG_LONG( dy ), |
| TRUE ); |
| } |
| } |
| else if ( exc->face->sph_compatibility_mode ) |
| { |
| if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) |
| { |
| dx = FT_PIX_ROUND( B1 + dx ) - B1; |
| dy = FT_PIX_ROUND( B1 + dy ) - B1; |
| } |
| |
| /* skip post-iup deltas */ |
| if ( exc->iup_called && |
| ( ( exc->sph_in_func_flags & SPH_FDEF_INLINE_DELTA_1 ) || |
| ( exc->sph_in_func_flags & SPH_FDEF_INLINE_DELTA_2 ) ) ) |
| goto Skip; |
| |
| if ( !( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) && |
| ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || |
| ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y ) || |
| ( exc->sph_tweak_flags & SPH_TWEAK_DO_SHPIX ) ) ) |
| Move_Zp2_Point( exc, point, 0, dy, TRUE ); |
| |
| /* save new point */ |
| if ( exc->GS.freeVector.y != 0 ) |
| { |
| B2 = exc->zp2.cur[point].y; |
| |
| /* reverse any disallowed moves */ |
| if ( ( B1 & 63 ) == 0 && |
| ( B2 & 63 ) != 0 && |
| B1 != B2 ) |
| Move_Zp2_Point( exc, point, 0, NEG_LONG( dy ), TRUE ); |
| } |
| } |
| else if ( exc->sph_in_func_flags & SPH_FDEF_TYPEMAN_DIAGENDCTRL ) |
| Move_Zp2_Point( exc, point, dx, dy, TRUE ); |
| } |
| else |
| Move_Zp2_Point( exc, point, dx, dy, TRUE ); |
| } |
| else |
| #endif |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| if ( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility ) |
| { |
| /* Special case: allow SHPIX to move points in the twilight zone. */ |
| /* Otherwise, treat SHPIX the same as DELTAP. Unbreaks various */ |
| /* fonts such as older versions of Rokkitt and DTL Argo T Light */ |
| /* that would glitch severely after calling ALIGNRP after a */ |
| /* blocked SHPIX. */ |
| if ( in_twilight || |
| ( !( exc->iupx_called && exc->iupy_called ) && |
| ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || |
| ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y ) ) ) ) |
| Move_Zp2_Point( exc, point, 0, dy, TRUE ); |
| } |
| else |
| #endif |
| Move_Zp2_Point( exc, point, dx, dy, TRUE ); |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| Skip: |
| #endif |
| exc->GS.loop--; |
| } |
| |
| Fail: |
| exc->GS.loop = 1; |
| exc->new_top = exc->args; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MSIRP[a]: Move Stack Indirect Relative Position */ |
| /* Opcode range: 0x3A-0x3B */ |
| /* Stack: f26.6 uint32 --> */ |
| /* */ |
| static void |
| Ins_MSIRP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort point = 0; |
| FT_F26Dot6 distance; |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| FT_F26Dot6 control_value_cutin = 0; |
| |
| |
| if ( SUBPIXEL_HINTING_INFINALITY ) |
| { |
| control_value_cutin = exc->GS.control_value_cutin; |
| |
| if ( exc->ignore_x_mode && |
| exc->GS.freeVector.x != 0 && |
| !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) |
| control_value_cutin = 0; |
| } |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| point = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( point, exc->zp1.n_points ) || |
| BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| /* UNDOCUMENTED! The MS rasterizer does that with */ |
| /* twilight points (confirmed by Greg Hitchcock) */ |
| if ( exc->GS.gep1 == 0 ) |
| { |
| exc->zp1.org[point] = exc->zp0.org[exc->GS.rp0]; |
| exc->func_move_orig( exc, &exc->zp1, point, args[1] ); |
| exc->zp1.cur[point] = exc->zp1.org[point]; |
| } |
| |
| distance = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 ); |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* subpixel hinting - make MSIRP respect CVT cut-in; */ |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.freeVector.x != 0 && |
| FT_ABS( SUB_LONG( distance, args[1] ) ) >= control_value_cutin ) |
| distance = args[1]; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| exc->func_move( exc, |
| &exc->zp1, |
| point, |
| SUB_LONG( args[1], distance ) ); |
| |
| exc->GS.rp1 = exc->GS.rp0; |
| exc->GS.rp2 = point; |
| |
| if ( ( exc->opcode & 1 ) != 0 ) |
| exc->GS.rp0 = point; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MDAP[a]: Move Direct Absolute Point */ |
| /* Opcode range: 0x2E-0x2F */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_MDAP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort point; |
| FT_F26Dot6 cur_dist; |
| FT_F26Dot6 distance; |
| |
| |
| point = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( point, exc->zp0.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| if ( ( exc->opcode & 1 ) != 0 ) |
| { |
| cur_dist = FAST_PROJECT( &exc->zp0.cur[point] ); |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.freeVector.x != 0 ) |
| distance = Round_None( |
| exc, |
| cur_dist, |
| exc->tt_metrics.compensations[0] ) - cur_dist; |
| else |
| #endif |
| distance = exc->func_round( |
| exc, |
| cur_dist, |
| exc->tt_metrics.compensations[0] ) - cur_dist; |
| } |
| else |
| distance = 0; |
| |
| exc->func_move( exc, &exc->zp0, point, distance ); |
| |
| exc->GS.rp0 = point; |
| exc->GS.rp1 = point; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MIAP[a]: Move Indirect Absolute Point */ |
| /* Opcode range: 0x3E-0x3F */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_MIAP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong cvtEntry; |
| FT_UShort point; |
| FT_F26Dot6 distance; |
| FT_F26Dot6 org_dist; |
| FT_F26Dot6 control_value_cutin; |
| |
| |
| control_value_cutin = exc->GS.control_value_cutin; |
| cvtEntry = (FT_ULong)args[1]; |
| point = (FT_UShort)args[0]; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.freeVector.x != 0 && |
| exc->GS.freeVector.y == 0 && |
| !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) |
| control_value_cutin = 0; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| if ( BOUNDS( point, exc->zp0.n_points ) || |
| BOUNDSL( cvtEntry, exc->cvtSize ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( 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 = exc->func_read_cvt( exc, cvtEntry ); |
| |
| if ( exc->GS.gep0 == 0 ) /* If in twilight zone */ |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* Only adjust if not in sph_compatibility_mode or ignore_x_mode. */ |
| /* Determined via experimentation and may be incorrect... */ |
| if ( !( SUBPIXEL_HINTING_INFINALITY && |
| ( exc->ignore_x_mode && |
| exc->face->sph_compatibility_mode ) ) ) |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| exc->zp0.org[point].x = TT_MulFix14( distance, |
| exc->GS.freeVector.x ); |
| exc->zp0.org[point].y = TT_MulFix14( distance, |
| exc->GS.freeVector.y ), |
| exc->zp0.cur[point] = exc->zp0.org[point]; |
| } |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| ( exc->sph_tweak_flags & SPH_TWEAK_MIAP_HACK ) && |
| distance > 0 && |
| exc->GS.freeVector.y != 0 ) |
| distance = 0; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| org_dist = FAST_PROJECT( &exc->zp0.cur[point] ); |
| |
| if ( ( exc->opcode & 1 ) != 0 ) /* rounding and control cut-in flag */ |
| { |
| if ( FT_ABS( distance - org_dist ) > control_value_cutin ) |
| distance = org_dist; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.freeVector.x != 0 ) |
| distance = Round_None( exc, |
| distance, |
| exc->tt_metrics.compensations[0] ); |
| else |
| #endif |
| distance = exc->func_round( exc, |
| distance, |
| exc->tt_metrics.compensations[0] ); |
| } |
| |
| exc->func_move( exc, &exc->zp0, point, SUB_LONG( distance, org_dist ) ); |
| |
| Fail: |
| exc->GS.rp0 = point; |
| exc->GS.rp1 = point; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MDRP[abcde]: Move Direct Relative Point */ |
| /* Opcode range: 0xC0-0xDF */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_MDRP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort point = 0; |
| FT_F26Dot6 org_dist, distance, minimum_distance; |
| |
| |
| minimum_distance = exc->GS.minimum_distance; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.freeVector.x != 0 && |
| !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) |
| minimum_distance = 0; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| point = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( point, exc->zp1.n_points ) || |
| BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto Fail; |
| } |
| |
| /* XXX: Is there some undocumented feature while in the */ |
| /* twilight zone? */ |
| |
| /* XXX: UNDOCUMENTED: twilight zone special case */ |
| |
| if ( exc->GS.gep0 == 0 || exc->GS.gep1 == 0 ) |
| { |
| FT_Vector* vec1 = &exc->zp1.org[point]; |
| FT_Vector* vec2 = &exc->zp0.org[exc->GS.rp0]; |
| |
| |
| org_dist = DUALPROJ( vec1, vec2 ); |
| } |
| else |
| { |
| FT_Vector* vec1 = &exc->zp1.orus[point]; |
| FT_Vector* vec2 = &exc->zp0.orus[exc->GS.rp0]; |
| |
| |
| if ( exc->metrics.x_scale == exc->metrics.y_scale ) |
| { |
| /* this should be faster */ |
| org_dist = DUALPROJ( vec1, vec2 ); |
| org_dist = FT_MulFix( org_dist, exc->metrics.x_scale ); |
| } |
| else |
| { |
| FT_Vector vec; |
| |
| |
| vec.x = FT_MulFix( SUB_LONG( vec1->x, vec2->x ), |
| exc->metrics.x_scale ); |
| vec.y = FT_MulFix( SUB_LONG( vec1->y, vec2->y ), |
| exc->metrics.y_scale ); |
| |
| org_dist = FAST_DUALPROJ( &vec ); |
| } |
| } |
| |
| /* single width cut-in test */ |
| |
| /* |org_dist - single_width_value| < single_width_cutin */ |
| if ( org_dist < exc->GS.single_width_value + exc->GS.single_width_cutin || |
| org_dist > exc->GS.single_width_value - exc->GS.single_width_cutin ) |
| { |
| if ( org_dist >= 0 ) |
| org_dist = exc->GS.single_width_value; |
| else |
| org_dist = -exc->GS.single_width_value; |
| } |
| |
| /* round flag */ |
| |
| if ( ( exc->opcode & 4 ) != 0 ) |
| { |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.freeVector.x != 0 ) |
| distance = Round_None( |
| exc, |
| org_dist, |
| exc->tt_metrics.compensations[exc->opcode & 3] ); |
| else |
| #endif |
| distance = exc->func_round( |
| exc, |
| org_dist, |
| exc->tt_metrics.compensations[exc->opcode & 3] ); |
| } |
| else |
| distance = Round_None( |
| exc, |
| org_dist, |
| exc->tt_metrics.compensations[exc->opcode & 3] ); |
| |
| /* minimum distance flag */ |
| |
| if ( ( exc->opcode & 8 ) != 0 ) |
| { |
| if ( org_dist >= 0 ) |
| { |
| if ( distance < minimum_distance ) |
| distance = minimum_distance; |
| } |
| else |
| { |
| if ( distance > NEG_LONG( minimum_distance ) ) |
| distance = NEG_LONG( minimum_distance ); |
| } |
| } |
| |
| /* now move the point */ |
| |
| org_dist = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 ); |
| |
| exc->func_move( exc, &exc->zp1, point, SUB_LONG( distance, org_dist ) ); |
| |
| Fail: |
| exc->GS.rp1 = exc->GS.rp0; |
| exc->GS.rp2 = point; |
| |
| if ( ( exc->opcode & 16 ) != 0 ) |
| exc->GS.rp0 = point; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MIRP[abcde]: Move Indirect Relative Point */ |
| /* Opcode range: 0xE0-0xFF */ |
| /* Stack: int32? uint32 --> */ |
| /* */ |
| static void |
| Ins_MIRP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort point; |
| FT_ULong cvtEntry; |
| |
| FT_F26Dot6 cvt_dist, |
| distance, |
| cur_dist, |
| org_dist, |
| control_value_cutin, |
| minimum_distance; |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| FT_Int B1 = 0; /* pacify compiler */ |
| FT_Int B2 = 0; |
| FT_Bool reverse_move = FALSE; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| |
| minimum_distance = exc->GS.minimum_distance; |
| control_value_cutin = exc->GS.control_value_cutin; |
| point = (FT_UShort)args[0]; |
| cvtEntry = (FT_ULong)( args[1] + 1 ); |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.freeVector.x != 0 && |
| !( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) |
| control_value_cutin = minimum_distance = 0; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */ |
| |
| if ( BOUNDS( point, exc->zp1.n_points ) || |
| BOUNDSL( cvtEntry, exc->cvtSize + 1 ) || |
| BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto Fail; |
| } |
| |
| if ( !cvtEntry ) |
| cvt_dist = 0; |
| else |
| cvt_dist = exc->func_read_cvt( exc, cvtEntry - 1 ); |
| |
| /* single width test */ |
| |
| if ( FT_ABS( cvt_dist - exc->GS.single_width_value ) < |
| exc->GS.single_width_cutin ) |
| { |
| if ( cvt_dist >= 0 ) |
| cvt_dist = exc->GS.single_width_value; |
| else |
| cvt_dist = -exc->GS.single_width_value; |
| } |
| |
| /* UNDOCUMENTED! The MS rasterizer does that with */ |
| /* twilight points (confirmed by Greg Hitchcock) */ |
| if ( exc->GS.gep1 == 0 ) |
| { |
| exc->zp1.org[point].x = exc->zp0.org[exc->GS.rp0].x + |
| TT_MulFix14( cvt_dist, |
| exc->GS.freeVector.x ); |
| exc->zp1.org[point].y = exc->zp0.org[exc->GS.rp0].y + |
| TT_MulFix14( cvt_dist, |
| exc->GS.freeVector.y ); |
| exc->zp1.cur[point] = exc->zp1.org[point]; |
| } |
| |
| org_dist = DUALPROJ( &exc->zp1.org[point], &exc->zp0.org[exc->GS.rp0] ); |
| cur_dist = PROJECT ( &exc->zp1.cur[point], &exc->zp0.cur[exc->GS.rp0] ); |
| |
| /* auto-flip test */ |
| |
| if ( exc->GS.auto_flip ) |
| { |
| if ( ( org_dist ^ cvt_dist ) < 0 ) |
| cvt_dist = -cvt_dist; |
| } |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.freeVector.y != 0 && |
| ( exc->sph_tweak_flags & SPH_TWEAK_TIMES_NEW_ROMAN_HACK ) ) |
| { |
| if ( cur_dist < -64 ) |
| cvt_dist -= 16; |
| else if ( cur_dist > 64 && cur_dist < 84 ) |
| cvt_dist += 32; |
| } |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| /* control value cut-in and round */ |
| |
| if ( ( exc->opcode & 4 ) != 0 ) |
| { |
| /* XXX: UNDOCUMENTED! Only perform cut-in test when both points */ |
| /* refer to the same zone. */ |
| |
| if ( exc->GS.gep0 == exc->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 ) > control_value_cutin ) |
| cvt_dist = org_dist; |
| } |
| |
| distance = exc->func_round( |
| exc, |
| cvt_dist, |
| exc->tt_metrics.compensations[exc->opcode & 3] ); |
| } |
| else |
| { |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /* do cvt cut-in always in MIRP for sph */ |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->GS.gep0 == exc->GS.gep1 ) |
| { |
| if ( FT_ABS( cvt_dist - org_dist ) > control_value_cutin ) |
| cvt_dist = org_dist; |
| } |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| distance = Round_None( |
| exc, |
| cvt_dist, |
| exc->tt_metrics.compensations[exc->opcode & 3] ); |
| } |
| |
| /* minimum distance test */ |
| |
| if ( ( exc->opcode & 8 ) != 0 ) |
| { |
| if ( org_dist >= 0 ) |
| { |
| if ( distance < minimum_distance ) |
| distance = minimum_distance; |
| } |
| else |
| { |
| if ( distance > NEG_LONG( minimum_distance ) ) |
| distance = NEG_LONG( minimum_distance ); |
| } |
| } |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY ) |
| { |
| B1 = exc->zp1.cur[point].y; |
| |
| /* Round moves if necessary */ |
| if ( exc->ignore_x_mode && |
| exc->GS.freeVector.y != 0 && |
| ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) ) |
| distance = FT_PIX_ROUND( B1 + distance - cur_dist ) - B1 + cur_dist; |
| |
| if ( exc->ignore_x_mode && |
| exc->GS.freeVector.y != 0 && |
| ( exc->opcode & 16 ) == 0 && |
| ( exc->opcode & 8 ) == 0 && |
| ( exc->sph_tweak_flags & SPH_TWEAK_COURIER_NEW_2_HACK ) ) |
| distance += 64; |
| } |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| exc->func_move( exc, |
| &exc->zp1, |
| point, |
| SUB_LONG( distance, cur_dist ) ); |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY ) |
| { |
| B2 = exc->zp1.cur[point].y; |
| |
| /* Reverse move if necessary */ |
| if ( exc->ignore_x_mode ) |
| { |
| if ( exc->face->sph_compatibility_mode && |
| exc->GS.freeVector.y != 0 && |
| ( B1 & 63 ) == 0 && |
| ( B2 & 63 ) != 0 ) |
| reverse_move = TRUE; |
| |
| if ( ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES ) && |
| exc->GS.freeVector.y != 0 && |
| ( B2 & 63 ) != 0 && |
| ( B1 & 63 ) != 0 ) |
| reverse_move = TRUE; |
| } |
| |
| if ( reverse_move ) |
| exc->func_move( exc, |
| &exc->zp1, |
| point, |
| SUB_LONG( cur_dist, distance ) ); |
| } |
| |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| Fail: |
| exc->GS.rp1 = exc->GS.rp0; |
| |
| if ( ( exc->opcode & 16 ) != 0 ) |
| exc->GS.rp0 = point; |
| |
| exc->GS.rp2 = point; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ALIGNRP[]: ALIGN Relative Point */ |
| /* Opcode range: 0x3C */ |
| /* Stack: uint32 uint32... --> */ |
| /* */ |
| static void |
| Ins_ALIGNRP( TT_ExecContext exc ) |
| { |
| FT_UShort point; |
| FT_F26Dot6 distance; |
| |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->iup_called && |
| ( exc->sph_tweak_flags & SPH_TWEAK_NO_ALIGNRP_AFTER_IUP ) ) |
| { |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto Fail; |
| } |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| if ( exc->top < exc->GS.loop || |
| BOUNDS( exc->GS.rp0, exc->zp0.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto Fail; |
| } |
| |
| while ( exc->GS.loop > 0 ) |
| { |
| exc->args--; |
| |
| point = (FT_UShort)exc->stack[exc->args]; |
| |
| if ( BOUNDS( point, exc->zp1.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| { |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| } |
| else |
| { |
| distance = PROJECT( exc->zp1.cur + point, |
| exc->zp0.cur + exc->GS.rp0 ); |
| |
| exc->func_move( exc, &exc->zp1, point, NEG_LONG( distance ) ); |
| } |
| |
| exc->GS.loop--; |
| } |
| |
| Fail: |
| exc->GS.loop = 1; |
| exc->new_top = exc->args; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ISECT[]: moves point to InterSECTion */ |
| /* Opcode range: 0x0F */ |
| /* Stack: 5 * uint32 --> */ |
| /* */ |
| static void |
| Ins_ISECT( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort point, |
| a0, a1, |
| b0, b1; |
| |
| FT_F26Dot6 discriminant, dotproduct; |
| |
| 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, exc->zp0.n_points ) || |
| BOUNDS( b1, exc->zp0.n_points ) || |
| BOUNDS( a0, exc->zp1.n_points ) || |
| BOUNDS( a1, exc->zp1.n_points ) || |
| BOUNDS( point, exc->zp2.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| /* Cramer's rule */ |
| |
| dbx = SUB_LONG( exc->zp0.cur[b1].x, exc->zp0.cur[b0].x ); |
| dby = SUB_LONG( exc->zp0.cur[b1].y, exc->zp0.cur[b0].y ); |
| |
| dax = SUB_LONG( exc->zp1.cur[a1].x, exc->zp1.cur[a0].x ); |
| day = SUB_LONG( exc->zp1.cur[a1].y, exc->zp1.cur[a0].y ); |
| |
| dx = SUB_LONG( exc->zp0.cur[b0].x, exc->zp1.cur[a0].x ); |
| dy = SUB_LONG( exc->zp0.cur[b0].y, exc->zp1.cur[a0].y ); |
| |
| discriminant = ADD_LONG( FT_MulDiv( dax, NEG_LONG( dby ), 0x40 ), |
| FT_MulDiv( day, dbx, 0x40 ) ); |
| dotproduct = ADD_LONG( FT_MulDiv( dax, dbx, 0x40 ), |
| FT_MulDiv( day, dby, 0x40 ) ); |
| |
| /* The discriminant above is actually a cross product of vectors */ |
| /* da and db. Together with the dot product, they can be used as */ |
| /* surrogates for sine and cosine of the angle between the vectors. */ |
| /* Indeed, */ |
| /* dotproduct = |da||db|cos(angle) */ |
| /* discriminant = |da||db|sin(angle) . */ |
| /* We use these equations to reject grazing intersections by */ |
| /* thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. */ |
| if ( MUL_LONG( 19, FT_ABS( discriminant ) ) > FT_ABS( dotproduct ) ) |
| { |
| val = ADD_LONG( FT_MulDiv( dx, NEG_LONG( dby ), 0x40 ), |
| FT_MulDiv( dy, dbx, 0x40 ) ); |
| |
| R.x = FT_MulDiv( val, dax, discriminant ); |
| R.y = FT_MulDiv( val, day, discriminant ); |
| |
| /* XXX: Block in backward_compatibility and/or post-IUP? */ |
| exc->zp2.cur[point].x = ADD_LONG( exc->zp1.cur[a0].x, R.x ); |
| exc->zp2.cur[point].y = ADD_LONG( exc->zp1.cur[a0].y, R.y ); |
| } |
| else |
| { |
| /* else, take the middle of the middles of A and B */ |
| |
| /* XXX: Block in backward_compatibility and/or post-IUP? */ |
| exc->zp2.cur[point].x = |
| ADD_LONG( ADD_LONG( exc->zp1.cur[a0].x, exc->zp1.cur[a1].x ), |
| ADD_LONG( exc->zp0.cur[b0].x, exc->zp0.cur[b1].x ) ) / 4; |
| exc->zp2.cur[point].y = |
| ADD_LONG( ADD_LONG( exc->zp1.cur[a0].y, exc->zp1.cur[a1].y ), |
| ADD_LONG( exc->zp0.cur[b0].y, exc->zp0.cur[b1].y ) ) / 4; |
| } |
| |
| exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_BOTH; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* ALIGNPTS[]: ALIGN PoinTS */ |
| /* Opcode range: 0x27 */ |
| /* Stack: uint32 uint32 --> */ |
| /* */ |
| static void |
| Ins_ALIGNPTS( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort p1, p2; |
| FT_F26Dot6 distance; |
| |
| |
| p1 = (FT_UShort)args[0]; |
| p2 = (FT_UShort)args[1]; |
| |
| if ( BOUNDS( p1, exc->zp1.n_points ) || |
| BOUNDS( p2, exc->zp0.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| distance = PROJECT( exc->zp0.cur + p2, exc->zp1.cur + p1 ) / 2; |
| |
| exc->func_move( exc, &exc->zp1, p1, distance ); |
| exc->func_move( exc, &exc->zp0, p2, NEG_LONG( distance ) ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* IP[]: Interpolate Point */ |
| /* Opcode range: 0x39 */ |
| /* Stack: uint32... --> */ |
| /* */ |
| |
| /* SOMETIMES, DUMBER CODE IS BETTER CODE */ |
| |
| static void |
| Ins_IP( TT_ExecContext exc ) |
| { |
| FT_F26Dot6 old_range, cur_range; |
| FT_Vector* orus_base; |
| FT_Vector* cur_base; |
| FT_Int twilight; |
| |
| |
| if ( exc->top < exc->GS.loop ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto Fail; |
| } |
| |
| /* |
| * We need to deal in a special way with the twilight zone. |
| * Otherwise, by definition, the value of exc->twilight.orus[n] is (0,0), |
| * for every n. |
| */ |
| twilight = ( exc->GS.gep0 == 0 || |
| exc->GS.gep1 == 0 || |
| exc->GS.gep2 == 0 ); |
| |
| if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto Fail; |
| } |
| |
| if ( twilight ) |
| orus_base = &exc->zp0.org[exc->GS.rp1]; |
| else |
| orus_base = &exc->zp0.orus[exc->GS.rp1]; |
| |
| cur_base = &exc->zp0.cur[exc->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( exc->GS.rp1, exc->zp0.n_points ) || |
| BOUNDS( exc->GS.rp2, exc->zp1.n_points ) ) |
| { |
| old_range = 0; |
| cur_range = 0; |
| } |
| else |
| { |
| if ( twilight ) |
| old_range = DUALPROJ( &exc->zp1.org[exc->GS.rp2], orus_base ); |
| else if ( exc->metrics.x_scale == exc->metrics.y_scale ) |
| old_range = DUALPROJ( &exc->zp1.orus[exc->GS.rp2], orus_base ); |
| else |
| { |
| FT_Vector vec; |
| |
| |
| vec.x = FT_MulFix( SUB_LONG( exc->zp1.orus[exc->GS.rp2].x, |
| orus_base->x ), |
| exc->metrics.x_scale ); |
| vec.y = FT_MulFix( SUB_LONG( exc->zp1.orus[exc->GS.rp2].y, |
| orus_base->y ), |
| exc->metrics.y_scale ); |
| |
| old_range = FAST_DUALPROJ( &vec ); |
| } |
| |
| cur_range = PROJECT( &exc->zp1.cur[exc->GS.rp2], cur_base ); |
| } |
| |
| for ( ; exc->GS.loop > 0; exc->GS.loop-- ) |
| { |
| FT_UInt point = (FT_UInt)exc->stack[--exc->args]; |
| FT_F26Dot6 org_dist, cur_dist, new_dist; |
| |
| |
| /* check point bounds */ |
| if ( BOUNDS( point, exc->zp2.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| { |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| continue; |
| } |
| |
| if ( twilight ) |
| org_dist = DUALPROJ( &exc->zp2.org[point], orus_base ); |
| else if ( exc->metrics.x_scale == exc->metrics.y_scale ) |
| org_dist = DUALPROJ( &exc->zp2.orus[point], orus_base ); |
| else |
| { |
| FT_Vector vec; |
| |
| |
| vec.x = FT_MulFix( SUB_LONG( exc->zp2.orus[point].x, |
| orus_base->x ), |
| exc->metrics.x_scale ); |
| vec.y = FT_MulFix( SUB_LONG( exc->zp2.orus[point].y, |
| orus_base->y ), |
| exc->metrics.y_scale ); |
| |
| org_dist = FAST_DUALPROJ( &vec ); |
| } |
| |
| cur_dist = PROJECT( &exc->zp2.cur[point], cur_base ); |
| |
| if ( org_dist ) |
| { |
| if ( old_range ) |
| new_dist = FT_MulDiv( org_dist, cur_range, old_range ); |
| else |
| { |
| /* This is the same as what MS does for the invalid case: */ |
| /* */ |
| /* delta = (Original_Pt - Original_RP1) - */ |
| /* (Current_Pt - Current_RP1) ; */ |
| /* */ |
| /* In FreeType speak: */ |
| /* */ |
| /* delta = org_dist - cur_dist . */ |
| /* */ |
| /* We move `point' by `new_dist - cur_dist' after leaving */ |
| /* this block, thus we have */ |
| /* */ |
| /* new_dist - cur_dist = delta , */ |
| /* new_dist - cur_dist = org_dist - cur_dist , */ |
| /* new_dist = org_dist . */ |
| |
| new_dist = org_dist; |
| } |
| } |
| else |
| new_dist = 0; |
| |
| exc->func_move( exc, |
| &exc->zp2, |
| (FT_UShort)point, |
| SUB_LONG( new_dist, cur_dist ) ); |
| } |
| |
| Fail: |
| exc->GS.loop = 1; |
| exc->new_top = exc->args; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* UTP[a]: UnTouch Point */ |
| /* Opcode range: 0x29 */ |
| /* Stack: uint32 --> */ |
| /* */ |
| static void |
| Ins_UTP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UShort point; |
| FT_Byte mask; |
| |
| |
| point = (FT_UShort)args[0]; |
| |
| if ( BOUNDS( point, exc->zp0.n_points ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| |
| mask = 0xFF; |
| |
| if ( exc->GS.freeVector.x != 0 ) |
| mask &= ~FT_CURVE_TAG_TOUCH_X; |
| |
| if ( exc->GS.freeVector.y != 0 ) |
| mask &= ~FT_CURVE_TAG_TOUCH_Y; |
| |
| exc->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 = SUB_LONG( worker->curs[p].x, worker->orgs[p].x ); |
| if ( dx != 0 ) |
| { |
| for ( i = p1; i < p; i++ ) |
| worker->curs[i].x = ADD_LONG( worker->curs[i].x, dx ); |
| |
| for ( i = p + 1; i <= p2; i++ ) |
| worker->curs[i].x = ADD_LONG( 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, cur1, cur2, 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; |
| cur1 = worker->curs[ref1].x; |
| cur2 = worker->curs[ref2].x; |
| delta1 = SUB_LONG( cur1, org1 ); |
| delta2 = SUB_LONG( cur2, org2 ); |
| |
| if ( cur1 == cur2 || orus1 == orus2 ) |
| { |
| |
| /* trivial snap or shift of untouched points */ |
| for ( i = p1; i <= p2; i++ ) |
| { |
| FT_F26Dot6 x = worker->orgs[i].x; |
| |
| |
| if ( x <= org1 ) |
| x = ADD_LONG( x, delta1 ); |
| |
| else if ( x >= org2 ) |
| x = ADD_LONG( x, delta2 ); |
| |
| else |
| x = cur1; |
| |
| 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 = ADD_LONG( x, delta1 ); |
| |
| else if ( x >= org2 ) |
| x = ADD_LONG( x, delta2 ); |
| |
| else |
| { |
| if ( !scale_valid ) |
| { |
| scale_valid = 1; |
| scale = FT_DivFix( SUB_LONG( cur2, cur1 ), |
| SUB_LONG( orus2, orus1 ) ); |
| } |
| |
| x = ADD_LONG( cur1, |
| FT_MulFix( SUB_LONG( 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( TT_ExecContext exc ) |
| { |
| 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 */ |
| |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* See `ttinterp.h' for details on backward compatibility mode. */ |
| /* Allow IUP until it has been called on both axes. Immediately */ |
| /* return on subsequent ones. */ |
| if ( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility ) |
| { |
| if ( exc->iupx_called && exc->iupy_called ) |
| return; |
| |
| if ( exc->opcode & 1 ) |
| exc->iupx_called = TRUE; |
| else |
| exc->iupy_called = TRUE; |
| } |
| #endif |
| |
| /* ignore empty outlines */ |
| if ( exc->pts.n_contours == 0 ) |
| return; |
| |
| if ( exc->opcode & 1 ) |
| { |
| mask = FT_CURVE_TAG_TOUCH_X; |
| V.orgs = exc->pts.org; |
| V.curs = exc->pts.cur; |
| V.orus = exc->pts.orus; |
| } |
| else |
| { |
| mask = FT_CURVE_TAG_TOUCH_Y; |
| V.orgs = (FT_Vector*)( (FT_Pos*)exc->pts.org + 1 ); |
| V.curs = (FT_Vector*)( (FT_Pos*)exc->pts.cur + 1 ); |
| V.orus = (FT_Vector*)( (FT_Pos*)exc->pts.orus + 1 ); |
| } |
| V.max_points = exc->pts.n_points; |
| |
| contour = 0; |
| point = 0; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode ) |
| { |
| exc->iup_called = TRUE; |
| if ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_IUP ) |
| return; |
| } |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| do |
| { |
| end_point = exc->pts.contours[contour] - exc->pts.first_point; |
| first_point = point; |
| |
| if ( BOUNDS( end_point, exc->pts.n_points ) ) |
| end_point = exc->pts.n_points - 1; |
| |
| while ( point <= end_point && ( exc->pts.tags[point] & mask ) == 0 ) |
| point++; |
| |
| if ( point <= end_point ) |
| { |
| first_touched = point; |
| cur_touched = point; |
| |
| point++; |
| |
| while ( point <= end_point ) |
| { |
| if ( ( exc->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 < exc->pts.n_contours ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* DELTAPn[]: DELTA exceptions P1, P2, P3 */ |
| /* Opcode range: 0x5D,0x71,0x72 */ |
| /* Stack: uint32 (2 * uint32)... --> */ |
| /* */ |
| static void |
| Ins_DELTAP( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong nump, k; |
| FT_UShort A; |
| FT_ULong C, P; |
| FT_Long B; |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| FT_UShort B1, B2; |
| |
| |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->ignore_x_mode && |
| exc->iup_called && |
| ( exc->sph_tweak_flags & SPH_TWEAK_NO_DELTAP_AFTER_IUP ) ) |
| goto Fail; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| P = (FT_ULong)exc->func_cur_ppem( exc ); |
| 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 ( exc->args < 2 ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Too_Few_Arguments ); |
| exc->args = 0; |
| goto Fail; |
| } |
| |
| exc->args -= 2; |
| |
| A = (FT_UShort)exc->stack[exc->args + 1]; |
| B = exc->stack[exc->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, exc->zp0.n_points ) ) |
| { |
| C = ( (FT_ULong)B & 0xF0 ) >> 4; |
| |
| switch ( exc->opcode ) |
| { |
| case 0x5D: |
| break; |
| |
| case 0x71: |
| C += 16; |
| break; |
| |
| case 0x72: |
| C += 32; |
| break; |
| } |
| |
| C += exc->GS.delta_base; |
| |
| if ( P == C ) |
| { |
| B = ( (FT_ULong)B & 0xF ) - 8; |
| if ( B >= 0 ) |
| B++; |
| B *= 1L << ( 6 - exc->GS.delta_shift ); |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| |
| if ( SUBPIXEL_HINTING_INFINALITY ) |
| { |
| /* |
| * Allow delta move if |
| * |
| * - not using ignore_x_mode rendering, |
| * - glyph is specifically set to allow it, or |
| * - glyph is composite and freedom vector is not in subpixel |
| * direction. |
| */ |
| if ( !exc->ignore_x_mode || |
| ( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_DO_DELTAP ) || |
| ( exc->is_composite && exc->GS.freeVector.y != 0 ) ) |
| exc->func_move( exc, &exc->zp0, A, B ); |
| |
| /* Otherwise, apply subpixel hinting and compatibility mode */ |
| /* rules, always skipping deltas in subpixel direction. */ |
| else if ( exc->ignore_x_mode && exc->GS.freeVector.y != 0 ) |
| { |
| /* save the y value of the point now; compare after move */ |
| B1 = (FT_UShort)exc->zp0.cur[A].y; |
| |
| /* Standard subpixel hinting: Allow y move for y-touched */ |
| /* points. This messes up DejaVu ... */ |
| if ( !exc->face->sph_compatibility_mode && |
| ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) |
| exc->func_move( exc, &exc->zp0, A, B ); |
| |
| /* compatibility mode */ |
| else if ( exc->face->sph_compatibility_mode && |
| !( exc->sph_tweak_flags & SPH_TWEAK_ALWAYS_SKIP_DELTAP ) ) |
| { |
| if ( exc->sph_tweak_flags & SPH_TWEAK_ROUND_NONPIXEL_Y_MOVES ) |
| B = FT_PIX_ROUND( B1 + B ) - B1; |
| |
| /* Allow delta move if using sph_compatibility_mode, */ |
| /* IUP has not been called, and point is touched on Y. */ |
| if ( !exc->iup_called && |
| ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) |
| exc->func_move( exc, &exc->zp0, A, B ); |
| } |
| |
| B2 = (FT_UShort)exc->zp0.cur[A].y; |
| |
| /* Reverse this move if it results in a disallowed move */ |
| if ( exc->GS.freeVector.y != 0 && |
| ( ( exc->face->sph_compatibility_mode && |
| ( B1 & 63 ) == 0 && |
| ( B2 & 63 ) != 0 ) || |
| ( ( exc->sph_tweak_flags & |
| SPH_TWEAK_SKIP_NONPIXEL_Y_MOVES_DELTAP ) && |
| ( B1 & 63 ) != 0 && |
| ( B2 & 63 ) != 0 ) ) ) |
| exc->func_move( exc, &exc->zp0, A, NEG_LONG( B ) ); |
| } |
| } |
| else |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| { |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* See `ttinterp.h' for details on backward compatibility */ |
| /* mode. */ |
| if ( SUBPIXEL_HINTING_MINIMAL && |
| exc->backward_compatibility ) |
| { |
| if ( !( exc->iupx_called && exc->iupy_called ) && |
| ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) || |
| ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y ) ) ) |
| exc->func_move( exc, &exc->zp0, A, B ); |
| } |
| else |
| #endif |
| exc->func_move( exc, &exc->zp0, A, B ); |
| } |
| } |
| } |
| else |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Invalid_Reference ); |
| } |
| |
| Fail: |
| exc->new_top = exc->args; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* DELTACn[]: DELTA exceptions C1, C2, C3 */ |
| /* Opcode range: 0x73,0x74,0x75 */ |
| /* Stack: uint32 (2 * uint32)... --> */ |
| /* */ |
| static void |
| Ins_DELTAC( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_ULong nump, k; |
| FT_ULong A, C, P; |
| FT_Long B; |
| |
| |
| P = (FT_ULong)exc->func_cur_ppem( exc ); |
| nump = (FT_ULong)args[0]; |
| |
| for ( k = 1; k <= nump; k++ ) |
| { |
| if ( exc->args < 2 ) |
| { |
| if ( exc->pedantic_hinting ) |
| exc->error = FT_THROW( Too_Few_Arguments ); |
| exc->args = 0; |
| goto Fail; |
| } |
| |
| exc->args -= 2; |
| |
| A = (FT_ULong)exc->stack[exc->args + 1]; |
| B = exc->stack[exc->args]; |
| |
| if ( BOUNDSL( A, exc->cvtSize ) ) |
| { |
| if ( exc->pedantic_hinting ) |
| { |
| exc->error = FT_THROW( Invalid_Reference ); |
| return; |
| } |
| } |
| else |
| { |
| C = ( (FT_ULong)B & 0xF0 ) >> 4; |
| |
| switch ( exc->opcode ) |
| { |
| case 0x73: |
| break; |
| |
| case 0x74: |
| C += 16; |
| break; |
| |
| case 0x75: |
| C += 32; |
| break; |
| } |
| |
| C += exc->GS.delta_base; |
| |
| if ( P == C ) |
| { |
| B = ( (FT_ULong)B & 0xF ) - 8; |
| if ( B >= 0 ) |
| B++; |
| B *= 1L << ( 6 - exc->GS.delta_shift ); |
| |
| exc->func_move_cvt( exc, A, B ); |
| } |
| } |
| } |
| |
| Fail: |
| exc->new_top = exc->args; |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* MISC. INSTRUCTIONS */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* GETINFO[]: GET INFOrmation */ |
| /* Opcode range: 0x88 */ |
| /* Stack: uint32 --> uint32 */ |
| /* */ |
| /* XXX: UNDOCUMENTED: Selector bits higher than 9 are currently (May */ |
| /* 2015) not documented in the OpenType specification. */ |
| /* */ |
| /* Selector bit 11 is incorrectly described as bit 8, while the */ |
| /* real meaning of bit 8 (vertical LCD subpixels) stays */ |
| /* undocumented. The same mistake can be found in Greg Hitchcock's */ |
| /* whitepaper. */ |
| /* */ |
| static void |
| Ins_GETINFO( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_Long K; |
| TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( exc->face ); |
| |
| |
| K = 0; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| /********************************/ |
| /* RASTERIZER VERSION */ |
| /* Selector Bit: 0 */ |
| /* Return Bit(s): 0-7 */ |
| /* */ |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| ( args[0] & 1 ) != 0 && |
| exc->subpixel_hinting ) |
| { |
| if ( exc->ignore_x_mode ) |
| { |
| /* if in ClearType backward compatibility mode, */ |
| /* we sometimes change the TrueType version dynamically */ |
| K = exc->rasterizer_version; |
| FT_TRACE6(( "Setting rasterizer version %d\n", |
| exc->rasterizer_version )); |
| } |
| else |
| K = TT_INTERPRETER_VERSION_38; |
| } |
| else |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| if ( ( args[0] & 1 ) != 0 ) |
| K = driver->interpreter_version; |
| |
| /********************************/ |
| /* GLYPH ROTATED */ |
| /* Selector Bit: 1 */ |
| /* Return Bit(s): 8 */ |
| /* */ |
| if ( ( args[0] & 2 ) != 0 && exc->tt_metrics.rotated ) |
| K |= 1 << 8; |
| |
| /********************************/ |
| /* GLYPH STRETCHED */ |
| /* Selector Bit: 2 */ |
| /* Return Bit(s): 9 */ |
| /* */ |
| if ( ( args[0] & 4 ) != 0 && exc->tt_metrics.stretched ) |
| K |= 1 << 9; |
| |
| #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
| /********************************/ |
| /* VARIATION GLYPH */ |
| /* Selector Bit: 3 */ |
| /* Return Bit(s): 10 */ |
| /* */ |
| /* XXX: UNDOCUMENTED! */ |
| if ( (args[0] & 8 ) != 0 && exc->face->blend ) |
| K |= 1 << 10; |
| #endif |
| |
| /********************************/ |
| /* BI-LEVEL HINTING AND */ |
| /* GRAYSCALE RENDERING */ |
| /* Selector Bit: 5 */ |
| /* Return Bit(s): 12 */ |
| /* */ |
| if ( ( args[0] & 32 ) != 0 && exc->grayscale ) |
| K |= 1 << 12; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* Toggle the following flags only outside of monochrome mode. */ |
| /* Otherwise, instructions may behave weirdly and rendering results */ |
| /* may differ between v35 and v40 mode, e.g., in `Times New Roman */ |
| /* Bold Italic'. */ |
| if ( SUBPIXEL_HINTING_MINIMAL && exc->subpixel_hinting_lean ) |
| { |
| /********************************/ |
| /* HINTING FOR SUBPIXEL */ |
| /* Selector Bit: 6 */ |
| /* Return Bit(s): 13 */ |
| /* */ |
| /* v40 does subpixel hinting by default. */ |
| if ( ( args[0] & 64 ) != 0 ) |
| K |= 1 << 13; |
| |
| /********************************/ |
| /* VERTICAL LCD SUBPIXELS? */ |
| /* Selector Bit: 8 */ |
| /* Return Bit(s): 15 */ |
| /* */ |
| if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd_lean ) |
| K |= 1 << 15; |
| |
| /********************************/ |
| /* SUBPIXEL POSITIONED? */ |
| /* Selector Bit: 10 */ |
| /* Return Bit(s): 17 */ |
| /* */ |
| /* XXX: FreeType supports it, dependent on what client does? */ |
| if ( ( args[0] & 1024 ) != 0 ) |
| K |= 1 << 17; |
| |
| /********************************/ |
| /* SYMMETRICAL SMOOTHING */ |
| /* Selector Bit: 11 */ |
| /* Return Bit(s): 18 */ |
| /* */ |
| /* The only smoothing method FreeType supports unless someone sets */ |
| /* FT_LOAD_TARGET_MONO. */ |
| if ( ( args[0] & 2048 ) != 0 && exc->subpixel_hinting_lean ) |
| K |= 1 << 18; |
| |
| /********************************/ |
| /* CLEARTYPE HINTING AND */ |
| /* GRAYSCALE RENDERING */ |
| /* Selector Bit: 12 */ |
| /* Return Bit(s): 19 */ |
| /* */ |
| /* Grayscale rendering is what FreeType does anyway unless someone */ |
| /* sets FT_LOAD_TARGET_MONO or FT_LOAD_TARGET_LCD(_V) */ |
| if ( ( args[0] & 4096 ) != 0 && exc->grayscale_cleartype ) |
| K |= 1 << 19; |
| } |
| #endif |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| |
| if ( SUBPIXEL_HINTING_INFINALITY && |
| exc->rasterizer_version >= TT_INTERPRETER_VERSION_35 ) |
| { |
| |
| if ( exc->rasterizer_version >= 37 ) |
| { |
| /********************************/ |
| /* HINTING FOR SUBPIXEL */ |
| /* Selector Bit: 6 */ |
| /* Return Bit(s): 13 */ |
| /* */ |
| if ( ( args[0] & 64 ) != 0 && exc->subpixel_hinting ) |
| K |= 1 << 13; |
| |
| /********************************/ |
| /* COMPATIBLE WIDTHS ENABLED */ |
| /* Selector Bit: 7 */ |
| /* Return Bit(s): 14 */ |
| /* */ |
| /* Functionality still needs to be added */ |
| if ( ( args[0] & 128 ) != 0 && exc->compatible_widths ) |
| K |= 1 << 14; |
| |
| /********************************/ |
| /* VERTICAL LCD SUBPIXELS? */ |
| /* Selector Bit: 8 */ |
| /* Return Bit(s): 15 */ |
| /* */ |
| /* Functionality still needs to be added */ |
| if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd ) |
| K |= 1 << 15; |
| |
| /********************************/ |
| /* HINTING FOR BGR? */ |
| /* Selector Bit: 9 */ |
| /* Return Bit(s): 16 */ |
| /* */ |
| /* Functionality still needs to be added */ |
| if ( ( args[0] & 512 ) != 0 && exc->bgr ) |
| K |= 1 << 16; |
| |
| if ( exc->rasterizer_version >= 38 ) |
| { |
| /********************************/ |
| /* SUBPIXEL POSITIONED? */ |
| /* Selector Bit: 10 */ |
| /* Return Bit(s): 17 */ |
| /* */ |
| /* Functionality still needs to be added */ |
| if ( ( args[0] & 1024 ) != 0 && exc->subpixel_positioned ) |
| K |= 1 << 17; |
| |
| /********************************/ |
| /* SYMMETRICAL SMOOTHING */ |
| /* Selector Bit: 11 */ |
| /* Return Bit(s): 18 */ |
| /* */ |
| /* Functionality still needs to be added */ |
| if ( ( args[0] & 2048 ) != 0 && exc->symmetrical_smoothing ) |
| K |= 1 << 18; |
| |
| /********************************/ |
| /* GRAY CLEARTYPE */ |
| /* Selector Bit: 12 */ |
| /* Return Bit(s): 19 */ |
| /* */ |
| /* Functionality still needs to be added */ |
| if ( ( args[0] & 4096 ) != 0 && exc->gray_cleartype ) |
| K |= 1 << 19; |
| } |
| } |
| } |
| |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| args[0] = K; |
| } |
| |
| |
| #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
| |
| /*************************************************************************/ |
| /* */ |
| /* GETVARIATION[]: get normalized variation (blend) coordinates */ |
| /* Opcode range: 0x91 */ |
| /* Stack: --> f2.14... */ |
| /* */ |
| /* XXX: UNDOCUMENTED! There is no official documentation from Apple for */ |
| /* this bytecode instruction. Active only if a font has GX */ |
| /* variation axes. */ |
| /* */ |
| static void |
| Ins_GETVARIATION( TT_ExecContext exc, |
| FT_Long* args ) |
| { |
| FT_UInt num_axes = exc->face->blend->num_axis; |
| FT_Fixed* coords = exc->face->blend->normalizedcoords; |
| |
| FT_UInt i; |
| |
| |
| if ( BOUNDS( num_axes, exc->stackSize + 1 - exc->top ) ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| return; |
| } |
| |
| for ( i = 0; i < num_axes; i++ ) |
| args[i] = coords[i] >> 2; /* convert 16.16 to 2.14 format */ |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* GETDATA[]: no idea what this is good for */ |
| /* Opcode range: 0x92 */ |
| /* Stack: --> 17 */ |
| /* */ |
| /* XXX: UNDOCUMENTED! There is no documentation from Apple for this */ |
| /* very weird bytecode instruction. */ |
| /* */ |
| static void |
| Ins_GETDATA( FT_Long* args ) |
| { |
| args[0] = 17; |
| } |
| |
| #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ |
| |
| |
| static void |
| Ins_UNKNOWN( TT_ExecContext exc ) |
| { |
| TT_DefRecord* def = exc->IDefs; |
| TT_DefRecord* limit = def + exc->numIDefs; |
| |
| |
| for ( ; def < limit; def++ ) |
| { |
| if ( (FT_Byte)def->opc == exc->opcode && def->active ) |
| { |
| TT_CallRec* call; |
| |
| |
| if ( exc->callTop >= exc->callSize ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| return; |
| } |
| |
| call = exc->callStack + exc->callTop++; |
| |
| call->Caller_Range = exc->curRange; |
| call->Caller_IP = exc->IP + 1; |
| call->Cur_Count = 1; |
| call->Def = def; |
| |
| Ins_Goto_CodeRange( exc, def->range, def->start ); |
| |
| exc->step_ins = FALSE; |
| return; |
| } |
| } |
| |
| exc->error = FT_THROW( Invalid_Opcode ); |
| } |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* 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. */ |
| /* */ |
| /*************************************************************************/ |
| |
| |
| /* documentation is in ttinterp.h */ |
| |
| FT_EXPORT_DEF( FT_Error ) |
| TT_RunIns( TT_ExecContext exc ) |
| { |
| FT_ULong ins_counter = 0; /* executed instructions counter */ |
| FT_ULong num_twilight_points; |
| FT_UShort i; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| FT_Byte opcode_pattern[1][2] = { |
| /* #8 TypeMan Talk Align */ |
| { |
| 0x06, /* SPVTL */ |
| 0x7D, /* RDTG */ |
| }, |
| }; |
| FT_UShort opcode_patterns = 1; |
| FT_UShort opcode_pointer[1] = { 0 }; |
| FT_UShort opcode_size[1] = { 1 }; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| exc->iup_called = FALSE; |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| /* |
| * Toggle backward compatibility according to what font wants, except |
| * when |
| * |
| * 1) we have a `tricky' font that heavily relies on the interpreter to |
| * render glyphs correctly, for example DFKai-SB, or |
| * 2) FT_RENDER_MODE_MONO (i.e, monochome rendering) is requested. |
| * |
| * In those cases, backward compatibility needs to be turned off to get |
| * correct rendering. The rendering is then completely up to the |
| * font's programming. |
| * |
| */ |
| if ( SUBPIXEL_HINTING_MINIMAL && |
| exc->subpixel_hinting_lean && |
| !FT_IS_TRICKY( &exc->face->root ) ) |
| exc->backward_compatibility = !( exc->GS.instruct_control & 4 ); |
| else |
| exc->backward_compatibility = FALSE; |
| |
| exc->iupx_called = FALSE; |
| exc->iupy_called = FALSE; |
| #endif |
| |
| /* We restrict the number of twilight points to a reasonable, */ |
| /* heuristic value to avoid slow execution of malformed bytecode. */ |
| num_twilight_points = FT_MAX( 30, |
| 2 * ( exc->pts.n_points + exc->cvtSize ) ); |
| if ( exc->twilight.n_points > num_twilight_points ) |
| { |
| if ( num_twilight_points > 0xFFFFU ) |
| num_twilight_points = 0xFFFFU; |
| |
| FT_TRACE5(( "TT_RunIns: Resetting number of twilight points\n" |
| " from %d to the more reasonable value %d\n", |
| exc->twilight.n_points, |
| num_twilight_points )); |
| exc->twilight.n_points = (FT_UShort)num_twilight_points; |
| } |
| |
| /* Set up loop detectors. We restrict the number of LOOPCALL loops */ |
| /* and the number of JMPR, JROT, and JROF calls with a negative */ |
| /* argument to values that depend on various parameters like the */ |
| /* size of the CVT table or the number of points in the current */ |
| /* glyph (if applicable). */ |
| /* */ |
| /* The idea is that in real-world bytecode you either iterate over */ |
| /* all CVT entries (in the `prep' table), or over all points (or */ |
| /* contours, in the `glyf' table) of a glyph, and such iterations */ |
| /* don't happen very often. */ |
| exc->loopcall_counter = 0; |
| exc->neg_jump_counter = 0; |
| |
| /* The maximum values are heuristic. */ |
| if ( exc->pts.n_points ) |
| exc->loopcall_counter_max = FT_MAX( 50, |
| 10 * exc->pts.n_points ) + |
| FT_MAX( 50, |
| exc->cvtSize / 10 ); |
| else |
| exc->loopcall_counter_max = 300 + 8 * exc->cvtSize; |
| |
| /* as a protection against an unreasonable number of CVT entries */ |
| /* we assume at most 100 control values per glyph for the counter */ |
| if ( exc->loopcall_counter_max > |
| 100 * (FT_ULong)exc->face->root.num_glyphs ) |
| exc->loopcall_counter_max = 100 * (FT_ULong)exc->face->root.num_glyphs; |
| |
| FT_TRACE5(( "TT_RunIns: Limiting total number of loops in LOOPCALL" |
| " to %d\n", exc->loopcall_counter_max )); |
| |
| exc->neg_jump_counter_max = exc->loopcall_counter_max; |
| FT_TRACE5(( "TT_RunIns: Limiting total number of backward jumps" |
| " to %d\n", exc->neg_jump_counter_max )); |
| |
| /* set PPEM and CVT functions */ |
| exc->tt_metrics.ratio = 0; |
| if ( exc->metrics.x_ppem != exc->metrics.y_ppem ) |
| { |
| /* non-square pixels, use the stretched routines */ |
| exc->func_cur_ppem = Current_Ppem_Stretched; |
| exc->func_read_cvt = Read_CVT_Stretched; |
| exc->func_write_cvt = Write_CVT_Stretched; |
| exc->func_move_cvt = Move_CVT_Stretched; |
| } |
| else |
| { |
| /* square pixels, use normal routines */ |
| exc->func_cur_ppem = Current_Ppem; |
| exc->func_read_cvt = Read_CVT; |
| exc->func_write_cvt = Write_CVT; |
| exc->func_move_cvt = Move_CVT; |
| } |
| |
| Compute_Funcs( exc ); |
| Compute_Round( exc, (FT_Byte)exc->GS.round_state ); |
| |
| do |
| { |
| exc->opcode = exc->code[exc->IP]; |
| |
| #ifdef FT_DEBUG_LEVEL_TRACE |
| { |
| FT_Long cnt = FT_MIN( 8, exc->top ); |
| FT_Long n; |
| |
| |
| /* if tracing level is 7, show current code position */ |
| /* and the first few stack elements also */ |
| FT_TRACE6(( " " )); |
| FT_TRACE7(( "%06d ", exc->IP )); |
| FT_TRACE6(( opcode_name[exc->opcode] + 2 )); |
| FT_TRACE7(( "%*s", *opcode_name[exc->opcode] == 'A' |
| ? 2 |
| : 12 - ( *opcode_name[exc->opcode] - '0' ), |
| "#" )); |
| for ( n = 1; n <= cnt; n++ ) |
| FT_TRACE7(( " %d", exc->stack[exc->top - n] )); |
| FT_TRACE6(( "\n" )); |
| } |
| #endif /* FT_DEBUG_LEVEL_TRACE */ |
| |
| if ( ( exc->length = opcode_length[exc->opcode] ) < 0 ) |
| { |
| if ( exc->IP + 1 >= exc->codeSize ) |
| goto LErrorCodeOverflow_; |
| |
| exc->length = 2 - exc->length * exc->code[exc->IP + 1]; |
| } |
| |
| if ( exc->IP + exc->length > exc->codeSize ) |
| goto LErrorCodeOverflow_; |
| |
| /* First, let's check for empty stack and overflow */ |
| exc->args = exc->top - ( Pop_Push_Count[exc->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 ( exc->args < 0 ) |
| { |
| if ( exc->pedantic_hinting ) |
| { |
| exc->error = FT_THROW( Too_Few_Arguments ); |
| goto LErrorLabel_; |
| } |
| |
| /* push zeroes onto the stack */ |
| for ( i = 0; i < Pop_Push_Count[exc->opcode] >> 4; i++ ) |
| exc->stack[i] = 0; |
| exc->args = 0; |
| } |
| |
| #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
| if ( exc->opcode == 0x91 ) |
| { |
| /* this is very special: GETVARIATION returns */ |
| /* a variable number of arguments */ |
| |
| /* it is the job of the application to `activate' GX handling, */ |
| /* this is, calling any of the GX API functions on the current */ |
| /* font to select a variation instance */ |
| if ( exc->face->blend ) |
| exc->new_top = exc->args + exc->face->blend->num_axis; |
| } |
| else |
| #endif |
| exc->new_top = exc->args + ( Pop_Push_Count[exc->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 ( exc->new_top > exc->stackSize ) |
| { |
| exc->error = FT_THROW( Stack_Overflow ); |
| goto LErrorLabel_; |
| } |
| |
| exc->step_ins = TRUE; |
| exc->error = FT_Err_Ok; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| |
| if ( SUBPIXEL_HINTING_INFINALITY ) |
| { |
| for ( i = 0; i < opcode_patterns; i++ ) |
| { |
| if ( opcode_pointer[i] < opcode_size[i] && |
| exc->opcode == opcode_pattern[i][opcode_pointer[i]] ) |
| { |
| opcode_pointer[i] += 1; |
| |
| if ( opcode_pointer[i] == opcode_size[i] ) |
| { |
| FT_TRACE6(( "sph: opcode ptrn: %d, %s %s\n", |
| i, |
| exc->face->root.family_name, |
| exc->face->root.style_name )); |
| |
| switch ( i ) |
| { |
| case 0: |
| break; |
| } |
| opcode_pointer[i] = 0; |
| } |
| } |
| else |
| opcode_pointer[i] = 0; |
| } |
| } |
| |
| #endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */ |
| |
| { |
| FT_Long* args = exc->stack + exc->args; |
| FT_Byte opcode = exc->opcode; |
| |
| |
| 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 */ |
| Ins_SxyTCA( exc ); |
| break; |
| |
| case 0x06: /* SPvTL // */ |
| case 0x07: /* SPvTL + */ |
| Ins_SPVTL( exc, args ); |
| break; |
| |
| case 0x08: /* SFvTL // */ |
| case 0x09: /* SFvTL + */ |
| Ins_SFVTL( exc, args ); |
| break; |
| |
| case 0x0A: /* SPvFS */ |
| Ins_SPVFS( exc, args ); |
| break; |
| |
| case 0x0B: /* SFvFS */ |
| Ins_SFVFS( exc, args ); |
| break; |
| |
| case 0x0C: /* GPv */ |
| Ins_GPV( exc, args ); |
| break; |
| |
| case 0x0D: /* GFv */ |
| Ins_GFV( exc, args ); |
| break; |
| |
| case 0x0E: /* SFvTPv */ |
| Ins_SFVTPV( exc ); |
| break; |
| |
| case 0x0F: /* ISECT */ |
| Ins_ISECT( exc, args ); |
| break; |
| |
| case 0x10: /* SRP0 */ |
| Ins_SRP0( exc, args ); |
| break; |
| |
| case 0x11: /* SRP1 */ |
| Ins_SRP1( exc, args ); |
| break; |
| |
| case 0x12: /* SRP2 */ |
| Ins_SRP2( exc, args ); |
| break; |
| |
| case 0x13: /* SZP0 */ |
| Ins_SZP0( exc, args ); |
| break; |
| |
| case 0x14: /* SZP1 */ |
| Ins_SZP1( exc, args ); |
| break; |
| |
| case 0x15: /* SZP2 */ |
| Ins_SZP2( exc, args ); |
| break; |
| |
| case 0x16: /* SZPS */ |
| Ins_SZPS( exc, args ); |
| break; |
| |
| case 0x17: /* SLOOP */ |
| Ins_SLOOP( exc, args ); |
| break; |
| |
| case 0x18: /* RTG */ |
| Ins_RTG( exc ); |
| break; |
| |
| case 0x19: /* RTHG */ |
| Ins_RTHG( exc ); |
| break; |
| |
| case 0x1A: /* SMD */ |
| Ins_SMD( exc, args ); |
| break; |
| |
| case 0x1B: /* ELSE */ |
| Ins_ELSE( exc ); |
| break; |
| |
| case 0x1C: /* JMPR */ |
| Ins_JMPR( exc, args ); |
| break; |
| |
| case 0x1D: /* SCVTCI */ |
| Ins_SCVTCI( exc, args ); |
| break; |
| |
| case 0x1E: /* SSWCI */ |
| Ins_SSWCI( exc, args ); |
| break; |
| |
| case 0x1F: /* SSW */ |
| Ins_SSW( exc, args ); |
| break; |
| |
| case 0x20: /* DUP */ |
| Ins_DUP( args ); |
| break; |
| |
| case 0x21: /* POP */ |
| Ins_POP(); |
| break; |
| |
| case 0x22: /* CLEAR */ |
| Ins_CLEAR( exc ); |
| break; |
| |
| case 0x23: /* SWAP */ |
| Ins_SWAP( args ); |
| break; |
| |
| case 0x24: /* DEPTH */ |
| Ins_DEPTH( exc, args ); |
| break; |
| |
| case 0x25: /* CINDEX */ |
| Ins_CINDEX( exc, args ); |
| break; |
| |
| case 0x26: /* MINDEX */ |
| Ins_MINDEX( exc, args ); |
| break; |
| |
| case 0x27: /* ALIGNPTS */ |
| Ins_ALIGNPTS( exc, args ); |
| break; |
| |
| case 0x28: /* RAW */ |
| Ins_UNKNOWN( exc ); |
| break; |
| |
| case 0x29: /* UTP */ |
| Ins_UTP( exc, args ); |
| break; |
| |
| case 0x2A: /* LOOPCALL */ |
| Ins_LOOPCALL( exc, args ); |
| break; |
| |
| case 0x2B: /* CALL */ |
| Ins_CALL( exc, args ); |
| break; |
| |
| case 0x2C: /* FDEF */ |
| Ins_FDEF( exc, args ); |
| break; |
| |
| case 0x2D: /* ENDF */ |
| Ins_ENDF( exc ); |
| break; |
| |
| case 0x2E: /* MDAP */ |
| case 0x2F: /* MDAP */ |
| Ins_MDAP( exc, args ); |
| break; |
| |
| case 0x30: /* IUP */ |
| case 0x31: /* IUP */ |
| Ins_IUP( exc ); |
| break; |
| |
| case 0x32: /* SHP */ |
| case 0x33: /* SHP */ |
| Ins_SHP( exc ); |
| break; |
| |
| case 0x34: /* SHC */ |
| case 0x35: /* SHC */ |
| Ins_SHC( exc, args ); |
| break; |
| |
| case 0x36: /* SHZ */ |
| case 0x37: /* SHZ */ |
| Ins_SHZ( exc, args ); |
| break; |
| |
| case 0x38: /* SHPIX */ |
| Ins_SHPIX( exc, args ); |
| break; |
| |
| case 0x39: /* IP */ |
| Ins_IP( exc ); |
| break; |
| |
| case 0x3A: /* MSIRP */ |
| case 0x3B: /* MSIRP */ |
| Ins_MSIRP( exc, args ); |
| break; |
| |
| case 0x3C: /* AlignRP */ |
| Ins_ALIGNRP( exc ); |
| break; |
| |
| case 0x3D: /* RTDG */ |
| Ins_RTDG( exc ); |
| break; |
| |
| case 0x3E: /* MIAP */ |
| case 0x3F: /* MIAP */ |
| Ins_MIAP( exc, args ); |
| break; |
| |
| case 0x40: /* NPUSHB */ |
| Ins_NPUSHB( exc, args ); |
| break; |
| |
| case 0x41: /* NPUSHW */ |
| Ins_NPUSHW( exc, args ); |
| break; |
| |
| case 0x42: /* WS */ |
| Ins_WS( exc, args ); |
| break; |
| |
| case 0x43: /* RS */ |
| Ins_RS( exc, args ); |
| break; |
| |
| case 0x44: /* WCVTP */ |
| Ins_WCVTP( exc, args ); |
| break; |
| |
| case 0x45: /* RCVT */ |
| Ins_RCVT( exc, args ); |
| break; |
| |
| case 0x46: /* GC */ |
| case 0x47: /* GC */ |
| Ins_GC( exc, args ); |
| break; |
| |
| case 0x48: /* SCFS */ |
| Ins_SCFS( exc, args ); |
| break; |
| |
| case 0x49: /* MD */ |
| case 0x4A: /* MD */ |
| Ins_MD( exc, args ); |
| break; |
| |
| case 0x4B: /* MPPEM */ |
| Ins_MPPEM( exc, args ); |
| break; |
| |
| case 0x4C: /* MPS */ |
| Ins_MPS( exc, args ); |
| break; |
| |
| case 0x4D: /* FLIPON */ |
| Ins_FLIPON( exc ); |
| break; |
| |
| case 0x4E: /* FLIPOFF */ |
| Ins_FLIPOFF( exc ); |
| break; |
| |
| case 0x4F: /* DEBUG */ |
| Ins_DEBUG( exc ); |
| break; |
| |
| case 0x50: /* LT */ |
| Ins_LT( args ); |
| break; |
| |
| case 0x51: /* LTEQ */ |
| Ins_LTEQ( args ); |
| break; |
| |
| case 0x52: /* GT */ |
| Ins_GT( args ); |
| break; |
| |
| case 0x53: /* GTEQ */ |
| Ins_GTEQ( args ); |
| break; |
| |
| case 0x54: /* EQ */ |
| Ins_EQ( args ); |
| break; |
| |
| case 0x55: /* NEQ */ |
| Ins_NEQ( args ); |
| break; |
| |
| case 0x56: /* ODD */ |
| Ins_ODD( exc, args ); |
| break; |
| |
| case 0x57: /* EVEN */ |
| Ins_EVEN( exc, args ); |
| break; |
| |
| case 0x58: /* IF */ |
| Ins_IF( exc, args ); |
| break; |
| |
| case 0x59: /* EIF */ |
| Ins_EIF(); |
| break; |
| |
| case 0x5A: /* AND */ |
| Ins_AND( args ); |
| break; |
| |
| case 0x5B: /* OR */ |
| Ins_OR( args ); |
| break; |
| |
| case 0x5C: /* NOT */ |
| Ins_NOT( args ); |
| break; |
| |
| case 0x5D: /* DELTAP1 */ |
| Ins_DELTAP( exc, args ); |
| break; |
| |
| case 0x5E: /* SDB */ |
| Ins_SDB( exc, args ); |
| break; |
| |
| case 0x5F: /* SDS */ |
| Ins_SDS( exc, args ); |
| break; |
| |
| case 0x60: /* ADD */ |
| Ins_ADD( args ); |
| break; |
| |
| case 0x61: /* SUB */ |
| Ins_SUB( args ); |
| break; |
| |
| case 0x62: /* DIV */ |
| Ins_DIV( exc, args ); |
| break; |
| |
| case 0x63: /* MUL */ |
| Ins_MUL( args ); |
| break; |
| |
| case 0x64: /* ABS */ |
| Ins_ABS( args ); |
| break; |
| |
| case 0x65: /* NEG */ |
| Ins_NEG( args ); |
| break; |
| |
| case 0x66: /* FLOOR */ |
| Ins_FLOOR( args ); |
| break; |
| |
| case 0x67: /* CEILING */ |
| Ins_CEILING( args ); |
| break; |
| |
| case 0x68: /* ROUND */ |
| case 0x69: /* ROUND */ |
| case 0x6A: /* ROUND */ |
| case 0x6B: /* ROUND */ |
| Ins_ROUND( exc, args ); |
| break; |
| |
| case 0x6C: /* NROUND */ |
| case 0x6D: /* NROUND */ |
| case 0x6E: /* NRRUND */ |
| case 0x6F: /* NROUND */ |
| Ins_NROUND( exc, args ); |
| break; |
| |
| case 0x70: /* WCVTF */ |
| Ins_WCVTF( exc, args ); |
| break; |
| |
| case 0x71: /* DELTAP2 */ |
| case 0x72: /* DELTAP3 */ |
| Ins_DELTAP( exc, args ); |
| break; |
| |
| case 0x73: /* DELTAC0 */ |
| case 0x74: /* DELTAC1 */ |
| case 0x75: /* DELTAC2 */ |
| Ins_DELTAC( exc, args ); |
| break; |
| |
| case 0x76: /* SROUND */ |
| Ins_SROUND( exc, args ); |
| break; |
| |
| case 0x77: /* S45Round */ |
| Ins_S45ROUND( exc, args ); |
| break; |
| |
| case 0x78: /* JROT */ |
| Ins_JROT( exc, args ); |
| break; |
| |
| case 0x79: /* JROF */ |
| Ins_JROF( exc, args ); |
| break; |
| |
| case 0x7A: /* ROFF */ |
| Ins_ROFF( exc ); |
| break; |
| |
| case 0x7B: /* ???? */ |
| Ins_UNKNOWN( exc ); |
| break; |
| |
| case 0x7C: /* RUTG */ |
| Ins_RUTG( exc ); |
| break; |
| |
| case 0x7D: /* RDTG */ |
| Ins_RDTG( exc ); |
| break; |
| |
| case 0x7E: /* SANGW */ |
| Ins_SANGW(); |
| break; |
| |
| case 0x7F: /* AA */ |
| Ins_AA(); |
| break; |
| |
| case 0x80: /* FLIPPT */ |
| Ins_FLIPPT( exc ); |
| break; |
| |
| case 0x81: /* FLIPRGON */ |
| Ins_FLIPRGON( exc, args ); |
| break; |
| |
| case 0x82: /* FLIPRGOFF */ |
| Ins_FLIPRGOFF( exc, args ); |
| break; |
| |
| case 0x83: /* UNKNOWN */ |
| case 0x84: /* UNKNOWN */ |
| Ins_UNKNOWN( exc ); |
| break; |
| |
| case 0x85: /* SCANCTRL */ |
| Ins_SCANCTRL( exc, args ); |
| break; |
| |
| case 0x86: /* SDPvTL */ |
| case 0x87: /* SDPvTL */ |
| Ins_SDPVTL( exc, args ); |
| break; |
| |
| case 0x88: /* GETINFO */ |
| Ins_GETINFO( exc, args ); |
| break; |
| |
| case 0x89: /* IDEF */ |
| Ins_IDEF( exc, args ); |
| break; |
| |
| case 0x8A: /* ROLL */ |
| Ins_ROLL( args ); |
| break; |
| |
| case 0x8B: /* MAX */ |
| Ins_MAX( args ); |
| break; |
| |
| case 0x8C: /* MIN */ |
| Ins_MIN( args ); |
| break; |
| |
| case 0x8D: /* SCANTYPE */ |
| Ins_SCANTYPE( exc, args ); |
| break; |
| |
| case 0x8E: /* INSTCTRL */ |
| Ins_INSTCTRL( exc, args ); |
| break; |
| |
| case 0x8F: /* ADJUST */ |
| case 0x90: /* ADJUST */ |
| Ins_UNKNOWN( exc ); |
| break; |
| |
| #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
| case 0x91: |
| /* it is the job of the application to `activate' GX handling, */ |
| /* this is, calling any of the GX API functions on the current */ |
| /* font to select a variation instance */ |
| if ( exc->face->blend ) |
| Ins_GETVARIATION( exc, args ); |
| else |
| Ins_UNKNOWN( exc ); |
| break; |
| |
| case 0x92: |
| /* there is at least one MS font (LaoUI.ttf version 5.01) that */ |
| /* uses IDEFs for 0x91 and 0x92; for this reason we activate */ |
| /* GETDATA for GX fonts only, similar to GETVARIATION */ |
| if ( exc->face->blend ) |
| Ins_GETDATA( args ); |
| else |
| Ins_UNKNOWN( exc ); |
| break; |
| #endif |
| |
| default: |
| if ( opcode >= 0xE0 ) |
| Ins_MIRP( exc, args ); |
| else if ( opcode >= 0xC0 ) |
| Ins_MDRP( exc, args ); |
| else if ( opcode >= 0xB8 ) |
| Ins_PUSHW( exc, args ); |
| else if ( opcode >= 0xB0 ) |
| Ins_PUSHB( exc, args ); |
| else |
| Ins_UNKNOWN( exc ); |
| } |
| } |
| |
| if ( exc->error ) |
| { |
| switch ( exc->error ) |
| { |
| /* looking for redefined instructions */ |
| case FT_ERR( Invalid_Opcode ): |
| { |
| TT_DefRecord* def = exc->IDefs; |
| TT_DefRecord* limit = def + exc->numIDefs; |
| |
| |
| for ( ; def < limit; def++ ) |
| { |
| if ( def->active && exc->opcode == (FT_Byte)def->opc ) |
| { |
| TT_CallRec* callrec; |
| |
| |
| if ( exc->callTop >= exc->callSize ) |
| { |
| exc->error = FT_THROW( Invalid_Reference ); |
| goto LErrorLabel_; |
| } |
| |
| callrec = &exc->callStack[exc->callTop]; |
| |
| callrec->Caller_Range = exc->curRange; |
| callrec->Caller_IP = exc->IP + 1; |
| callrec->Cur_Count = 1; |
| callrec->Def = def; |
| |
| if ( Ins_Goto_CodeRange( exc, |
| def->range, |
| def->start ) == FAILURE ) |
| goto LErrorLabel_; |
| |
| goto LSuiteLabel_; |
| } |
| } |
| } |
| |
| exc->error = FT_THROW( 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 |
| } |
| } |
| |
| exc->top = exc->new_top; |
| |
| if ( exc->step_ins ) |
| exc->IP += exc->length; |
| |
| /* increment instruction counter and check if we didn't */ |
| /* run this program for too long (e.g. infinite loops). */ |
| if ( ++ins_counter > TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES ) |
| return FT_THROW( Execution_Too_Long ); |
| |
| LSuiteLabel_: |
| if ( exc->IP >= exc->codeSize ) |
| { |
| if ( exc->callTop > 0 ) |
| { |
| exc->error = FT_THROW( Code_Overflow ); |
| goto LErrorLabel_; |
| } |
| else |
| goto LNo_Error_; |
| } |
| } while ( !exc->instruction_trap ); |
| |
| LNo_Error_: |
| FT_TRACE4(( " %d instructions executed\n", ins_counter )); |
| return FT_Err_Ok; |
| |
| LErrorCodeOverflow_: |
| exc->error = FT_THROW( Code_Overflow ); |
| |
| LErrorLabel_: |
| if ( exc->error && !exc->instruction_trap ) |
| FT_TRACE1(( " The interpreter returned error 0x%x\n", exc->error )); |
| |
| return exc->error; |
| } |
| |
| #else /* !TT_USE_BYTECODE_INTERPRETER */ |
| |
| /* ANSI C doesn't like empty source files */ |
| typedef int _tt_interp_dummy; |
| |
| #endif /* !TT_USE_BYTECODE_INTERPRETER */ |
| |
| |
| /* END */ |