| /**************************************************************************** |
| * |
| * ttinterp.c |
| * |
| * TrueType bytecode interpreter (body). |
| * |
| * Copyright (C) 1996-2021 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 <freetype/internal/ftdebug.h> |
| #include <freetype/internal/ftcalc.h> |
| #include <freetype/fttrigon.h> |
| #include <freetype/ftsystem.h> |
| #include <freetype/ftdriver.h> |
| #include <freetype/ftmm.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 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 glyf cvt working area */ |
| FT_FREE( exec->glyfCvt ); |
| exec->glyfCvtSize = 0; |
| |
| /* free glyf storage working area */ |
| FT_FREE( exec->glyfStorage ); |
| exec->glyfStoreSize = 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: |
| * 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_QREALLOC( *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. |
| * |
| * Note that not all members of `TT_ExecContext` get initialized. |
| */ |
| 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 = (FT_ULong)exec->glyphSize; |
| error = Update_Max( exec->memory, |
| &tmp, |
| sizeof ( FT_Byte ), |
| (void*)&exec->glyphIns, |
| maxp->maxSizeOfInstructions ); |
| exec->glyphSize = (FT_UInt)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: |
| * 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; |
| |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY |
| exec->iup_called = FALSE; |
| #endif |
| #ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL |
| exec->iupx_called = FALSE; |
| exec->iupy_called = FALSE; |
| #endif |
| |
| /* 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 and zero everything inside */ |
| if ( FT_NEW( exec ) ) |
| goto Fail; |
| |
| /* create callStack here, other allocations delayed */ |
| exec->memory = memory; |
| exec->callSize = 32; |
| |
| if ( FT_QNEW_ARRAY( exec->callStack, exec->callSize ) ) |
| FT_FREE( exec ); |
| |
| Fail: |
| return exec; |
| } |
| |
| |
| /************************************************************************** |
| * |
| * 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 */ |
| |
| /* 0x00 */ |
| /* SVTCA[0] */ PACK( 0, 0 ), |
| /* SVTCA[1] */ PACK( 0, 0 ), |
| /* SPVTCA[0] */ PACK( 0, 0 ), |
| /* SPVTCA[1] */ PACK( 0, 0 ), |
| /* SFVTCA[0] */ PACK( 0, 0 ), |
| /* SFVTCA[1] */ PACK( 0, 0 ), |
| /* SPVTL[0] */ PACK( 2, 0 ), |
| /* SPVTL[1] */ PACK( 2, 0 ), |
| /* SFVTL[0] */ PACK( 2, 0 ), |
| /* SFVTL[1] */ 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 ), |
| |
| /* 0x10 */ |
| /* 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 ), |
| |
| /* 0x20 */ |
| /* 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 ), |
| |
| /* 0x30 */ |
| /* 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 ), |
| |
| /* 0x40 */ |
| /* 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 ), |
| |
| /* 0x50 */ |
| /* 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 ), |
| |
| /* 0x60 */ |
| /* 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 ), |
| |
| /* 0x70 */ |
| /* WCVTF */ PACK( 2, 0 ), |
| /* DELTAP2 */ PACK( 1, 0 ), |
| /* DELTAP3 */ PACK( 1, 0 ), |
| /* DELTAC1 */ PACK( 1, 0 ), |
| /* DELTAC2 */ PACK( 1, 0 ), |
| /* DELTAC3 */ 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 ), |
| |
| /* 0x80 */ |
| /* 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 ), |
| |
| /* 0x90 */ |
| /* 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 ), |
| |
| /* 0xA0 */ |
| /* 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 ), |
| |
| /* 0xB0 */ |
| /* 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 ), |
| |
| /* 0xC0 */ |
| /* 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 ), |
| |
| /* 0xD0 */ |
| /* 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 ), |
| |
| /* 0xE0 */ |
| /* 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 ), |
| |
| /* 0xF0 */ |
| /* 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] = |
| { |
| /* 0x00 */ |
| "8 SVTCA[y]", |
| "8 SVTCA[x]", |
| "9 SPVTCA[y]", |
| "9 SPVTCA[x]", |
| "9 SFVTCA[y]", |
| "9 SFVTCA[x]", |
| "9 SPVTL[||]", |
| "8 SPVTL[+]", |
| "9 SFVTL[||]", |
| "8 SFVTL[+]", |
| "5 SPVFS", |
| "5 SFVFS", |
| "3 GPV", |
| "3 GFV", |
| "6 SFVTPV", |
| "5 ISECT", |
| |
| /* 0x10 */ |
| "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", |
| |
| /* 0x20 */ |
| "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", |
| "6 MDAP[]", |
| "9 MDAP[rnd]", |
| |
| /* 0x30 */ |
| "6 IUP[y]", |
| "6 IUP[x]", |
| "8 SHP[rp2]", |
| "8 SHP[rp1]", |
| "8 SHC[rp2]", |
| "8 SHC[rp1]", |
| "8 SHZ[rp2]", |
| "8 SHZ[rp1]", |
| "5 SHPIX", |
| "2 IP", |
| "7 MSIRP[]", |
| "A MSIRP[rp0]", |
| "7 ALIGNRP", |
| "4 RTDG", |
| "6 MIAP[]", |
| "9 MIAP[rnd]", |
| |
| /* 0x40 */ |
| "6 NPUSHB", |
| "6 NPUSHW", |
| "2 WS", |
| "2 RS", |
| "5 WCVTP", |
| "4 RCVT", |
| "8 GC[curr]", |
| "8 GC[orig]", |
| "4 SCFS", |
| "8 MD[curr]", |
| "8 MD[orig]", |
| "5 MPPEM", |
| "3 MPS", |
| "6 FLIPON", |
| "7 FLIPOFF", |
| "5 DEBUG", |
| |
| /* 0x50 */ |
| "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", |
| |
| /* 0x60 */ |
| "3 ADD", |
| "3 SUB", |
| "3 DIV", |
| "3 MUL", |
| "3 ABS", |
| "3 NEG", |
| "5 FLOOR", |
| "7 CEILING", |
| "8 ROUND[G]", |
| "8 ROUND[B]", |
| "8 ROUND[W]", |
| "7 ROUND[]", |
| "9 NROUND[G]", |
| "9 NROUND[B]", |
| "9 NROUND[W]", |
| "8 NROUND[]", |
| |
| /* 0x70 */ |
| "5 WCVTF", |
| "7 DELTAP2", |
| "7 DELTAP3", |
| "7 DELTAC1", |
| "7 DELTAC2", |
| "7 DELTAC3", |
| "6 SROUND", |
| "8 S45ROUND", |
| "4 JROT", |
| "4 JROF", |
| "4 ROFF", |
| "7 INS_$7B", |
| "4 RUTG", |
| "4 RDTG", |
| "5 SANGW", |
| "2 AA", |
| |
| /* 0x80 */ |
| "6 FLIPPT", |
| "8 FLIPRGON", |
| "9 FLIPRGOFF", |
| "7 INS_$83", |
| "7 INS_$84", |
| "8 SCANCTRL", |
| "A SDPVTL[||]", |
| "9 SDPVTL[+]", |
| "7 GETINFO", |
| "4 IDEF", |
| "4 ROLL", |
| "3 MAX", |
| "3 MIN", |
| "8 SCANTYPE", |
| "8 INSTCTRL", |
| "7 INS_$8F", |
| |
| /* 0x90 */ |
| "7 INS_$90", |
| #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT |
| "C GETVARIATION", |
| "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", |
| |
| /* 0xA0 */ |
| "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", |
| |
| /* 0xB0 */ |
| "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]", |
| |
| /* 0xC0 */ |
| "7 MDRP[G]", |
| "7 MDRP[B]", |
| "7 MDRP[W]", |
| "6 MDRP[]", |
| "8 MDRP[rG]", |
| "8 MDRP[rB]", |
| "8 MDRP[rW]", |
| "7 MDRP[r]", |
| "8 MDRP[mG]", |
| "8 MDRP[mB]", |
| "8 MDRP[mW]", |
| "7 MDRP[m]", |
| "9 MDRP[mrG]", |
| "9 MDRP[mrB]", |
| "9 MDRP[mrW]", |
| "8 MDRP[mr]", |
| |
| /* 0xD0 */ |
| "8 MDRP[pG]", |
| "8 MDRP[pB]", |
| "8 MDRP[pW]", |
| "7 MDRP[p]", |
| "9 MDRP[prG]", |
| "9 MDRP[prB]", |
| "9 MDRP[prW]", |
| "8 MDRP[pr]", |
| "9 MDRP[pmG]", |
| "9 MDRP[pmB]", |
| "9 MDRP[pmW]", |
| "8 MDRP[pm]", |
| "A MDRP[pmrG]", |
| "A MDRP[pmrB]", |
| "A MDRP[pmrW]", |
| "9 MDRP[pmr]", |
| |
| /* 0xE0 */ |
| "7 MIRP[G]", |
| "7 MIRP[B]", |
| "7 MIRP[W]", |
| "6 MIRP[]", |
| "8 MIRP[rG]", |
| "8 MIRP[rB]", |
| "8 MIRP[rW]", |
| "7 MIRP[r]", |
| "8 MIRP[mG]", |
| "8 MIRP[mB]", |
| "8 MIRP[mW]", |
| "7 MIRP[m]", |
| "9 MIRP[mrG]", |
| "9 MIRP[mrB]", |
| "9 MIRP[mrW]", |
| "8 MIRP[mr]", |
| |
| /* 0xF0 */ |
| "8 MIRP[pG]", |
| "8 MIRP[pB]", |
| "8 MIRP[pW]", |
| "7 MIRP[p]", |
| "9 MIRP[prG]", |
| "9 MIRP[prB]", |
| "9 MIRP[prW]", |
| "8 MIRP[pr]", |
| "9 MIRP[pmG]", |
| "9 MIRP[pmB]", |
| "9 MIRP[pmW]", |
| "8 MIRP[pm]", |
| "A MIRP[pmrG]", |
| "A MIRP[pmrB]", |
| "A MIRP[pmrW]", |
| "9 MIRP[pmr]" |
| }; |
| |
| #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 ) ); |
| } |
| |
| |
| static void |
| Modify_CVT_Check( TT_ExecContext exc ) |
| { |
| /* TT_RunIns sets origCvt and restores cvt to origCvt when done. */ |
| if ( exc->iniRange == tt_coderange_glyph && |
| exc->cvt == exc->origCvt ) |
| { |
| exc->error = Update_Max( exc->memory, |
| &exc->glyfCvtSize, |
| sizeof ( FT_Long ), |
| (void*)&exc->glyfCvt, |
| exc->cvtSize ); |
| if ( exc->error ) |
| return; |
| |
| FT_ARRAY_COPY( exc->glyfCvt, exc->cvt, exc->glyfCvtSize ); |
| exc->cvt = exc->glyfCvt; |
| } |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| Write_CVT( TT_ExecContext exc, |
| FT_ULong idx, |
| FT_F26Dot6 value ) |
| { |
| Modify_CVT_Check( exc ); |
| if ( exc->error ) |
| return; |
| |
| exc->cvt[idx] = value; |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| Write_CVT_Stretched( TT_ExecContext exc, |
| FT_ULong idx, |
| FT_F26Dot6 value ) |
| { |
| Modify_CVT_Check( exc ); |
| if ( exc->error ) |
| return; |
| |
| exc->cvt[idx] = FT_DivFix( value, Current_Ratio( exc ) ); |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| Move_CVT( TT_ExecContext exc, |
| FT_ULong idx, |
| FT_F26Dot6 value ) |
| { |
| Modify_CVT_Check( exc ); |
| if ( exc->error ) |
| return; |
| |
| exc->cvt[idx] = ADD_LONG( exc->cvt[idx], value ); |
| } |
| |
| |
| FT_CALLBACK_DEF( void ) |
| Move_CVT_Stretched( TT_ExecContext exc, |
| FT_ULong idx, |
| FT_F26Dot6 value ) |
| { |
| Modify_CVT_Check( exc ); |
| if ( exc->error ) |
| return; |
| |
| exc->cvt[idx] = ADD_LONG( 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; |
| } |
| |
| |
| /* |
| * |
| * Apple's TrueType specification at |
| * |
| * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#order |
| * |
| * gives the following order of operations in instructions that move |
| * points. |
| * |
| * - check single width cut-in (MIRP, MDRP) |
| * |
| * - check control value cut-in (MIRP, MIAP) |
| * |
| * - apply engine compensation (MIRP, MDRP) |
| * |
| * - round distance (MIRP, MDRP) or value (MIAP, MDAP) |
| * |
| * - check minimum distance (MIRP,MDRP) |
| * |
| * - move point (MIRP, MDRP, MIAP, MSIRP, MDAP) |
| * |
| * For rounding instructions, engine compensation happens before rounding. |
| * |
| */ |
| |
| |
| /************************************************************************** |
| * |
| * @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. |
| * |
| * color :: |
| * The engine compensation color. |
| * |
| * @Return: |
| * The compensated distance. |
| */ |
| static FT_F26Dot6 |
| Round_None( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_Int color ) |
| { |
| FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; |
| FT_F26Dot6 val; |
| |
| |
| 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. |
| * |
| * color :: |
| * The engine compensation color. |
| * |
| * @Return: |
| * Rounded distance. |
| */ |
| static FT_F26Dot6 |
| Round_To_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_Int color ) |
| { |
| FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; |
| FT_F26Dot6 val; |
| |
| |
| 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. |
| * |
| * color :: |
| * The engine compensation color. |
| * |
| * @Return: |
| * Rounded distance. |
| */ |
| static FT_F26Dot6 |
| Round_To_Half_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_Int color ) |
| { |
| FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; |
| FT_F26Dot6 val; |
| |
| |
| 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. |
| * |
| * color :: |
| * The engine compensation color. |
| * |
| * @Return: |
| * Rounded distance. |
| */ |
| static FT_F26Dot6 |
| Round_Down_To_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_Int color ) |
| { |
| FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; |
| FT_F26Dot6 val; |
| |
| |
| 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. |
| * |
| * color :: |
| * The engine compensation color. |
| * |
| * @Return: |
| * Rounded distance. |
| */ |
| static FT_F26Dot6 |
| Round_Up_To_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_Int color ) |
| { |
| FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; |
| FT_F26Dot6 val; |
| |
| |
| 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. |
| * |
| * color :: |
| * The engine compensation color. |
| * |
| * @Return: |
| * Rounded distance. |
| */ |
| static FT_F26Dot6 |
| Round_To_Double_Grid( TT_ExecContext exc, |
| FT_F26Dot6 distance, |
| FT_Int color ) |
| { |
| FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; |
| FT_F26Dot6 val; |
| |
| |
| 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. |
| * |
| * color :: |
| * The engine compensation color. |
| * |
| * @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_Int color ) |
| { |
| FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; |
| FT_F26Dot6 val; |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = ADD_LONG( distance, |
| exc->threshold - exc->phase + compensation ) & |
| -exc->period; |
| val = ADD_LONG( val, exc->phase ); |
| if ( val < 0 ) |
| val = exc->phase; |
| } |
| else |
| { |
| val = NEG_LONG( SUB_LONG( exc->threshold - exc->phase + compensation, |
| distance ) & |
| -exc->period ); |
| val = SUB_LONG( 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. |
| * |
| * color :: |
| * The engine compensation color. |
| * |
| * @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_Int color ) |
| { |
| FT_F26Dot6 compensation = exc->tt_metrics.compensations[color]; |
| FT_F26Dot6 val; |
| |
| |
| if ( distance >= 0 ) |
| { |
| val = ( ADD_LONG( distance, |
| exc->threshold - exc->phase + compensation ) / |
| exc->period ) * exc->period; |
| val = ADD_LONG( 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 = SUB_LONG( 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], 3 ) & 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], 3 ) & 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_LONG( 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 |
| { |
| /* TT_RunIns sets origStorage and restores storage to origStorage */ |
| /* when done. */ |
| if ( exc->iniRange == tt_coderange_glyph && |
| exc->storage == exc->origStorage ) |
| { |
| FT_ULong tmp = (FT_ULong)exc->glyfStoreSize; |
| |
| |
| exc->error = Update_Max( exc->memory, |
| &tmp, |
| sizeof ( FT_Long ), |
| (void*)&exc->glyfStorage, |
| exc->storeSize ); |
| exc->glyfStoreSize = (FT_UShort)tmp; |
| if ( exc->error ) |
| return; |
| |
| FT_ARRAY_COPY( exc->glyfStorage, exc->storage, exc->glyfStoreSize ); |
| exc->storage = exc->glyfStorage; |
| } |
| |
| 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->opcode & 3 ); |
| } |
| |
| |
| /************************************************************************** |
| * |
| * 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->opcode & 3 ); |
| } |
| |
| |
| /************************************************************************** |
| * |
| * 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 ( |