/***************************************************************************/ | |
/* */ | |
/* ttinterp.c */ | |
/* */ | |
/* TrueType bytecode interpreter (body). */ | |
/* */ | |
/* Copyright 1996-2015 by */ | |
/* David Turner, Robert Wilhelm, and Werner Lemberg. */ | |
/* */ | |
/* This file is part of the FreeType project, and may only be used, */ | |
/* modified, and distributed under the terms of the FreeType project */ | |
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ | |
/* this file you indicate that you have read the license and */ | |
/* understand and accept it fully. */ | |
/* */ | |
/***************************************************************************/ | |
/* Greg Hitchcock from Microsoft has helped a lot in resolving unclear */ | |
/* issues; many thanks! */ | |
#include <ft2build.h> | |
#include FT_INTERNAL_DEBUG_H | |
#include FT_INTERNAL_CALC_H | |
#include FT_TRIGONOMETRY_H | |
#include FT_SYSTEM_H | |
#include FT_TRUETYPE_DRIVER_H | |
#include "ttinterp.h" | |
#include "tterrors.h" | |
#include "ttsubpix.h" | |
#ifdef TT_USE_BYTECODE_INTERPRETER | |
/*************************************************************************/ | |
/* */ | |
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */ | |
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ | |
/* messages during execution. */ | |
/* */ | |
#undef FT_COMPONENT | |
#define FT_COMPONENT trace_ttinterp | |
#define SUBPIXEL_HINTING \ | |
( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \ | |
TT_INTERPRETER_VERSION_38 ) | |
#define PROJECT( v1, v2 ) \ | |
exc->func_project( exc, (v1)->x - (v2)->x, (v1)->y - (v2)->y ) | |
#define DUALPROJ( v1, v2 ) \ | |
exc->func_dualproj( exc, (v1)->x - (v2)->x, (v1)->y - (v2)->y ) | |
#define FAST_PROJECT( v ) \ | |
exc->func_project( exc, (v)->x, (v)->y ) | |
#define FAST_DUALPROJ( v ) \ | |
exc->func_dualproj( exc, (v)->x, (v)->y ) | |
/*************************************************************************/ | |
/* */ | |
/* Instruction dispatch function, as used by the interpreter. */ | |
/* */ | |
typedef void (*TInstruction_Function)( TT_ExecContext exc, | |
FT_Long* args ); | |
/*************************************************************************/ | |
/* */ | |
/* Two simple bounds-checking macros. */ | |
/* */ | |
#define BOUNDS( x, n ) ( (FT_UInt)(x) >= (FT_UInt)(n) ) | |
#define BOUNDSL( x, n ) ( (FT_ULong)(x) >= (FT_ULong)(n) ) | |
#undef SUCCESS | |
#define SUCCESS 0 | |
#undef FAILURE | |
#define FAILURE 1 | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
#define GUESS_VECTOR( V ) \ | |
do \ | |
{ \ | |
if ( exc->face->unpatented_hinting ) \ | |
{ \ | |
exc->GS.V.x = (FT_F2Dot14)( exc->GS.both_x_axis ? 0x4000 : 0 ); \ | |
exc->GS.V.y = (FT_F2Dot14)( exc->GS.both_x_axis ? 0 : 0x4000 ); \ | |
} \ | |
} while (0) | |
#else | |
#define GUESS_VECTOR( V ) do { } while (0) | |
#endif | |
/*************************************************************************/ | |
/* */ | |
/* CODERANGE FUNCTIONS */ | |
/* */ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* TT_Goto_CodeRange */ | |
/* */ | |
/* <Description> */ | |
/* Switches to a new code range (updates the code related elements in */ | |
/* `exec', and `IP'). */ | |
/* */ | |
/* <Input> */ | |
/* range :: The new execution code range. */ | |
/* */ | |
/* IP :: The new IP in the new code range. */ | |
/* */ | |
/* <InOut> */ | |
/* exec :: The target execution context. */ | |
/* */ | |
FT_LOCAL_DEF( void ) | |
TT_Goto_CodeRange( TT_ExecContext exec, | |
FT_Int range, | |
FT_Long IP ) | |
{ | |
TT_CodeRange* coderange; | |
FT_ASSERT( range >= 1 && range <= 3 ); | |
coderange = &exec->codeRangeTable[range - 1]; | |
FT_ASSERT( coderange->base != NULL ); | |
/* NOTE: Because the last instruction of a program may be a CALL */ | |
/* which will return to the first byte *after* the code */ | |
/* range, we test for IP <= Size instead of IP < Size. */ | |
/* */ | |
FT_ASSERT( IP <= coderange->size ); | |
exec->code = coderange->base; | |
exec->codeSize = coderange->size; | |
exec->IP = IP; | |
exec->curRange = range; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* TT_Set_CodeRange */ | |
/* */ | |
/* <Description> */ | |
/* Sets a code range. */ | |
/* */ | |
/* <Input> */ | |
/* range :: The code range index. */ | |
/* */ | |
/* base :: The new code base. */ | |
/* */ | |
/* length :: The range size in bytes. */ | |
/* */ | |
/* <InOut> */ | |
/* exec :: The target execution context. */ | |
/* */ | |
FT_LOCAL_DEF( void ) | |
TT_Set_CodeRange( TT_ExecContext exec, | |
FT_Int range, | |
void* base, | |
FT_Long length ) | |
{ | |
FT_ASSERT( range >= 1 && range <= 3 ); | |
exec->codeRangeTable[range - 1].base = (FT_Byte*)base; | |
exec->codeRangeTable[range - 1].size = length; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* TT_Clear_CodeRange */ | |
/* */ | |
/* <Description> */ | |
/* Clears a code range. */ | |
/* */ | |
/* <Input> */ | |
/* range :: The code range index. */ | |
/* */ | |
/* <InOut> */ | |
/* exec :: The target execution context. */ | |
/* */ | |
FT_LOCAL_DEF( void ) | |
TT_Clear_CodeRange( TT_ExecContext exec, | |
FT_Int range ) | |
{ | |
FT_ASSERT( range >= 1 && range <= 3 ); | |
exec->codeRangeTable[range - 1].base = NULL; | |
exec->codeRangeTable[range - 1].size = 0; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* EXECUTION CONTEXT ROUTINES */ | |
/* */ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* TT_Done_Context */ | |
/* */ | |
/* <Description> */ | |
/* Destroys a given context. */ | |
/* */ | |
/* <Input> */ | |
/* exec :: A handle to the target execution context. */ | |
/* */ | |
/* memory :: A handle to the parent memory object. */ | |
/* */ | |
/* <Note> */ | |
/* Only the glyph loader and debugger should call this function. */ | |
/* */ | |
FT_LOCAL_DEF( void ) | |
TT_Done_Context( TT_ExecContext exec ) | |
{ | |
FT_Memory memory = exec->memory; | |
/* points zone */ | |
exec->maxPoints = 0; | |
exec->maxContours = 0; | |
/* free stack */ | |
FT_FREE( exec->stack ); | |
exec->stackSize = 0; | |
/* free call stack */ | |
FT_FREE( exec->callStack ); | |
exec->callSize = 0; | |
exec->callTop = 0; | |
/* free glyph code range */ | |
FT_FREE( exec->glyphIns ); | |
exec->glyphSize = 0; | |
exec->size = NULL; | |
exec->face = NULL; | |
FT_FREE( exec ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Init_Context */ | |
/* */ | |
/* <Description> */ | |
/* Initializes a context object. */ | |
/* */ | |
/* <Input> */ | |
/* memory :: A handle to the parent memory object. */ | |
/* */ | |
/* <InOut> */ | |
/* exec :: A handle to the target execution context. */ | |
/* */ | |
/* <Return> */ | |
/* FreeType error code. 0 means success. */ | |
/* */ | |
static FT_Error | |
Init_Context( TT_ExecContext exec, | |
FT_Memory memory ) | |
{ | |
FT_Error error; | |
FT_TRACE1(( "Init_Context: new object at 0x%08p\n", exec )); | |
exec->memory = memory; | |
exec->callSize = 32; | |
if ( FT_NEW_ARRAY( exec->callStack, exec->callSize ) ) | |
goto Fail_Memory; | |
/* all values in the context are set to 0 already, but this is */ | |
/* here as a remainder */ | |
exec->maxPoints = 0; | |
exec->maxContours = 0; | |
exec->stackSize = 0; | |
exec->glyphSize = 0; | |
exec->stack = NULL; | |
exec->glyphIns = NULL; | |
exec->face = NULL; | |
exec->size = NULL; | |
return FT_Err_Ok; | |
Fail_Memory: | |
FT_ERROR(( "Init_Context: not enough memory for %p\n", exec )); | |
TT_Done_Context( exec ); | |
return error; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Update_Max */ | |
/* */ | |
/* <Description> */ | |
/* Checks the size of a buffer and reallocates it if necessary. */ | |
/* */ | |
/* <Input> */ | |
/* memory :: A handle to the parent memory object. */ | |
/* */ | |
/* multiplier :: The size in bytes of each element in the buffer. */ | |
/* */ | |
/* new_max :: The new capacity (size) of the buffer. */ | |
/* */ | |
/* <InOut> */ | |
/* size :: The address of the buffer's current size expressed */ | |
/* in elements. */ | |
/* */ | |
/* buff :: The address of the buffer base pointer. */ | |
/* */ | |
/* <Return> */ | |
/* FreeType error code. 0 means success. */ | |
/* */ | |
FT_LOCAL_DEF( FT_Error ) | |
Update_Max( FT_Memory memory, | |
FT_ULong* size, | |
FT_ULong multiplier, | |
void* _pbuff, | |
FT_ULong new_max ) | |
{ | |
FT_Error error; | |
void** pbuff = (void**)_pbuff; | |
if ( *size < new_max ) | |
{ | |
if ( FT_REALLOC( *pbuff, *size * multiplier, new_max * multiplier ) ) | |
return error; | |
*size = new_max; | |
} | |
return FT_Err_Ok; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* TT_Load_Context */ | |
/* */ | |
/* <Description> */ | |
/* Prepare an execution context for glyph hinting. */ | |
/* */ | |
/* <Input> */ | |
/* face :: A handle to the source face object. */ | |
/* */ | |
/* size :: A handle to the source size object. */ | |
/* */ | |
/* <InOut> */ | |
/* exec :: A handle to the target execution context. */ | |
/* */ | |
/* <Return> */ | |
/* FreeType error code. 0 means success. */ | |
/* */ | |
/* <Note> */ | |
/* Only the glyph loader and debugger should call this function. */ | |
/* */ | |
FT_LOCAL_DEF( FT_Error ) | |
TT_Load_Context( TT_ExecContext exec, | |
TT_Face face, | |
TT_Size size ) | |
{ | |
FT_Int i; | |
FT_ULong tmp; | |
TT_MaxProfile* maxp; | |
FT_Error error; | |
exec->face = face; | |
maxp = &face->max_profile; | |
exec->size = size; | |
if ( size ) | |
{ | |
exec->numFDefs = size->num_function_defs; | |
exec->maxFDefs = size->max_function_defs; | |
exec->numIDefs = size->num_instruction_defs; | |
exec->maxIDefs = size->max_instruction_defs; | |
exec->FDefs = size->function_defs; | |
exec->IDefs = size->instruction_defs; | |
exec->tt_metrics = size->ttmetrics; | |
exec->metrics = size->metrics; | |
exec->maxFunc = size->max_func; | |
exec->maxIns = size->max_ins; | |
for ( i = 0; i < TT_MAX_CODE_RANGES; i++ ) | |
exec->codeRangeTable[i] = size->codeRangeTable[i]; | |
/* set graphics state */ | |
exec->GS = size->GS; | |
exec->cvtSize = size->cvt_size; | |
exec->cvt = size->cvt; | |
exec->storeSize = size->storage_size; | |
exec->storage = size->storage; | |
exec->twilight = size->twilight; | |
/* In case of multi-threading it can happen that the old size object */ | |
/* no longer exists, thus we must clear all glyph zone references. */ | |
ft_memset( &exec->zp0, 0, sizeof ( exec->zp0 ) ); | |
exec->zp1 = exec->zp0; | |
exec->zp2 = exec->zp0; | |
} | |
/* XXX: We reserve a little more elements on the stack to deal safely */ | |
/* with broken fonts like arialbs, courbs, timesbs, etc. */ | |
tmp = (FT_ULong)exec->stackSize; | |
error = Update_Max( exec->memory, | |
&tmp, | |
sizeof ( FT_F26Dot6 ), | |
(void*)&exec->stack, | |
maxp->maxStackElements + 32 ); | |
exec->stackSize = (FT_Long)tmp; | |
if ( error ) | |
return error; | |
tmp = exec->glyphSize; | |
error = Update_Max( exec->memory, | |
&tmp, | |
sizeof ( FT_Byte ), | |
(void*)&exec->glyphIns, | |
maxp->maxSizeOfInstructions ); | |
exec->glyphSize = (FT_UShort)tmp; | |
if ( error ) | |
return error; | |
exec->pts.n_points = 0; | |
exec->pts.n_contours = 0; | |
exec->zp1 = exec->pts; | |
exec->zp2 = exec->pts; | |
exec->zp0 = exec->pts; | |
exec->instruction_trap = FALSE; | |
return FT_Err_Ok; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* TT_Save_Context */ | |
/* */ | |
/* <Description> */ | |
/* Saves the code ranges in a `size' object. */ | |
/* */ | |
/* <Input> */ | |
/* exec :: A handle to the source execution context. */ | |
/* */ | |
/* <InOut> */ | |
/* size :: A handle to the target size object. */ | |
/* */ | |
/* <Note> */ | |
/* Only the glyph loader and debugger should call this function. */ | |
/* */ | |
FT_LOCAL_DEF( void ) | |
TT_Save_Context( TT_ExecContext exec, | |
TT_Size size ) | |
{ | |
FT_Int i; | |
/* XXX: Will probably disappear soon with all the code range */ | |
/* management, which is now rather obsolete. */ | |
/* */ | |
size->num_function_defs = exec->numFDefs; | |
size->num_instruction_defs = exec->numIDefs; | |
size->max_func = exec->maxFunc; | |
size->max_ins = exec->maxIns; | |
for ( i = 0; i < TT_MAX_CODE_RANGES; i++ ) | |
size->codeRangeTable[i] = exec->codeRangeTable[i]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* TT_Run_Context */ | |
/* */ | |
/* <Description> */ | |
/* Executes one or more instructions in the execution context. */ | |
/* */ | |
/* <Input> */ | |
/* debug :: A Boolean flag. If set, the function sets some internal */ | |
/* variables and returns immediately, otherwise TT_RunIns() */ | |
/* is called. */ | |
/* */ | |
/* This is commented out currently. */ | |
/* */ | |
/* <Input> */ | |
/* exec :: A handle to the target execution context. */ | |
/* */ | |
/* <Return> */ | |
/* TrueType error code. 0 means success. */ | |
/* */ | |
FT_LOCAL_DEF( FT_Error ) | |
TT_Run_Context( TT_ExecContext exec ) | |
{ | |
TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 ); | |
exec->zp0 = exec->pts; | |
exec->zp1 = exec->pts; | |
exec->zp2 = exec->pts; | |
exec->GS.gep0 = 1; | |
exec->GS.gep1 = 1; | |
exec->GS.gep2 = 1; | |
exec->GS.projVector.x = 0x4000; | |
exec->GS.projVector.y = 0x0000; | |
exec->GS.freeVector = exec->GS.projVector; | |
exec->GS.dualVector = exec->GS.projVector; | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
exec->GS.both_x_axis = TRUE; | |
#endif | |
exec->GS.round_state = 1; | |
exec->GS.loop = 1; | |
/* some glyphs leave something on the stack. so we clean it */ | |
/* before a new execution. */ | |
exec->top = 0; | |
exec->callTop = 0; | |
return exec->face->interpreter( exec ); | |
} | |
/* The default value for `scan_control' is documented as FALSE in the */ | |
/* TrueType specification. This is confusing since it implies a */ | |
/* Boolean value. However, this is not the case, thus both the */ | |
/* default values of our `scan_type' and `scan_control' fields (which */ | |
/* the documentation's `scan_control' variable is split into) are */ | |
/* zero. */ | |
const TT_GraphicsState tt_default_graphics_state = | |
{ | |
0, 0, 0, | |
{ 0x4000, 0 }, | |
{ 0x4000, 0 }, | |
{ 0x4000, 0 }, | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
TRUE, | |
#endif | |
1, 64, 1, | |
TRUE, 68, 0, 0, 9, 3, | |
0, FALSE, 0, 1, 1, 1 | |
}; | |
/* documentation is in ttinterp.h */ | |
FT_EXPORT_DEF( TT_ExecContext ) | |
TT_New_Context( TT_Driver driver ) | |
{ | |
FT_Memory memory; | |
FT_Error error; | |
TT_ExecContext exec = NULL; | |
if ( !driver ) | |
goto Fail; | |
memory = driver->root.root.memory; | |
/* allocate object */ | |
if ( FT_NEW( exec ) ) | |
goto Fail; | |
/* initialize it; in case of error this deallocates `exec' too */ | |
error = Init_Context( exec, memory ); | |
if ( error ) | |
goto Fail; | |
return exec; | |
Fail: | |
return NULL; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* Before an opcode is executed, the interpreter verifies that there are */ | |
/* enough arguments on the stack, with the help of the `Pop_Push_Count' */ | |
/* table. */ | |
/* */ | |
/* For each opcode, the first column gives the number of arguments that */ | |
/* are popped from the stack; the second one gives the number of those */ | |
/* that are pushed in result. */ | |
/* */ | |
/* Opcodes which have a varying number of parameters in the data stream */ | |
/* (NPUSHB, NPUSHW) are handled specially; they have a negative value in */ | |
/* the `opcode_length' table, and the value in `Pop_Push_Count' is set */ | |
/* to zero. */ | |
/* */ | |
/*************************************************************************/ | |
#undef PACK | |
#define PACK( x, y ) ( ( x << 4 ) | y ) | |
static | |
const FT_Byte Pop_Push_Count[256] = | |
{ | |
/* opcodes are gathered in groups of 16 */ | |
/* please keep the spaces as they are */ | |
/* SVTCA y */ PACK( 0, 0 ), | |
/* SVTCA x */ PACK( 0, 0 ), | |
/* SPvTCA y */ PACK( 0, 0 ), | |
/* SPvTCA x */ PACK( 0, 0 ), | |
/* SFvTCA y */ PACK( 0, 0 ), | |
/* SFvTCA x */ PACK( 0, 0 ), | |
/* SPvTL // */ PACK( 2, 0 ), | |
/* SPvTL + */ PACK( 2, 0 ), | |
/* SFvTL // */ PACK( 2, 0 ), | |
/* SFvTL + */ PACK( 2, 0 ), | |
/* SPvFS */ PACK( 2, 0 ), | |
/* SFvFS */ PACK( 2, 0 ), | |
/* GPv */ PACK( 0, 2 ), | |
/* GFv */ PACK( 0, 2 ), | |
/* SFvTPv */ PACK( 0, 0 ), | |
/* ISECT */ PACK( 5, 0 ), | |
/* SRP0 */ PACK( 1, 0 ), | |
/* SRP1 */ PACK( 1, 0 ), | |
/* SRP2 */ PACK( 1, 0 ), | |
/* SZP0 */ PACK( 1, 0 ), | |
/* SZP1 */ PACK( 1, 0 ), | |
/* SZP2 */ PACK( 1, 0 ), | |
/* SZPS */ PACK( 1, 0 ), | |
/* SLOOP */ PACK( 1, 0 ), | |
/* RTG */ PACK( 0, 0 ), | |
/* RTHG */ PACK( 0, 0 ), | |
/* SMD */ PACK( 1, 0 ), | |
/* ELSE */ PACK( 0, 0 ), | |
/* JMPR */ PACK( 1, 0 ), | |
/* SCvTCi */ PACK( 1, 0 ), | |
/* SSwCi */ PACK( 1, 0 ), | |
/* SSW */ PACK( 1, 0 ), | |
/* DUP */ PACK( 1, 2 ), | |
/* POP */ PACK( 1, 0 ), | |
/* CLEAR */ PACK( 0, 0 ), | |
/* SWAP */ PACK( 2, 2 ), | |
/* DEPTH */ PACK( 0, 1 ), | |
/* CINDEX */ PACK( 1, 1 ), | |
/* MINDEX */ PACK( 1, 0 ), | |
/* AlignPTS */ PACK( 2, 0 ), | |
/* INS_$28 */ PACK( 0, 0 ), | |
/* UTP */ PACK( 1, 0 ), | |
/* LOOPCALL */ PACK( 2, 0 ), | |
/* CALL */ PACK( 1, 0 ), | |
/* FDEF */ PACK( 1, 0 ), | |
/* ENDF */ PACK( 0, 0 ), | |
/* MDAP[0] */ PACK( 1, 0 ), | |
/* MDAP[1] */ PACK( 1, 0 ), | |
/* IUP[0] */ PACK( 0, 0 ), | |
/* IUP[1] */ PACK( 0, 0 ), | |
/* SHP[0] */ PACK( 0, 0 ), | |
/* SHP[1] */ PACK( 0, 0 ), | |
/* SHC[0] */ PACK( 1, 0 ), | |
/* SHC[1] */ PACK( 1, 0 ), | |
/* SHZ[0] */ PACK( 1, 0 ), | |
/* SHZ[1] */ PACK( 1, 0 ), | |
/* SHPIX */ PACK( 1, 0 ), | |
/* IP */ PACK( 0, 0 ), | |
/* MSIRP[0] */ PACK( 2, 0 ), | |
/* MSIRP[1] */ PACK( 2, 0 ), | |
/* AlignRP */ PACK( 0, 0 ), | |
/* RTDG */ PACK( 0, 0 ), | |
/* MIAP[0] */ PACK( 2, 0 ), | |
/* MIAP[1] */ PACK( 2, 0 ), | |
/* NPushB */ PACK( 0, 0 ), | |
/* NPushW */ PACK( 0, 0 ), | |
/* WS */ PACK( 2, 0 ), | |
/* RS */ PACK( 1, 1 ), | |
/* WCvtP */ PACK( 2, 0 ), | |
/* RCvt */ PACK( 1, 1 ), | |
/* GC[0] */ PACK( 1, 1 ), | |
/* GC[1] */ PACK( 1, 1 ), | |
/* SCFS */ PACK( 2, 0 ), | |
/* MD[0] */ PACK( 2, 1 ), | |
/* MD[1] */ PACK( 2, 1 ), | |
/* MPPEM */ PACK( 0, 1 ), | |
/* MPS */ PACK( 0, 1 ), | |
/* FlipON */ PACK( 0, 0 ), | |
/* FlipOFF */ PACK( 0, 0 ), | |
/* DEBUG */ PACK( 1, 0 ), | |
/* LT */ PACK( 2, 1 ), | |
/* LTEQ */ PACK( 2, 1 ), | |
/* GT */ PACK( 2, 1 ), | |
/* GTEQ */ PACK( 2, 1 ), | |
/* EQ */ PACK( 2, 1 ), | |
/* NEQ */ PACK( 2, 1 ), | |
/* ODD */ PACK( 1, 1 ), | |
/* EVEN */ PACK( 1, 1 ), | |
/* IF */ PACK( 1, 0 ), | |
/* EIF */ PACK( 0, 0 ), | |
/* AND */ PACK( 2, 1 ), | |
/* OR */ PACK( 2, 1 ), | |
/* NOT */ PACK( 1, 1 ), | |
/* DeltaP1 */ PACK( 1, 0 ), | |
/* SDB */ PACK( 1, 0 ), | |
/* SDS */ PACK( 1, 0 ), | |
/* ADD */ PACK( 2, 1 ), | |
/* SUB */ PACK( 2, 1 ), | |
/* DIV */ PACK( 2, 1 ), | |
/* MUL */ PACK( 2, 1 ), | |
/* ABS */ PACK( 1, 1 ), | |
/* NEG */ PACK( 1, 1 ), | |
/* FLOOR */ PACK( 1, 1 ), | |
/* CEILING */ PACK( 1, 1 ), | |
/* ROUND[0] */ PACK( 1, 1 ), | |
/* ROUND[1] */ PACK( 1, 1 ), | |
/* ROUND[2] */ PACK( 1, 1 ), | |
/* ROUND[3] */ PACK( 1, 1 ), | |
/* NROUND[0] */ PACK( 1, 1 ), | |
/* NROUND[1] */ PACK( 1, 1 ), | |
/* NROUND[2] */ PACK( 1, 1 ), | |
/* NROUND[3] */ PACK( 1, 1 ), | |
/* WCvtF */ PACK( 2, 0 ), | |
/* DeltaP2 */ PACK( 1, 0 ), | |
/* DeltaP3 */ PACK( 1, 0 ), | |
/* DeltaCn[0] */ PACK( 1, 0 ), | |
/* DeltaCn[1] */ PACK( 1, 0 ), | |
/* DeltaCn[2] */ PACK( 1, 0 ), | |
/* SROUND */ PACK( 1, 0 ), | |
/* S45Round */ PACK( 1, 0 ), | |
/* JROT */ PACK( 2, 0 ), | |
/* JROF */ PACK( 2, 0 ), | |
/* ROFF */ PACK( 0, 0 ), | |
/* INS_$7B */ PACK( 0, 0 ), | |
/* RUTG */ PACK( 0, 0 ), | |
/* RDTG */ PACK( 0, 0 ), | |
/* SANGW */ PACK( 1, 0 ), | |
/* AA */ PACK( 1, 0 ), | |
/* FlipPT */ PACK( 0, 0 ), | |
/* FlipRgON */ PACK( 2, 0 ), | |
/* FlipRgOFF */ PACK( 2, 0 ), | |
/* INS_$83 */ PACK( 0, 0 ), | |
/* INS_$84 */ PACK( 0, 0 ), | |
/* ScanCTRL */ PACK( 1, 0 ), | |
/* SDPvTL[0] */ PACK( 2, 0 ), | |
/* SDPvTL[1] */ PACK( 2, 0 ), | |
/* GetINFO */ PACK( 1, 1 ), | |
/* IDEF */ PACK( 1, 0 ), | |
/* ROLL */ PACK( 3, 3 ), | |
/* MAX */ PACK( 2, 1 ), | |
/* MIN */ PACK( 2, 1 ), | |
/* ScanTYPE */ PACK( 1, 0 ), | |
/* InstCTRL */ PACK( 2, 0 ), | |
/* INS_$8F */ PACK( 0, 0 ), | |
/* INS_$90 */ PACK( 0, 0 ), | |
/* INS_$91 */ PACK( 0, 0 ), | |
/* INS_$92 */ PACK( 0, 0 ), | |
/* INS_$93 */ PACK( 0, 0 ), | |
/* INS_$94 */ PACK( 0, 0 ), | |
/* INS_$95 */ PACK( 0, 0 ), | |
/* INS_$96 */ PACK( 0, 0 ), | |
/* INS_$97 */ PACK( 0, 0 ), | |
/* INS_$98 */ PACK( 0, 0 ), | |
/* INS_$99 */ PACK( 0, 0 ), | |
/* INS_$9A */ PACK( 0, 0 ), | |
/* INS_$9B */ PACK( 0, 0 ), | |
/* INS_$9C */ PACK( 0, 0 ), | |
/* INS_$9D */ PACK( 0, 0 ), | |
/* INS_$9E */ PACK( 0, 0 ), | |
/* INS_$9F */ PACK( 0, 0 ), | |
/* INS_$A0 */ PACK( 0, 0 ), | |
/* INS_$A1 */ PACK( 0, 0 ), | |
/* INS_$A2 */ PACK( 0, 0 ), | |
/* INS_$A3 */ PACK( 0, 0 ), | |
/* INS_$A4 */ PACK( 0, 0 ), | |
/* INS_$A5 */ PACK( 0, 0 ), | |
/* INS_$A6 */ PACK( 0, 0 ), | |
/* INS_$A7 */ PACK( 0, 0 ), | |
/* INS_$A8 */ PACK( 0, 0 ), | |
/* INS_$A9 */ PACK( 0, 0 ), | |
/* INS_$AA */ PACK( 0, 0 ), | |
/* INS_$AB */ PACK( 0, 0 ), | |
/* INS_$AC */ PACK( 0, 0 ), | |
/* INS_$AD */ PACK( 0, 0 ), | |
/* INS_$AE */ PACK( 0, 0 ), | |
/* INS_$AF */ PACK( 0, 0 ), | |
/* PushB[0] */ PACK( 0, 1 ), | |
/* PushB[1] */ PACK( 0, 2 ), | |
/* PushB[2] */ PACK( 0, 3 ), | |
/* PushB[3] */ PACK( 0, 4 ), | |
/* PushB[4] */ PACK( 0, 5 ), | |
/* PushB[5] */ PACK( 0, 6 ), | |
/* PushB[6] */ PACK( 0, 7 ), | |
/* PushB[7] */ PACK( 0, 8 ), | |
/* PushW[0] */ PACK( 0, 1 ), | |
/* PushW[1] */ PACK( 0, 2 ), | |
/* PushW[2] */ PACK( 0, 3 ), | |
/* PushW[3] */ PACK( 0, 4 ), | |
/* PushW[4] */ PACK( 0, 5 ), | |
/* PushW[5] */ PACK( 0, 6 ), | |
/* PushW[6] */ PACK( 0, 7 ), | |
/* PushW[7] */ PACK( 0, 8 ), | |
/* MDRP[00] */ PACK( 1, 0 ), | |
/* MDRP[01] */ PACK( 1, 0 ), | |
/* MDRP[02] */ PACK( 1, 0 ), | |
/* MDRP[03] */ PACK( 1, 0 ), | |
/* MDRP[04] */ PACK( 1, 0 ), | |
/* MDRP[05] */ PACK( 1, 0 ), | |
/* MDRP[06] */ PACK( 1, 0 ), | |
/* MDRP[07] */ PACK( 1, 0 ), | |
/* MDRP[08] */ PACK( 1, 0 ), | |
/* MDRP[09] */ PACK( 1, 0 ), | |
/* MDRP[10] */ PACK( 1, 0 ), | |
/* MDRP[11] */ PACK( 1, 0 ), | |
/* MDRP[12] */ PACK( 1, 0 ), | |
/* MDRP[13] */ PACK( 1, 0 ), | |
/* MDRP[14] */ PACK( 1, 0 ), | |
/* MDRP[15] */ PACK( 1, 0 ), | |
/* MDRP[16] */ PACK( 1, 0 ), | |
/* MDRP[17] */ PACK( 1, 0 ), | |
/* MDRP[18] */ PACK( 1, 0 ), | |
/* MDRP[19] */ PACK( 1, 0 ), | |
/* MDRP[20] */ PACK( 1, 0 ), | |
/* MDRP[21] */ PACK( 1, 0 ), | |
/* MDRP[22] */ PACK( 1, 0 ), | |
/* MDRP[23] */ PACK( 1, 0 ), | |
/* MDRP[24] */ PACK( 1, 0 ), | |
/* MDRP[25] */ PACK( 1, 0 ), | |
/* MDRP[26] */ PACK( 1, 0 ), | |
/* MDRP[27] */ PACK( 1, 0 ), | |
/* MDRP[28] */ PACK( 1, 0 ), | |
/* MDRP[29] */ PACK( 1, 0 ), | |
/* MDRP[30] */ PACK( 1, 0 ), | |
/* MDRP[31] */ PACK( 1, 0 ), | |
/* MIRP[00] */ PACK( 2, 0 ), | |
/* MIRP[01] */ PACK( 2, 0 ), | |
/* MIRP[02] */ PACK( 2, 0 ), | |
/* MIRP[03] */ PACK( 2, 0 ), | |
/* MIRP[04] */ PACK( 2, 0 ), | |
/* MIRP[05] */ PACK( 2, 0 ), | |
/* MIRP[06] */ PACK( 2, 0 ), | |
/* MIRP[07] */ PACK( 2, 0 ), | |
/* MIRP[08] */ PACK( 2, 0 ), | |
/* MIRP[09] */ PACK( 2, 0 ), | |
/* MIRP[10] */ PACK( 2, 0 ), | |
/* MIRP[11] */ PACK( 2, 0 ), | |
/* MIRP[12] */ PACK( 2, 0 ), | |
/* MIRP[13] */ PACK( 2, 0 ), | |
/* MIRP[14] */ PACK( 2, 0 ), | |
/* MIRP[15] */ PACK( 2, 0 ), | |
/* MIRP[16] */ PACK( 2, 0 ), | |
/* MIRP[17] */ PACK( 2, 0 ), | |
/* MIRP[18] */ PACK( 2, 0 ), | |
/* MIRP[19] */ PACK( 2, 0 ), | |
/* MIRP[20] */ PACK( 2, 0 ), | |
/* MIRP[21] */ PACK( 2, 0 ), | |
/* MIRP[22] */ PACK( 2, 0 ), | |
/* MIRP[23] */ PACK( 2, 0 ), | |
/* MIRP[24] */ PACK( 2, 0 ), | |
/* MIRP[25] */ PACK( 2, 0 ), | |
/* MIRP[26] */ PACK( 2, 0 ), | |
/* MIRP[27] */ PACK( 2, 0 ), | |
/* MIRP[28] */ PACK( 2, 0 ), | |
/* MIRP[29] */ PACK( 2, 0 ), | |
/* MIRP[30] */ PACK( 2, 0 ), | |
/* MIRP[31] */ PACK( 2, 0 ) | |
}; | |
#ifdef FT_DEBUG_LEVEL_TRACE | |
/* the first hex digit gives the length of the opcode name; the space */ | |
/* after the digit is here just to increase readability of the source */ | |
/* code */ | |
static | |
const char* const opcode_name[256] = | |
{ | |
"7 SVTCA y", | |
"7 SVTCA x", | |
"8 SPvTCA y", | |
"8 SPvTCA x", | |
"8 SFvTCA y", | |
"8 SFvTCA x", | |
"8 SPvTL ||", | |
"7 SPvTL +", | |
"8 SFvTL ||", | |
"7 SFvTL +", | |
"5 SPvFS", | |
"5 SFvFS", | |
"3 GPv", | |
"3 GFv", | |
"6 SFvTPv", | |
"5 ISECT", | |
"4 SRP0", | |
"4 SRP1", | |
"4 SRP2", | |
"4 SZP0", | |
"4 SZP1", | |
"4 SZP2", | |
"4 SZPS", | |
"5 SLOOP", | |
"3 RTG", | |
"4 RTHG", | |
"3 SMD", | |
"4 ELSE", | |
"4 JMPR", | |
"6 SCvTCi", | |
"5 SSwCi", | |
"3 SSW", | |
"3 DUP", | |
"3 POP", | |
"5 CLEAR", | |
"4 SWAP", | |
"5 DEPTH", | |
"6 CINDEX", | |
"6 MINDEX", | |
"8 AlignPTS", | |
"7 INS_$28", | |
"3 UTP", | |
"8 LOOPCALL", | |
"4 CALL", | |
"4 FDEF", | |
"4 ENDF", | |
"7 MDAP[0]", | |
"7 MDAP[1]", | |
"6 IUP[0]", | |
"6 IUP[1]", | |
"6 SHP[0]", | |
"6 SHP[1]", | |
"6 SHC[0]", | |
"6 SHC[1]", | |
"6 SHZ[0]", | |
"6 SHZ[1]", | |
"5 SHPIX", | |
"2 IP", | |
"8 MSIRP[0]", | |
"8 MSIRP[1]", | |
"7 AlignRP", | |
"4 RTDG", | |
"7 MIAP[0]", | |
"7 MIAP[1]", | |
"6 NPushB", | |
"6 NPushW", | |
"2 WS", | |
"2 RS", | |
"5 WCvtP", | |
"4 RCvt", | |
"5 GC[0]", | |
"5 GC[1]", | |
"4 SCFS", | |
"5 MD[0]", | |
"5 MD[1]", | |
"5 MPPEM", | |
"3 MPS", | |
"6 FlipON", | |
"7 FlipOFF", | |
"5 DEBUG", | |
"2 LT", | |
"4 LTEQ", | |
"2 GT", | |
"4 GTEQ", | |
"2 EQ", | |
"3 NEQ", | |
"3 ODD", | |
"4 EVEN", | |
"2 IF", | |
"3 EIF", | |
"3 AND", | |
"2 OR", | |
"3 NOT", | |
"7 DeltaP1", | |
"3 SDB", | |
"3 SDS", | |
"3 ADD", | |
"3 SUB", | |
"3 DIV", | |
"3 MUL", | |
"3 ABS", | |
"3 NEG", | |
"5 FLOOR", | |
"7 CEILING", | |
"8 ROUND[0]", | |
"8 ROUND[1]", | |
"8 ROUND[2]", | |
"8 ROUND[3]", | |
"9 NROUND[0]", | |
"9 NROUND[1]", | |
"9 NROUND[2]", | |
"9 NROUND[3]", | |
"5 WCvtF", | |
"7 DeltaP2", | |
"7 DeltaP3", | |
"A DeltaCn[0]", | |
"A DeltaCn[1]", | |
"A DeltaCn[2]", | |
"6 SROUND", | |
"8 S45Round", | |
"4 JROT", | |
"4 JROF", | |
"4 ROFF", | |
"7 INS_$7B", | |
"4 RUTG", | |
"4 RDTG", | |
"5 SANGW", | |
"2 AA", | |
"6 FlipPT", | |
"8 FlipRgON", | |
"9 FlipRgOFF", | |
"7 INS_$83", | |
"7 INS_$84", | |
"8 ScanCTRL", | |
"9 SDPvTL[0]", | |
"9 SDPvTL[1]", | |
"7 GetINFO", | |
"4 IDEF", | |
"4 ROLL", | |
"3 MAX", | |
"3 MIN", | |
"8 ScanTYPE", | |
"8 InstCTRL", | |
"7 INS_$8F", | |
"7 INS_$90", | |
"7 INS_$91", | |
"7 INS_$92", | |
"7 INS_$93", | |
"7 INS_$94", | |
"7 INS_$95", | |
"7 INS_$96", | |
"7 INS_$97", | |
"7 INS_$98", | |
"7 INS_$99", | |
"7 INS_$9A", | |
"7 INS_$9B", | |
"7 INS_$9C", | |
"7 INS_$9D", | |
"7 INS_$9E", | |
"7 INS_$9F", | |
"7 INS_$A0", | |
"7 INS_$A1", | |
"7 INS_$A2", | |
"7 INS_$A3", | |
"7 INS_$A4", | |
"7 INS_$A5", | |
"7 INS_$A6", | |
"7 INS_$A7", | |
"7 INS_$A8", | |
"7 INS_$A9", | |
"7 INS_$AA", | |
"7 INS_$AB", | |
"7 INS_$AC", | |
"7 INS_$AD", | |
"7 INS_$AE", | |
"7 INS_$AF", | |
"8 PushB[0]", | |
"8 PushB[1]", | |
"8 PushB[2]", | |
"8 PushB[3]", | |
"8 PushB[4]", | |
"8 PushB[5]", | |
"8 PushB[6]", | |
"8 PushB[7]", | |
"8 PushW[0]", | |
"8 PushW[1]", | |
"8 PushW[2]", | |
"8 PushW[3]", | |
"8 PushW[4]", | |
"8 PushW[5]", | |
"8 PushW[6]", | |
"8 PushW[7]", | |
"8 MDRP[00]", | |
"8 MDRP[01]", | |
"8 MDRP[02]", | |
"8 MDRP[03]", | |
"8 MDRP[04]", | |
"8 MDRP[05]", | |
"8 MDRP[06]", | |
"8 MDRP[07]", | |
"8 MDRP[08]", | |
"8 MDRP[09]", | |
"8 MDRP[10]", | |
"8 MDRP[11]", | |
"8 MDRP[12]", | |
"8 MDRP[13]", | |
"8 MDRP[14]", | |
"8 MDRP[15]", | |
"8 MDRP[16]", | |
"8 MDRP[17]", | |
"8 MDRP[18]", | |
"8 MDRP[19]", | |
"8 MDRP[20]", | |
"8 MDRP[21]", | |
"8 MDRP[22]", | |
"8 MDRP[23]", | |
"8 MDRP[24]", | |
"8 MDRP[25]", | |
"8 MDRP[26]", | |
"8 MDRP[27]", | |
"8 MDRP[28]", | |
"8 MDRP[29]", | |
"8 MDRP[30]", | |
"8 MDRP[31]", | |
"8 MIRP[00]", | |
"8 MIRP[01]", | |
"8 MIRP[02]", | |
"8 MIRP[03]", | |
"8 MIRP[04]", | |
"8 MIRP[05]", | |
"8 MIRP[06]", | |
"8 MIRP[07]", | |
"8 MIRP[08]", | |
"8 MIRP[09]", | |
"8 MIRP[10]", | |
"8 MIRP[11]", | |
"8 MIRP[12]", | |
"8 MIRP[13]", | |
"8 MIRP[14]", | |
"8 MIRP[15]", | |
"8 MIRP[16]", | |
"8 MIRP[17]", | |
"8 MIRP[18]", | |
"8 MIRP[19]", | |
"8 MIRP[20]", | |
"8 MIRP[21]", | |
"8 MIRP[22]", | |
"8 MIRP[23]", | |
"8 MIRP[24]", | |
"8 MIRP[25]", | |
"8 MIRP[26]", | |
"8 MIRP[27]", | |
"8 MIRP[28]", | |
"8 MIRP[29]", | |
"8 MIRP[30]", | |
"8 MIRP[31]" | |
}; | |
#endif /* FT_DEBUG_LEVEL_TRACE */ | |
static | |
const FT_Char opcode_length[256] = | |
{ | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
-1,-2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
2, 3, 4, 5, 6, 7, 8, 9, 3, 5, 7, 9, 11,13,15,17, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 | |
}; | |
#undef PACK | |
#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER | |
#if defined( __arm__ ) && \ | |
( defined( __thumb2__ ) || !defined( __thumb__ ) ) | |
#define TT_MulFix14 TT_MulFix14_arm | |
static FT_Int32 | |
TT_MulFix14_arm( FT_Int32 a, | |
FT_Int b ) | |
{ | |
FT_Int32 t, t2; | |
#if defined( __CC_ARM ) || defined( __ARMCC__ ) | |
__asm | |
{ | |
smull t2, t, b, a /* (lo=t2,hi=t) = a*b */ | |
mov a, t, asr #31 /* a = (hi >> 31) */ | |
add a, a, #0x2000 /* a += 0x2000 */ | |
adds t2, t2, a /* t2 += a */ | |
adc t, t, #0 /* t += carry */ | |
mov a, t2, lsr #14 /* a = t2 >> 14 */ | |
orr a, a, t, lsl #18 /* a |= t << 18 */ | |
} | |
#elif defined( __GNUC__ ) | |
__asm__ __volatile__ ( | |
"smull %1, %2, %4, %3\n\t" /* (lo=%1,hi=%2) = a*b */ | |
"mov %0, %2, asr #31\n\t" /* %0 = (hi >> 31) */ | |
#if defined( __clang__ ) && defined( __thumb2__ ) | |
"add.w %0, %0, #0x2000\n\t" /* %0 += 0x2000 */ | |
#else | |
"add %0, %0, #0x2000\n\t" /* %0 += 0x2000 */ | |
#endif | |
"adds %1, %1, %0\n\t" /* %1 += %0 */ | |
"adc %2, %2, #0\n\t" /* %2 += carry */ | |
"mov %0, %1, lsr #14\n\t" /* %0 = %1 >> 16 */ | |
"orr %0, %0, %2, lsl #18\n\t" /* %0 |= %2 << 16 */ | |
: "=r"(a), "=&r"(t2), "=&r"(t) | |
: "r"(a), "r"(b) | |
: "cc" ); | |
#endif | |
return a; | |
} | |
#endif /* __arm__ && ( __thumb2__ || !__thumb__ ) */ | |
#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */ | |
#if defined( __GNUC__ ) && \ | |
( defined( __i386__ ) || defined( __x86_64__ ) ) | |
#define TT_MulFix14 TT_MulFix14_long_long | |
/* Temporarily disable the warning that C90 doesn't support `long long'. */ | |
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 | |
#pragma GCC diagnostic push | |
#endif | |
#pragma GCC diagnostic ignored "-Wlong-long" | |
/* This is declared `noinline' because inlining the function results */ | |
/* in slower code. The `pure' attribute indicates that the result */ | |
/* only depends on the parameters. */ | |
static __attribute__(( noinline )) | |
__attribute__(( pure )) FT_Int32 | |
TT_MulFix14_long_long( FT_Int32 a, | |
FT_Int b ) | |
{ | |
long long ret = (long long)a * b; | |
/* The following line assumes that right shifting of signed values */ | |
/* will actually preserve the sign bit. The exact behaviour is */ | |
/* undefined, but this is true on x86 and x86_64. */ | |
long long tmp = ret >> 63; | |
ret += 0x2000 + tmp; | |
return (FT_Int32)( ret >> 14 ); | |
} | |
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 | |
#pragma GCC diagnostic pop | |
#endif | |
#endif /* __GNUC__ && ( __i386__ || __x86_64__ ) */ | |
#ifndef TT_MulFix14 | |
/* Compute (a*b)/2^14 with maximum accuracy and rounding. */ | |
/* This is optimized to be faster than calling FT_MulFix() */ | |
/* for platforms where sizeof(int) == 2. */ | |
static FT_Int32 | |
TT_MulFix14( FT_Int32 a, | |
FT_Int b ) | |
{ | |
FT_Int32 sign; | |
FT_UInt32 ah, al, mid, lo, hi; | |
sign = a ^ b; | |
if ( a < 0 ) | |
a = -a; | |
if ( b < 0 ) | |
b = -b; | |
ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU ); | |
al = (FT_UInt32)( a & 0xFFFFU ); | |
lo = al * b; | |
mid = ah * b; | |
hi = mid >> 16; | |
mid = ( mid << 16 ) + ( 1 << 13 ); /* rounding */ | |
lo += mid; | |
if ( lo < mid ) | |
hi += 1; | |
mid = ( lo >> 14 ) | ( hi << 18 ); | |
return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid; | |
} | |
#endif /* !TT_MulFix14 */ | |
#if defined( __GNUC__ ) && \ | |
( defined( __i386__ ) || \ | |
defined( __x86_64__ ) || \ | |
defined( __arm__ ) ) | |
#define TT_DotFix14 TT_DotFix14_long_long | |
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 | |
#pragma GCC diagnostic push | |
#endif | |
#pragma GCC diagnostic ignored "-Wlong-long" | |
static __attribute__(( pure )) FT_Int32 | |
TT_DotFix14_long_long( FT_Int32 ax, | |
FT_Int32 ay, | |
FT_Int bx, | |
FT_Int by ) | |
{ | |
/* Temporarily disable the warning that C90 doesn't support */ | |
/* `long long'. */ | |
long long temp1 = (long long)ax * bx; | |
long long temp2 = (long long)ay * by; | |
temp1 += temp2; | |
temp2 = temp1 >> 63; | |
temp1 += 0x2000 + temp2; | |
return (FT_Int32)( temp1 >> 14 ); | |
} | |
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406 | |
#pragma GCC diagnostic pop | |
#endif | |
#endif /* __GNUC__ && (__arm__ || __i386__ || __x86_64__) */ | |
#ifndef TT_DotFix14 | |
/* compute (ax*bx+ay*by)/2^14 with maximum accuracy and rounding */ | |
static FT_Int32 | |
TT_DotFix14( FT_Int32 ax, | |
FT_Int32 ay, | |
FT_Int bx, | |
FT_Int by ) | |
{ | |
FT_Int32 m, s, hi1, hi2, hi; | |
FT_UInt32 l, lo1, lo2, lo; | |
/* compute ax*bx as 64-bit value */ | |
l = (FT_UInt32)( ( ax & 0xFFFFU ) * bx ); | |
m = ( ax >> 16 ) * bx; | |
lo1 = l + ( (FT_UInt32)m << 16 ); | |
hi1 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo1 < l ); | |
/* compute ay*by as 64-bit value */ | |
l = (FT_UInt32)( ( ay & 0xFFFFU ) * by ); | |
m = ( ay >> 16 ) * by; | |
lo2 = l + ( (FT_UInt32)m << 16 ); | |
hi2 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo2 < l ); | |
/* add them */ | |
lo = lo1 + lo2; | |
hi = hi1 + hi2 + ( lo < lo1 ); | |
/* divide the result by 2^14 with rounding */ | |
s = hi >> 31; | |
l = lo + (FT_UInt32)s; | |
hi += s + ( l < lo ); | |
lo = l; | |
l = lo + 0x2000U; | |
hi += ( l < lo ); | |
return (FT_Int32)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) ); | |
} | |
#endif /* TT_DotFix14 */ | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Current_Ratio */ | |
/* */ | |
/* <Description> */ | |
/* Returns the current aspect ratio scaling factor depending on the */ | |
/* projection vector's state and device resolutions. */ | |
/* */ | |
/* <Return> */ | |
/* The aspect ratio in 16.16 format, always <= 1.0 . */ | |
/* */ | |
static FT_Long | |
Current_Ratio( TT_ExecContext exc ) | |
{ | |
if ( !exc->tt_metrics.ratio ) | |
{ | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
if ( exc->face->unpatented_hinting ) | |
{ | |
if ( exc->GS.both_x_axis ) | |
exc->tt_metrics.ratio = exc->tt_metrics.x_ratio; | |
else | |
exc->tt_metrics.ratio = exc->tt_metrics.y_ratio; | |
} | |
else | |
#endif | |
{ | |
if ( exc->GS.projVector.y == 0 ) | |
exc->tt_metrics.ratio = exc->tt_metrics.x_ratio; | |
else if ( exc->GS.projVector.x == 0 ) | |
exc->tt_metrics.ratio = exc->tt_metrics.y_ratio; | |
else | |
{ | |
FT_F26Dot6 x, y; | |
x = TT_MulFix14( exc->tt_metrics.x_ratio, | |
exc->GS.projVector.x ); | |
y = TT_MulFix14( exc->tt_metrics.y_ratio, | |
exc->GS.projVector.y ); | |
exc->tt_metrics.ratio = FT_Hypot( x, y ); | |
} | |
} | |
} | |
return exc->tt_metrics.ratio; | |
} | |
FT_CALLBACK_DEF( FT_Long ) | |
Current_Ppem( TT_ExecContext exc ) | |
{ | |
return exc->tt_metrics.ppem; | |
} | |
FT_CALLBACK_DEF( FT_Long ) | |
Current_Ppem_Stretched( TT_ExecContext exc ) | |
{ | |
return FT_MulFix( exc->tt_metrics.ppem, Current_Ratio( exc ) ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* Functions related to the control value table (CVT). */ | |
/* */ | |
/*************************************************************************/ | |
FT_CALLBACK_DEF( FT_F26Dot6 ) | |
Read_CVT( TT_ExecContext exc, | |
FT_ULong idx ) | |
{ | |
return exc->cvt[idx]; | |
} | |
FT_CALLBACK_DEF( FT_F26Dot6 ) | |
Read_CVT_Stretched( TT_ExecContext exc, | |
FT_ULong idx ) | |
{ | |
return FT_MulFix( exc->cvt[idx], Current_Ratio( exc ) ); | |
} | |
FT_CALLBACK_DEF( void ) | |
Write_CVT( TT_ExecContext exc, | |
FT_ULong idx, | |
FT_F26Dot6 value ) | |
{ | |
exc->cvt[idx] = value; | |
} | |
FT_CALLBACK_DEF( void ) | |
Write_CVT_Stretched( TT_ExecContext exc, | |
FT_ULong idx, | |
FT_F26Dot6 value ) | |
{ | |
exc->cvt[idx] = FT_DivFix( value, Current_Ratio( exc ) ); | |
} | |
FT_CALLBACK_DEF( void ) | |
Move_CVT( TT_ExecContext exc, | |
FT_ULong idx, | |
FT_F26Dot6 value ) | |
{ | |
exc->cvt[idx] += value; | |
} | |
FT_CALLBACK_DEF( void ) | |
Move_CVT_Stretched( TT_ExecContext exc, | |
FT_ULong idx, | |
FT_F26Dot6 value ) | |
{ | |
exc->cvt[idx] += FT_DivFix( value, Current_Ratio( exc ) ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* GetShortIns */ | |
/* */ | |
/* <Description> */ | |
/* Returns a short integer taken from the instruction stream at */ | |
/* address IP. */ | |
/* */ | |
/* <Return> */ | |
/* Short read at code[IP]. */ | |
/* */ | |
/* <Note> */ | |
/* This one could become a macro. */ | |
/* */ | |
static FT_Short | |
GetShortIns( TT_ExecContext exc ) | |
{ | |
/* Reading a byte stream so there is no endianess (DaveP) */ | |
exc->IP += 2; | |
return (FT_Short)( ( exc->code[exc->IP - 2] << 8 ) + | |
exc->code[exc->IP - 1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Ins_Goto_CodeRange */ | |
/* */ | |
/* <Description> */ | |
/* Goes to a certain code range in the instruction stream. */ | |
/* */ | |
/* <Input> */ | |
/* aRange :: The index of the code range. */ | |
/* */ | |
/* aIP :: The new IP address in the code range. */ | |
/* */ | |
/* <Return> */ | |
/* SUCCESS or FAILURE. */ | |
/* */ | |
static FT_Bool | |
Ins_Goto_CodeRange( TT_ExecContext exc, | |
FT_Int aRange, | |
FT_Long aIP ) | |
{ | |
TT_CodeRange* range; | |
if ( aRange < 1 || aRange > 3 ) | |
{ | |
exc->error = FT_THROW( Bad_Argument ); | |
return FAILURE; | |
} | |
range = &exc->codeRangeTable[aRange - 1]; | |
if ( range->base == NULL ) /* invalid coderange */ | |
{ | |
exc->error = FT_THROW( Invalid_CodeRange ); | |
return FAILURE; | |
} | |
/* NOTE: Because the last instruction of a program may be a CALL */ | |
/* which will return to the first byte *after* the code */ | |
/* range, we test for aIP <= Size, instead of aIP < Size. */ | |
if ( aIP > range->size ) | |
{ | |
exc->error = FT_THROW( Code_Overflow ); | |
return FAILURE; | |
} | |
exc->code = range->base; | |
exc->codeSize = range->size; | |
exc->IP = aIP; | |
exc->curRange = aRange; | |
return SUCCESS; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Direct_Move */ | |
/* */ | |
/* <Description> */ | |
/* Moves a point by a given distance along the freedom vector. The */ | |
/* point will be `touched'. */ | |
/* */ | |
/* <Input> */ | |
/* point :: The index of the point to move. */ | |
/* */ | |
/* distance :: The distance to apply. */ | |
/* */ | |
/* <InOut> */ | |
/* zone :: The affected glyph zone. */ | |
/* */ | |
static void | |
Direct_Move( TT_ExecContext exc, | |
TT_GlyphZone zone, | |
FT_UShort point, | |
FT_F26Dot6 distance ) | |
{ | |
FT_F26Dot6 v; | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
FT_ASSERT( !exc->face->unpatented_hinting ); | |
#endif | |
v = exc->GS.freeVector.x; | |
if ( v != 0 ) | |
{ | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( !SUBPIXEL_HINTING || | |
( !exc->ignore_x_mode || | |
( exc->sph_tweak_flags & SPH_TWEAK_ALLOW_X_DMOVE ) ) ) | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
zone->cur[point].x += FT_MulDiv( distance, v, exc->F_dot_P ); | |
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; | |
} | |
v = exc->GS.freeVector.y; | |
if ( v != 0 ) | |
{ | |
zone->cur[point].y += FT_MulDiv( distance, v, exc->F_dot_P ); | |
zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; | |
} | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Direct_Move_Orig */ | |
/* */ | |
/* <Description> */ | |
/* Moves the *original* position of a point by a given distance along */ | |
/* the freedom vector. Obviously, the point will not be `touched'. */ | |
/* */ | |
/* <Input> */ | |
/* point :: The index of the point to move. */ | |
/* */ | |
/* distance :: The distance to apply. */ | |
/* */ | |
/* <InOut> */ | |
/* zone :: The affected glyph zone. */ | |
/* */ | |
static void | |
Direct_Move_Orig( TT_ExecContext exc, | |
TT_GlyphZone zone, | |
FT_UShort point, | |
FT_F26Dot6 distance ) | |
{ | |
FT_F26Dot6 v; | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
FT_ASSERT( !exc->face->unpatented_hinting ); | |
#endif | |
v = exc->GS.freeVector.x; | |
if ( v != 0 ) | |
zone->org[point].x += FT_MulDiv( distance, v, exc->F_dot_P ); | |
v = exc->GS.freeVector.y; | |
if ( v != 0 ) | |
zone->org[point].y += FT_MulDiv( distance, v, exc->F_dot_P ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* Special versions of Direct_Move() */ | |
/* */ | |
/* The following versions are used whenever both vectors are both */ | |
/* along one of the coordinate unit vectors, i.e. in 90% of the cases. */ | |
/* */ | |
/*************************************************************************/ | |
static void | |
Direct_Move_X( TT_ExecContext exc, | |
TT_GlyphZone zone, | |
FT_UShort point, | |
FT_F26Dot6 distance ) | |
{ | |
FT_UNUSED( exc ); | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( !SUBPIXEL_HINTING || | |
!exc->ignore_x_mode ) | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
zone->cur[point].x += distance; | |
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X; | |
} | |
static void | |
Direct_Move_Y( TT_ExecContext exc, | |
TT_GlyphZone zone, | |
FT_UShort point, | |
FT_F26Dot6 distance ) | |
{ | |
FT_UNUSED( exc ); | |
zone->cur[point].y += distance; | |
zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* Special versions of Direct_Move_Orig() */ | |
/* */ | |
/* The following versions are used whenever both vectors are both */ | |
/* along one of the coordinate unit vectors, i.e. in 90% of the cases. */ | |
/* */ | |
/*************************************************************************/ | |
static void | |
Direct_Move_Orig_X( TT_ExecContext exc, | |
TT_GlyphZone zone, | |
FT_UShort point, | |
FT_F26Dot6 distance ) | |
{ | |
FT_UNUSED( exc ); | |
zone->org[point].x += distance; | |
} | |
static void | |
Direct_Move_Orig_Y( TT_ExecContext exc, | |
TT_GlyphZone zone, | |
FT_UShort point, | |
FT_F26Dot6 distance ) | |
{ | |
FT_UNUSED( exc ); | |
zone->org[point].y += distance; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Round_None */ | |
/* */ | |
/* <Description> */ | |
/* Does not round, but adds engine compensation. */ | |
/* */ | |
/* <Input> */ | |
/* distance :: The distance (not) to round. */ | |
/* */ | |
/* compensation :: The engine compensation. */ | |
/* */ | |
/* <Return> */ | |
/* The compensated distance. */ | |
/* */ | |
/* <Note> */ | |
/* The TrueType specification says very few about the relationship */ | |
/* between rounding and engine compensation. However, it seems from */ | |
/* the description of super round that we should add the compensation */ | |
/* before rounding. */ | |
/* */ | |
static FT_F26Dot6 | |
Round_None( TT_ExecContext exc, | |
FT_F26Dot6 distance, | |
FT_F26Dot6 compensation ) | |
{ | |
FT_F26Dot6 val; | |
FT_UNUSED( exc ); | |
if ( distance >= 0 ) | |
{ | |
val = distance + compensation; | |
if ( val < 0 ) | |
val = 0; | |
} | |
else | |
{ | |
val = distance - compensation; | |
if ( val > 0 ) | |
val = 0; | |
} | |
return val; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Round_To_Grid */ | |
/* */ | |
/* <Description> */ | |
/* Rounds value to grid after adding engine compensation. */ | |
/* */ | |
/* <Input> */ | |
/* distance :: The distance to round. */ | |
/* */ | |
/* compensation :: The engine compensation. */ | |
/* */ | |
/* <Return> */ | |
/* Rounded distance. */ | |
/* */ | |
static FT_F26Dot6 | |
Round_To_Grid( TT_ExecContext exc, | |
FT_F26Dot6 distance, | |
FT_F26Dot6 compensation ) | |
{ | |
FT_F26Dot6 val; | |
FT_UNUSED( exc ); | |
if ( distance >= 0 ) | |
{ | |
val = FT_PIX_ROUND( distance + compensation ); | |
if ( val < 0 ) | |
val = 0; | |
} | |
else | |
{ | |
val = -FT_PIX_ROUND( compensation - distance ); | |
if ( val > 0 ) | |
val = 0; | |
} | |
return val; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Round_To_Half_Grid */ | |
/* */ | |
/* <Description> */ | |
/* Rounds value to half grid after adding engine compensation. */ | |
/* */ | |
/* <Input> */ | |
/* distance :: The distance to round. */ | |
/* */ | |
/* compensation :: The engine compensation. */ | |
/* */ | |
/* <Return> */ | |
/* Rounded distance. */ | |
/* */ | |
static FT_F26Dot6 | |
Round_To_Half_Grid( TT_ExecContext exc, | |
FT_F26Dot6 distance, | |
FT_F26Dot6 compensation ) | |
{ | |
FT_F26Dot6 val; | |
FT_UNUSED( exc ); | |
if ( distance >= 0 ) | |
{ | |
val = FT_PIX_FLOOR( distance + compensation ) + 32; | |
if ( val < 0 ) | |
val = 32; | |
} | |
else | |
{ | |
val = -( FT_PIX_FLOOR( compensation - distance ) + 32 ); | |
if ( val > 0 ) | |
val = -32; | |
} | |
return val; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Round_Down_To_Grid */ | |
/* */ | |
/* <Description> */ | |
/* Rounds value down to grid after adding engine compensation. */ | |
/* */ | |
/* <Input> */ | |
/* distance :: The distance to round. */ | |
/* */ | |
/* compensation :: The engine compensation. */ | |
/* */ | |
/* <Return> */ | |
/* Rounded distance. */ | |
/* */ | |
static FT_F26Dot6 | |
Round_Down_To_Grid( TT_ExecContext exc, | |
FT_F26Dot6 distance, | |
FT_F26Dot6 compensation ) | |
{ | |
FT_F26Dot6 val; | |
FT_UNUSED( exc ); | |
if ( distance >= 0 ) | |
{ | |
val = FT_PIX_FLOOR( distance + compensation ); | |
if ( val < 0 ) | |
val = 0; | |
} | |
else | |
{ | |
val = -FT_PIX_FLOOR( compensation - distance ); | |
if ( val > 0 ) | |
val = 0; | |
} | |
return val; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Round_Up_To_Grid */ | |
/* */ | |
/* <Description> */ | |
/* Rounds value up to grid after adding engine compensation. */ | |
/* */ | |
/* <Input> */ | |
/* distance :: The distance to round. */ | |
/* */ | |
/* compensation :: The engine compensation. */ | |
/* */ | |
/* <Return> */ | |
/* Rounded distance. */ | |
/* */ | |
static FT_F26Dot6 | |
Round_Up_To_Grid( TT_ExecContext exc, | |
FT_F26Dot6 distance, | |
FT_F26Dot6 compensation ) | |
{ | |
FT_F26Dot6 val; | |
FT_UNUSED( exc ); | |
if ( distance >= 0 ) | |
{ | |
val = FT_PIX_CEIL( distance + compensation ); | |
if ( val < 0 ) | |
val = 0; | |
} | |
else | |
{ | |
val = -FT_PIX_CEIL( compensation - distance ); | |
if ( val > 0 ) | |
val = 0; | |
} | |
return val; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Round_To_Double_Grid */ | |
/* */ | |
/* <Description> */ | |
/* Rounds value to double grid after adding engine compensation. */ | |
/* */ | |
/* <Input> */ | |
/* distance :: The distance to round. */ | |
/* */ | |
/* compensation :: The engine compensation. */ | |
/* */ | |
/* <Return> */ | |
/* Rounded distance. */ | |
/* */ | |
static FT_F26Dot6 | |
Round_To_Double_Grid( TT_ExecContext exc, | |
FT_F26Dot6 distance, | |
FT_F26Dot6 compensation ) | |
{ | |
FT_F26Dot6 val; | |
FT_UNUSED( exc ); | |
if ( distance >= 0 ) | |
{ | |
val = FT_PAD_ROUND( distance + compensation, 32 ); | |
if ( val < 0 ) | |
val = 0; | |
} | |
else | |
{ | |
val = -FT_PAD_ROUND( compensation - distance, 32 ); | |
if ( val > 0 ) | |
val = 0; | |
} | |
return val; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Round_Super */ | |
/* */ | |
/* <Description> */ | |
/* Super-rounds value to grid after adding engine compensation. */ | |
/* */ | |
/* <Input> */ | |
/* distance :: The distance to round. */ | |
/* */ | |
/* compensation :: The engine compensation. */ | |
/* */ | |
/* <Return> */ | |
/* Rounded distance. */ | |
/* */ | |
/* <Note> */ | |
/* The TrueType specification says very few about the relationship */ | |
/* between rounding and engine compensation. However, it seems from */ | |
/* the description of super round that we should add the compensation */ | |
/* before rounding. */ | |
/* */ | |
static FT_F26Dot6 | |
Round_Super( TT_ExecContext exc, | |
FT_F26Dot6 distance, | |
FT_F26Dot6 compensation ) | |
{ | |
FT_F26Dot6 val; | |
if ( distance >= 0 ) | |
{ | |
val = ( distance - exc->phase + exc->threshold + compensation ) & | |
-exc->period; | |
val += exc->phase; | |
if ( val < 0 ) | |
val = exc->phase; | |
} | |
else | |
{ | |
val = -( ( exc->threshold - exc->phase - distance + compensation ) & | |
-exc->period ); | |
val -= exc->phase; | |
if ( val > 0 ) | |
val = -exc->phase; | |
} | |
return val; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Round_Super_45 */ | |
/* */ | |
/* <Description> */ | |
/* Super-rounds value to grid after adding engine compensation. */ | |
/* */ | |
/* <Input> */ | |
/* distance :: The distance to round. */ | |
/* */ | |
/* compensation :: The engine compensation. */ | |
/* */ | |
/* <Return> */ | |
/* Rounded distance. */ | |
/* */ | |
/* <Note> */ | |
/* There is a separate function for Round_Super_45() as we may need */ | |
/* greater precision. */ | |
/* */ | |
static FT_F26Dot6 | |
Round_Super_45( TT_ExecContext exc, | |
FT_F26Dot6 distance, | |
FT_F26Dot6 compensation ) | |
{ | |
FT_F26Dot6 val; | |
if ( distance >= 0 ) | |
{ | |
val = ( ( distance - exc->phase + exc->threshold + compensation ) / | |
exc->period ) * exc->period; | |
val += exc->phase; | |
if ( val < 0 ) | |
val = exc->phase; | |
} | |
else | |
{ | |
val = -( ( ( exc->threshold - exc->phase - distance + compensation ) / | |
exc->period ) * exc->period ); | |
val -= exc->phase; | |
if ( val > 0 ) | |
val = -exc->phase; | |
} | |
return val; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Compute_Round */ | |
/* */ | |
/* <Description> */ | |
/* Sets the rounding mode. */ | |
/* */ | |
/* <Input> */ | |
/* round_mode :: The rounding mode to be used. */ | |
/* */ | |
static void | |
Compute_Round( TT_ExecContext exc, | |
FT_Byte round_mode ) | |
{ | |
switch ( round_mode ) | |
{ | |
case TT_Round_Off: | |
exc->func_round = (TT_Round_Func)Round_None; | |
break; | |
case TT_Round_To_Grid: | |
exc->func_round = (TT_Round_Func)Round_To_Grid; | |
break; | |
case TT_Round_Up_To_Grid: | |
exc->func_round = (TT_Round_Func)Round_Up_To_Grid; | |
break; | |
case TT_Round_Down_To_Grid: | |
exc->func_round = (TT_Round_Func)Round_Down_To_Grid; | |
break; | |
case TT_Round_To_Half_Grid: | |
exc->func_round = (TT_Round_Func)Round_To_Half_Grid; | |
break; | |
case TT_Round_To_Double_Grid: | |
exc->func_round = (TT_Round_Func)Round_To_Double_Grid; | |
break; | |
case TT_Round_Super: | |
exc->func_round = (TT_Round_Func)Round_Super; | |
break; | |
case TT_Round_Super_45: | |
exc->func_round = (TT_Round_Func)Round_Super_45; | |
break; | |
} | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* SetSuperRound */ | |
/* */ | |
/* <Description> */ | |
/* Sets Super Round parameters. */ | |
/* */ | |
/* <Input> */ | |
/* GridPeriod :: The grid period. */ | |
/* */ | |
/* selector :: The SROUND opcode. */ | |
/* */ | |
static void | |
SetSuperRound( TT_ExecContext exc, | |
FT_F2Dot14 GridPeriod, | |
FT_Long selector ) | |
{ | |
switch ( (FT_Int)( selector & 0xC0 ) ) | |
{ | |
case 0: | |
exc->period = GridPeriod / 2; | |
break; | |
case 0x40: | |
exc->period = GridPeriod; | |
break; | |
case 0x80: | |
exc->period = GridPeriod * 2; | |
break; | |
/* This opcode is reserved, but... */ | |
case 0xC0: | |
exc->period = GridPeriod; | |
break; | |
} | |
switch ( (FT_Int)( selector & 0x30 ) ) | |
{ | |
case 0: | |
exc->phase = 0; | |
break; | |
case 0x10: | |
exc->phase = exc->period / 4; | |
break; | |
case 0x20: | |
exc->phase = exc->period / 2; | |
break; | |
case 0x30: | |
exc->phase = exc->period * 3 / 4; | |
break; | |
} | |
if ( ( selector & 0x0F ) == 0 ) | |
exc->threshold = exc->period - 1; | |
else | |
exc->threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * exc->period / 8; | |
/* convert to F26Dot6 format */ | |
exc->period >>= 8; | |
exc->phase >>= 8; | |
exc->threshold >>= 8; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Project */ | |
/* */ | |
/* <Description> */ | |
/* Computes the projection of vector given by (v2-v1) along the */ | |
/* current projection vector. */ | |
/* */ | |
/* <Input> */ | |
/* v1 :: First input vector. */ | |
/* v2 :: Second input vector. */ | |
/* */ | |
/* <Return> */ | |
/* The distance in F26dot6 format. */ | |
/* */ | |
static FT_F26Dot6 | |
Project( TT_ExecContext exc, | |
FT_Pos dx, | |
FT_Pos dy ) | |
{ | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
FT_ASSERT( !exc->face->unpatented_hinting ); | |
#endif | |
return TT_DotFix14( dx, dy, | |
exc->GS.projVector.x, | |
exc->GS.projVector.y ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Dual_Project */ | |
/* */ | |
/* <Description> */ | |
/* Computes the projection of the vector given by (v2-v1) along the */ | |
/* current dual vector. */ | |
/* */ | |
/* <Input> */ | |
/* v1 :: First input vector. */ | |
/* v2 :: Second input vector. */ | |
/* */ | |
/* <Return> */ | |
/* The distance in F26dot6 format. */ | |
/* */ | |
static FT_F26Dot6 | |
Dual_Project( TT_ExecContext exc, | |
FT_Pos dx, | |
FT_Pos dy ) | |
{ | |
return TT_DotFix14( dx, dy, | |
exc->GS.dualVector.x, | |
exc->GS.dualVector.y ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Project_x */ | |
/* */ | |
/* <Description> */ | |
/* Computes the projection of the vector given by (v2-v1) along the */ | |
/* horizontal axis. */ | |
/* */ | |
/* <Input> */ | |
/* v1 :: First input vector. */ | |
/* v2 :: Second input vector. */ | |
/* */ | |
/* <Return> */ | |
/* The distance in F26dot6 format. */ | |
/* */ | |
static FT_F26Dot6 | |
Project_x( TT_ExecContext exc, | |
FT_Pos dx, | |
FT_Pos dy ) | |
{ | |
FT_UNUSED( exc ); | |
FT_UNUSED( dy ); | |
return dx; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Project_y */ | |
/* */ | |
/* <Description> */ | |
/* Computes the projection of the vector given by (v2-v1) along the */ | |
/* vertical axis. */ | |
/* */ | |
/* <Input> */ | |
/* v1 :: First input vector. */ | |
/* v2 :: Second input vector. */ | |
/* */ | |
/* <Return> */ | |
/* The distance in F26dot6 format. */ | |
/* */ | |
static FT_F26Dot6 | |
Project_y( TT_ExecContext exc, | |
FT_Pos dx, | |
FT_Pos dy ) | |
{ | |
FT_UNUSED( exc ); | |
FT_UNUSED( dx ); | |
return dy; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Compute_Funcs */ | |
/* */ | |
/* <Description> */ | |
/* Computes the projection and movement function pointers according */ | |
/* to the current graphics state. */ | |
/* */ | |
static void | |
Compute_Funcs( TT_ExecContext exc ) | |
{ | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
if ( exc->face->unpatented_hinting ) | |
{ | |
/* If both vectors point rightwards along the x axis, set */ | |
/* `both-x-axis' true, otherwise set it false. The x values only */ | |
/* need be tested because the vector has been normalised to a unit */ | |
/* vector of length 0x4000 = unity. */ | |
exc->GS.both_x_axis = (FT_Bool)( exc->GS.projVector.x == 0x4000 && | |
exc->GS.freeVector.x == 0x4000 ); | |
/* Throw away projection and freedom vector information */ | |
/* because the patents don't allow them to be stored. */ | |
/* The relevant US Patents are 5155805 and 5325479. */ | |
exc->GS.projVector.x = 0; | |
exc->GS.projVector.y = 0; | |
exc->GS.freeVector.x = 0; | |
exc->GS.freeVector.y = 0; | |
if ( exc->GS.both_x_axis ) | |
{ | |
exc->func_project = Project_x; | |
exc->func_move = Direct_Move_X; | |
exc->func_move_orig = Direct_Move_Orig_X; | |
} | |
else | |
{ | |
exc->func_project = Project_y; | |
exc->func_move = Direct_Move_Y; | |
exc->func_move_orig = Direct_Move_Orig_Y; | |
} | |
if ( exc->GS.dualVector.x == 0x4000 ) | |
exc->func_dualproj = Project_x; | |
else if ( exc->GS.dualVector.y == 0x4000 ) | |
exc->func_dualproj = Project_y; | |
else | |
exc->func_dualproj = Dual_Project; | |
/* Force recalculation of cached aspect ratio */ | |
exc->tt_metrics.ratio = 0; | |
return; | |
} | |
#endif /* TT_CONFIG_OPTION_UNPATENTED_HINTING */ | |
if ( exc->GS.freeVector.x == 0x4000 ) | |
exc->F_dot_P = exc->GS.projVector.x; | |
else if ( exc->GS.freeVector.y == 0x4000 ) | |
exc->F_dot_P = exc->GS.projVector.y; | |
else | |
exc->F_dot_P = | |
( (FT_Long)exc->GS.projVector.x * exc->GS.freeVector.x + | |
(FT_Long)exc->GS.projVector.y * exc->GS.freeVector.y ) >> 14; | |
if ( exc->GS.projVector.x == 0x4000 ) | |
exc->func_project = (TT_Project_Func)Project_x; | |
else if ( exc->GS.projVector.y == 0x4000 ) | |
exc->func_project = (TT_Project_Func)Project_y; | |
else | |
exc->func_project = (TT_Project_Func)Project; | |
if ( exc->GS.dualVector.x == 0x4000 ) | |
exc->func_dualproj = (TT_Project_Func)Project_x; | |
else if ( exc->GS.dualVector.y == 0x4000 ) | |
exc->func_dualproj = (TT_Project_Func)Project_y; | |
else | |
exc->func_dualproj = (TT_Project_Func)Dual_Project; | |
exc->func_move = (TT_Move_Func)Direct_Move; | |
exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig; | |
if ( exc->F_dot_P == 0x4000L ) | |
{ | |
if ( exc->GS.freeVector.x == 0x4000 ) | |
{ | |
exc->func_move = (TT_Move_Func)Direct_Move_X; | |
exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_X; | |
} | |
else if ( exc->GS.freeVector.y == 0x4000 ) | |
{ | |
exc->func_move = (TT_Move_Func)Direct_Move_Y; | |
exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y; | |
} | |
} | |
/* at small sizes, F_dot_P can become too small, resulting */ | |
/* in overflows and `spikes' in a number of glyphs like `w'. */ | |
if ( FT_ABS( exc->F_dot_P ) < 0x400L ) | |
exc->F_dot_P = 0x4000L; | |
/* Disable cached aspect ratio */ | |
exc->tt_metrics.ratio = 0; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* <Function> */ | |
/* Normalize */ | |
/* */ | |
/* <Description> */ | |
/* Norms a vector. */ | |
/* */ | |
/* <Input> */ | |
/* Vx :: The horizontal input vector coordinate. */ | |
/* Vy :: The vertical input vector coordinate. */ | |
/* */ | |
/* <Output> */ | |
/* R :: The normed unit vector. */ | |
/* */ | |
/* <Return> */ | |
/* Returns FAILURE if a vector parameter is zero. */ | |
/* */ | |
/* <Note> */ | |
/* In case Vx and Vy are both zero, `Normalize' returns SUCCESS, and */ | |
/* R is undefined. */ | |
/* */ | |
static FT_Bool | |
Normalize( FT_F26Dot6 Vx, | |
FT_F26Dot6 Vy, | |
FT_UnitVector* R ) | |
{ | |
FT_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 ) | |
{ | |
/* Note: The point size should be irrelevant in a given font program; */ | |
/* we thus decide to return only the PPEM value. */ | |
#if 0 | |
args[0] = exc->metrics.pointSize; | |
#else | |
args[0] = exc->func_cur_ppem( exc ); | |
#endif | |
} | |
/*************************************************************************/ | |
/* */ | |
/* DUP[]: DUPlicate the stack's top element */ | |
/* Opcode range: 0x20 */ | |
/* Stack: StkElt --> StkElt StkElt */ | |
/* */ | |
static void | |
Ins_DUP( FT_Long* args ) | |
{ | |
args[1] = args[0]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* POP[]: POP the stack's top element */ | |
/* Opcode range: 0x21 */ | |
/* Stack: StkElt --> */ | |
/* */ | |
static void | |
Ins_POP( void ) | |
{ | |
/* nothing to do */ | |
} | |
/*************************************************************************/ | |
/* */ | |
/* CLEAR[]: CLEAR the entire stack */ | |
/* Opcode range: 0x22 */ | |
/* Stack: StkElt... --> */ | |
/* */ | |
static void | |
Ins_CLEAR( TT_ExecContext exc ) | |
{ | |
exc->new_top = 0; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* SWAP[]: SWAP the stack's top two elements */ | |
/* Opcode range: 0x23 */ | |
/* Stack: 2 * StkElt --> 2 * StkElt */ | |
/* */ | |
static void | |
Ins_SWAP( FT_Long* args ) | |
{ | |
FT_Long L; | |
L = args[0]; | |
args[0] = args[1]; | |
args[1] = L; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* DEPTH[]: return the stack DEPTH */ | |
/* Opcode range: 0x24 */ | |
/* Stack: --> uint32 */ | |
/* */ | |
static void | |
Ins_DEPTH( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
args[0] = exc->top; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* LT[]: Less Than */ | |
/* Opcode range: 0x50 */ | |
/* Stack: int32? int32? --> bool */ | |
/* */ | |
static void | |
Ins_LT( FT_Long* args ) | |
{ | |
args[0] = ( args[0] < args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* LTEQ[]: Less Than or EQual */ | |
/* Opcode range: 0x51 */ | |
/* Stack: int32? int32? --> bool */ | |
/* */ | |
static void | |
Ins_LTEQ( FT_Long* args ) | |
{ | |
args[0] = ( args[0] <= args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* GT[]: Greater Than */ | |
/* Opcode range: 0x52 */ | |
/* Stack: int32? int32? --> bool */ | |
/* */ | |
static void | |
Ins_GT( FT_Long* args ) | |
{ | |
args[0] = ( args[0] > args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* GTEQ[]: Greater Than or EQual */ | |
/* Opcode range: 0x53 */ | |
/* Stack: int32? int32? --> bool */ | |
/* */ | |
static void | |
Ins_GTEQ( FT_Long* args ) | |
{ | |
args[0] = ( args[0] >= args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* EQ[]: EQual */ | |
/* Opcode range: 0x54 */ | |
/* Stack: StkElt StkElt --> bool */ | |
/* */ | |
static void | |
Ins_EQ( FT_Long* args ) | |
{ | |
args[0] = ( args[0] == args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* NEQ[]: Not EQual */ | |
/* Opcode range: 0x55 */ | |
/* Stack: StkElt StkElt --> bool */ | |
/* */ | |
static void | |
Ins_NEQ( FT_Long* args ) | |
{ | |
args[0] = ( args[0] != args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* ODD[]: Is ODD */ | |
/* Opcode range: 0x56 */ | |
/* Stack: f26.6 --> bool */ | |
/* */ | |
static void | |
Ins_ODD( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 64 ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* EVEN[]: Is EVEN */ | |
/* Opcode range: 0x57 */ | |
/* Stack: f26.6 --> bool */ | |
/* */ | |
static void | |
Ins_EVEN( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
args[0] = ( ( exc->func_round( exc, args[0], 0 ) & 127 ) == 0 ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* AND[]: logical AND */ | |
/* Opcode range: 0x5A */ | |
/* Stack: uint32 uint32 --> uint32 */ | |
/* */ | |
static void | |
Ins_AND( FT_Long* args ) | |
{ | |
args[0] = ( args[0] && args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* OR[]: logical OR */ | |
/* Opcode range: 0x5B */ | |
/* Stack: uint32 uint32 --> uint32 */ | |
/* */ | |
static void | |
Ins_OR( FT_Long* args ) | |
{ | |
args[0] = ( args[0] || args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* NOT[]: logical NOT */ | |
/* Opcode range: 0x5C */ | |
/* Stack: StkElt --> uint32 */ | |
/* */ | |
static void | |
Ins_NOT( FT_Long* args ) | |
{ | |
args[0] = !args[0]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* ADD[]: ADD */ | |
/* Opcode range: 0x60 */ | |
/* Stack: f26.6 f26.6 --> f26.6 */ | |
/* */ | |
static void | |
Ins_ADD( FT_Long* args ) | |
{ | |
args[0] += args[1]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* SUB[]: SUBtract */ | |
/* Opcode range: 0x61 */ | |
/* Stack: f26.6 f26.6 --> f26.6 */ | |
/* */ | |
static void | |
Ins_SUB( FT_Long* args ) | |
{ | |
args[0] -= args[1]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* DIV[]: DIVide */ | |
/* Opcode range: 0x62 */ | |
/* Stack: f26.6 f26.6 --> f26.6 */ | |
/* */ | |
static void | |
Ins_DIV( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
if ( args[1] == 0 ) | |
exc->error = FT_THROW( Divide_By_Zero ); | |
else | |
args[0] = FT_MulDiv_No_Round( args[0], 64L, args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* MUL[]: MULtiply */ | |
/* Opcode range: 0x63 */ | |
/* Stack: f26.6 f26.6 --> f26.6 */ | |
/* */ | |
static void | |
Ins_MUL( FT_Long* args ) | |
{ | |
args[0] = FT_MulDiv( args[0], args[1], 64L ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* ABS[]: ABSolute value */ | |
/* Opcode range: 0x64 */ | |
/* Stack: f26.6 --> f26.6 */ | |
/* */ | |
static void | |
Ins_ABS( FT_Long* args ) | |
{ | |
args[0] = FT_ABS( args[0] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* NEG[]: NEGate */ | |
/* Opcode range: 0x65 */ | |
/* Stack: f26.6 --> f26.6 */ | |
/* */ | |
static void | |
Ins_NEG( FT_Long* args ) | |
{ | |
args[0] = -args[0]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* FLOOR[]: FLOOR */ | |
/* Opcode range: 0x66 */ | |
/* Stack: f26.6 --> f26.6 */ | |
/* */ | |
static void | |
Ins_FLOOR( FT_Long* args ) | |
{ | |
args[0] = FT_PIX_FLOOR( args[0] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* CEILING[]: CEILING */ | |
/* Opcode range: 0x67 */ | |
/* Stack: f26.6 --> f26.6 */ | |
/* */ | |
static void | |
Ins_CEILING( FT_Long* args ) | |
{ | |
args[0] = FT_PIX_CEIL( args[0] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* RS[]: Read Store */ | |
/* Opcode range: 0x43 */ | |
/* Stack: uint32 --> uint32 */ | |
/* */ | |
static void | |
Ins_RS( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
FT_ULong I = (FT_ULong)args[0]; | |
if ( BOUNDSL( I, exc->storeSize ) ) | |
{ | |
if ( exc->pedantic_hinting ) | |
ARRAY_BOUND_ERROR; | |
else | |
args[0] = 0; | |
} | |
else | |
{ | |
/* subpixel hinting - avoid Typeman Dstroke and */ | |
/* IStroke and Vacuform rounds */ | |
if ( SUBPIXEL_HINTING && | |
exc->ignore_x_mode && | |
( ( I == 24 && | |
( exc->face->sph_found_func_flags & | |
( SPH_FDEF_SPACING_1 | | |
SPH_FDEF_SPACING_2 ) ) ) || | |
( I == 22 && | |
( exc->sph_in_func_flags & | |
SPH_FDEF_TYPEMAN_STROKES ) ) || | |
( I == 8 && | |
( exc->face->sph_found_func_flags & | |
SPH_FDEF_VACUFORM_ROUND_1 ) && | |
exc->iup_called ) ) ) | |
args[0] = 0; | |
else | |
args[0] = exc->storage[I]; | |
} | |
#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
FT_ULong I = (FT_ULong)args[0]; | |
if ( BOUNDSL( I, exc->storeSize ) ) | |
{ | |
if ( exc->pedantic_hinting ) | |
ARRAY_BOUND_ERROR; | |
else | |
args[0] = 0; | |
} | |
else | |
args[0] = exc->storage[I]; | |
#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
} | |
/*************************************************************************/ | |
/* */ | |
/* WS[]: Write Store */ | |
/* Opcode range: 0x42 */ | |
/* Stack: uint32 uint32 --> */ | |
/* */ | |
static void | |
Ins_WS( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
FT_ULong I = (FT_ULong)args[0]; | |
if ( BOUNDSL( I, exc->storeSize ) ) | |
{ | |
if ( exc->pedantic_hinting ) | |
ARRAY_BOUND_ERROR; | |
} | |
else | |
exc->storage[I] = args[1]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* WCVTP[]: Write CVT in Pixel units */ | |
/* Opcode range: 0x44 */ | |
/* Stack: f26.6 uint32 --> */ | |
/* */ | |
static void | |
Ins_WCVTP( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
FT_ULong I = (FT_ULong)args[0]; | |
if ( BOUNDSL( I, exc->cvtSize ) ) | |
{ | |
if ( exc->pedantic_hinting ) | |
ARRAY_BOUND_ERROR; | |
} | |
else | |
exc->func_write_cvt( exc, I, args[1] ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* WCVTF[]: Write CVT in Funits */ | |
/* Opcode range: 0x70 */ | |
/* Stack: uint32 uint32 --> */ | |
/* */ | |
static void | |
Ins_WCVTF( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
FT_ULong I = (FT_ULong)args[0]; | |
if ( BOUNDSL( I, exc->cvtSize ) ) | |
{ | |
if ( exc->pedantic_hinting ) | |
ARRAY_BOUND_ERROR; | |
} | |
else | |
exc->cvt[I] = FT_MulFix( args[1], exc->tt_metrics.scale ); | |
} | |
/*************************************************************************/ | |
/* */ | |
/* RCVT[]: Read CVT */ | |
/* Opcode range: 0x45 */ | |
/* Stack: uint32 --> f26.6 */ | |
/* */ | |
static void | |
Ins_RCVT( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
FT_ULong I = (FT_ULong)args[0]; | |
if ( BOUNDSL( 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: 0x68 */ | |
/* Stack: int32? int32? --> int32 */ | |
/* */ | |
static void | |
Ins_MAX( FT_Long* args ) | |
{ | |
if ( args[1] > args[0] ) | |
args[0] = args[1]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* MIN[]: MINimum */ | |
/* Opcode range: 0x69 */ | |
/* 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 ); | |
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 ); | |
exc->step_ins = FALSE; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
/* 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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
/* 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
/* 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING ) | |
{ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
exc->sph_in_func_flags = 0x0000; | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
/* 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
/* 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; | |
} | |
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; | |
/* 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 */ | |
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 = p1->x - p2->x; | |
B = p1->y - p2->y; | |
/* If p1 == p2, SPvTL and SFvTL behave the same as */ | |
/* SPvTCA[X] and SFvTCA[X], respectively. */ | |
/* */ | |
/* Confirmed by Greg Hitchcock. */ | |
if ( A == 0 && B == 0 ) | |
{ | |
A = 0x4000; | |
opcode = 0; | |
} | |
if ( ( opcode & 1 ) != 0 ) | |
{ | |
C = B; /* counter clockwise rotation */ | |
B = A; | |
A = -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; | |
} | |
else | |
GUESS_VECTOR( projVector ); | |
if ( ( opcode & 2 ) == 0 ) | |
{ | |
exc->GS.freeVector.x = AA; | |
exc->GS.freeVector.y = BB; | |
} | |
else | |
GUESS_VECTOR( freeVector ); | |
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; | |
GUESS_VECTOR( freeVector ); | |
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 ) | |
{ | |
GUESS_VECTOR( projVector ); | |
Compute_Funcs( exc ); | |
} | |
} | |
/*************************************************************************/ | |
/* */ | |
/* SFvTPv[]: Set FVector To PVector */ | |
/* Opcode range: 0x0E */ | |
/* Stack: --> */ | |
/* */ | |
static void | |
Ins_SFVTPV( TT_ExecContext exc ) | |
{ | |
GUESS_VECTOR( projVector ); | |
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; | |
GUESS_VECTOR( freeVector ); | |
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 ); | |
GUESS_VECTOR( projVector ); | |
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 ) | |
{ | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
if ( exc->face->unpatented_hinting ) | |
{ | |
args[0] = exc->GS.both_x_axis ? 0x4000 : 0; | |
args[1] = exc->GS.both_x_axis ? 0 : 0x4000; | |
} | |
else | |
{ | |
args[0] = exc->GS.projVector.x; | |
args[1] = exc->GS.projVector.y; | |
} | |
#else | |
args[0] = exc->GS.projVector.x; | |
args[1] = exc->GS.projVector.y; | |
#endif | |
} | |
/*************************************************************************/ | |
/* */ | |
/* GFv[]: Get Freedom Vector */ | |
/* Opcode range: 0x0D */ | |
/* Stack: ef2.14 --> ef2.14 */ | |
/* */ | |
static void | |
Ins_GFV( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
if ( exc->face->unpatented_hinting ) | |
{ | |
args[0] = exc->GS.both_x_axis ? 0x4000 : 0; | |
args[1] = exc->GS.both_x_axis ? 0 : 0x4000; | |
} | |
else | |
{ | |
args[0] = exc->GS.freeVector.x; | |
args[1] = exc->GS.freeVector.y; | |
} | |
#else | |
args[0] = exc->GS.freeVector.x; | |
args[1] = exc->GS.freeVector.y; | |
#endif | |
} | |
/*************************************************************************/ | |
/* */ | |
/* 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, 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
/* Disable Type 2 Vacuform Rounds - e.g. Arial Narrow */ | |
if ( SUBPIXEL_HINTING && | |
exc->ignore_x_mode && | |
FT_ABS( D ) == 64 ) | |
D += 1; | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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 = v1->x - v2->x; | |
B = v1->y - v2->y; | |
/* If v1 == v2, SDPvTL behaves the same as */ | |
/* SVTCA[X], respectively. */ | |
/* */ | |
/* Confirmed by Greg Hitchcock. */ | |
if ( A == 0 && B == 0 ) | |
{ | |
A = 0x4000; | |
opcode = 0; | |
} | |
} | |
if ( ( opcode & 1 ) != 0 ) | |
{ | |
C = B; /* counter clockwise rotation */ | |
B = A; | |
A = -C; | |
} | |
Normalize( A, B, &exc->GS.dualVector ); | |
{ | |
FT_Vector* v1 = exc->zp1.cur + p2; | |
FT_Vector* v2 = exc->zp2.cur + p1; | |
A = v1->x - v2->x; | |
B = 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 = -C; | |
} | |
Normalize( A, B, &exc->GS.projVector ); | |
GUESS_VECTOR( freeVector ); | |
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; | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
/* INSTCTRL modifying flag 3 also has an effect */ | |
/* outside of the CVT program */ | |
if ( K == 3 ) | |
exc->ignore_x_mode = 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: uint32? --> */ | |
/* */ | |
static void | |
Ins_SCANTYPE( TT_ExecContext exc, | |
FT_Long* args ) | |
{ | |
if ( args[0] >= 0 ) | |
exc->GS.scan_type = (FT_Int)args[0]; | |
} | |
/*************************************************************************/ | |
/* */ | |
/* MANAGING OUTLINES */ | |
/* */ | |
/*************************************************************************/ | |
/*************************************************************************/ | |
/* */ | |
/* FLIPPT[]: FLIP PoinT */ | |
/* Opcode range: 0x80 */ | |
/* Stack: uint32... --> */ | |
/* */ | |
static void | |
Ins_FLIPPT( TT_ExecContext exc ) | |
{ | |
FT_UShort point; | |
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; | |
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; | |
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 ); | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
if ( exc->face->unpatented_hinting ) | |
{ | |
if ( exc->GS.both_x_axis ) | |
{ | |
*x = d; | |
*y = 0; | |
} | |
else | |
{ | |
*x = 0; | |
*y = d; | |
} | |
} | |
else | |
#endif | |
{ | |
*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; | |
} | |
static void | |
Move_Zp2_Point( TT_ExecContext exc, | |
FT_UShort point, | |
FT_F26Dot6 dx, | |
FT_F26Dot6 dy, | |
FT_Bool touch ) | |
{ | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
if ( exc->face->unpatented_hinting ) | |
{ | |
if ( exc->GS.both_x_axis ) | |
{ | |
exc->zp2.cur[point].x += dx; | |
if ( touch ) | |
exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X; | |
} | |
else | |
{ | |
exc->zp2.cur[point].y += dy; | |
if ( touch ) | |
exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y; | |
} | |
return; | |
} | |
#endif | |
if ( exc->GS.freeVector.x != 0 ) | |
{ | |
exc->zp2.cur[point].x += dx; | |
if ( touch ) | |
exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X; | |
} | |
if ( exc->GS.freeVector.y != 0 ) | |
{ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
/* doesn't follow Cleartype spec but produces better result */ | |
if ( SUBPIXEL_HINTING && | |
exc->ignore_x_mode ) | |
Move_Zp2_Point( exc, point, 0, dy, TRUE ); | |
else | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
FT_Int B1, B2; | |
#endif | |
if ( exc->top < exc->GS.loop + 1 ) | |
{ | |
if ( exc->pedantic_hinting ) | |
exc->error = FT_THROW( Invalid_Reference ); | |
goto Fail; | |
} | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
if ( exc->face->unpatented_hinting ) | |
{ | |
if ( exc->GS.both_x_axis ) | |
{ | |
dx = (FT_UInt32)args[0]; | |
dy = 0; | |
} | |
else | |
{ | |
dx = 0; | |
dy = (FT_UInt32)args[0]; | |
} | |
} | |
else | |
#endif | |
{ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
{ | |
/* 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 ( SUBPIXEL_HINTING && | |
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, -dx, -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, -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 ); | |
} | |
Skip: | |
#else /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
Move_Zp2_Point( exc, point, dx, dy, TRUE ); | |
#endif /* !TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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; | |
FT_F26Dot6 distance; | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
FT_F26Dot6 control_value_cutin = 0; /* pacify compiler */ | |
if ( SUBPIXEL_HINTING ) | |
{ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
/* subpixel hinting - make MSIRP respect CVT cut-in; */ | |
if ( SUBPIXEL_HINTING && | |
exc->ignore_x_mode && | |
exc->GS.freeVector.x != 0 && | |
FT_ABS( distance - args[1] ) >= control_value_cutin ) | |
distance = args[1]; | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
exc->func_move( exc, &exc->zp1, point, 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
/* Only adjust if not in sph_compatibility_mode or ignore_x_mode. */ | |
/* Determined via experimentation and may be incorrect... */ | |
if ( !SUBPIXEL_HINTING || | |
( !exc->ignore_x_mode || | |
!exc->face->sph_compatibility_mode ) ) | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
exc->ignore_x_mode && | |
( exc->sph_tweak_flags & SPH_TWEAK_MIAP_HACK ) && | |
distance > 0 && | |
exc->GS.freeVector.y != 0 ) | |
distance = 0; | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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, 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; | |
FT_F26Dot6 org_dist, distance, minimum_distance; | |
minimum_distance = exc->GS.minimum_distance; | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
exc->ignore_x_mode && | |
exc->GS.freeVector.x != 0 && | |
!( exc->sph_tweak_flags & SPH_TWEAK_NORMAL_ROUND ) ) | |
minimum_distance = 0; | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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( vec1->x - vec2->x, exc->metrics.x_scale ); | |
vec.y = FT_MulFix( vec1->y - vec2->y, exc->metrics.y_scale ); | |
org_dist = FAST_DUALPROJ( &vec ); | |
} | |
} | |
/* single width cut-in test */ | |
if ( FT_ABS( 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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 > -minimum_distance ) | |
distance = -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, 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
FT_Int B1 = 0; /* pacify compiler */ | |
FT_Int B2 = 0; | |
FT_Bool reverse_move = FALSE; | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
/* 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
/* 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
/* do cvt cut-in always in MIRP for sph */ | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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 > -minimum_distance ) | |
distance = -minimum_distance; | |
} | |
} | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING ) | |
{ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
exc->func_move( exc, &exc->zp1, point, distance - cur_dist ); | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING ) | |
{ | |
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, -( distance - cur_dist ) ); | |
} | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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, -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 = exc->zp0.cur[b1].x - exc->zp0.cur[b0].x; | |
dby = exc->zp0.cur[b1].y - exc->zp0.cur[b0].y; | |
dax = exc->zp1.cur[a1].x - exc->zp1.cur[a0].x; | |
day = exc->zp1.cur[a1].y - exc->zp1.cur[a0].y; | |
dx = exc->zp0.cur[b0].x - exc->zp1.cur[a0].x; | |
dy = exc->zp0.cur[b0].y - exc->zp1.cur[a0].y; | |
discriminant = FT_MulDiv( dax, -dby, 0x40 ) + | |
FT_MulDiv( day, dbx, 0x40 ); | |
dotproduct = 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 ( 19 * FT_ABS( discriminant ) > FT_ABS( dotproduct ) ) | |
{ | |
val = FT_MulDiv( dx, -dby, 0x40 ) + FT_MulDiv( dy, dbx, 0x40 ); | |
R.x = FT_MulDiv( val, dax, discriminant ); | |
R.y = FT_MulDiv( val, day, discriminant ); | |
exc->zp2.cur[point].x = exc->zp1.cur[a0].x + R.x; | |
exc->zp2.cur[point].y = exc->zp1.cur[a0].y + R.y; | |
} | |
else | |
{ | |
/* else, take the middle of the middles of A and B */ | |
exc->zp2.cur[point].x = ( exc->zp1.cur[a0].x + | |
exc->zp1.cur[a1].x + | |
exc->zp0.cur[b0].x + | |
exc->zp0.cur[b1].x ) / 4; | |
exc->zp2.cur[point].y = ( exc->zp1.cur[a0].y + | |
exc->zp1.cur[a1].y + | |
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, -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( exc->zp1.orus[exc->GS.rp2].x - orus_base->x, | |
exc->metrics.x_scale ); | |
vec.y = FT_MulFix( 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( exc->zp2.orus[point].x - orus_base->x, | |
exc->metrics.x_scale ); | |
vec.y = FT_MulFix( 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, | |
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 = worker->curs[p].x - worker->orgs[p].x; | |
if ( dx != 0 ) | |
{ | |
for ( i = p1; i < p; i++ ) | |
worker->curs[i].x += dx; | |
for ( i = p + 1; i <= p2; i++ ) | |
worker->curs[i].x += dx; | |
} | |
} | |
static void | |
_iup_worker_interpolate( IUP_Worker worker, | |
FT_UInt p1, | |
FT_UInt p2, | |
FT_UInt ref1, | |
FT_UInt ref2 ) | |
{ | |
FT_UInt i; | |
FT_F26Dot6 orus1, orus2, org1, org2, 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 = cur1 - org1; | |
delta2 = 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 += delta1; | |
else if ( x >= org2 ) | |
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 += delta1; | |
else if ( x >= org2 ) | |
x += delta2; | |
else | |
{ | |
if ( !scale_valid ) | |
{ | |
scale_valid = 1; | |
scale = FT_DivFix( cur2 - cur1, orus2 - orus1 ); | |
} | |
x = cur1 + FT_MulFix( worker->orus[i].x - orus1, scale ); | |
} | |
worker->curs[i].x = x; | |
} | |
} | |
} | |
/*************************************************************************/ | |
/* */ | |
/* IUP[a]: Interpolate Untouched Points */ | |
/* Opcode range: 0x30-0x31 */ | |
/* Stack: --> */ | |
/* */ | |
static void | |
Ins_IUP( 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 */ | |
/* 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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
exc->ignore_x_mode ) | |
{ | |
exc->iup_called = TRUE; | |
if ( exc->sph_tweak_flags & SPH_TWEAK_SKIP_IUP ) | |
return; | |
} | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
FT_UShort B1, B2; | |
if ( SUBPIXEL_HINTING && | |
exc->ignore_x_mode && | |
exc->iup_called && | |
( exc->sph_tweak_flags & SPH_TWEAK_NO_DELTAP_AFTER_IUP ) ) | |
goto Fail; | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
/* Delta hinting is covered by US Patent 5159668. */ | |
if ( exc->face->unpatented_hinting ) | |
{ | |
FT_Long n = args[0] * 2; | |
if ( exc->args < n ) | |
{ | |
if ( exc->pedantic_hinting ) | |
exc->error = FT_THROW( Too_Few_Arguments ); | |
n = exc->args; | |
} | |
exc->args -= n; | |
exc->new_top = exc->args; | |
return; | |
} | |
#endif | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING ) | |
{ | |
/* | |
* 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, -B ); | |
} | |
} | |
else | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
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; | |
#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING | |
/* Delta hinting is covered by US Patent 5159668. */ | |
if ( exc->face->unpatented_hinting ) | |
{ | |
FT_Long n = args[0] * 2; | |
if ( exc->args < n ) | |
{ | |
if ( exc->pedantic_hinting ) | |
exc->error = FT_THROW( Too_Few_Arguments ); | |
n = exc->args; | |
} | |
exc->args -= n; | |
exc->new_top = exc->args; | |
return; | |
} | |
#endif | |
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; | |
K = 0; | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
/********************************/ | |
/* RASTERIZER VERSION */ | |
/* Selector Bit: 0 */ | |
/* Return Bit(s): 0-7 */ | |
/* */ | |
if ( SUBPIXEL_HINTING && | |
( args[0] & 1 ) != 0 && | |
exc->subpixel_hinting ) | |
{ | |
if ( exc->ignore_x_mode ) | |
{ | |
/* if in ClearType backwards 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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
if ( ( args[0] & 1 ) != 0 ) | |
K = TT_INTERPRETER_VERSION_35; | |
/********************************/ | |
/* GLYPH ROTATED */ | |
/* Selector Bit: 1 */ | |
/* Return Bit(s): 8 */ | |
/* */ | |
if ( ( args[0] & 2 ) != 0 && exc->tt_metrics.rotated ) | |
K |= 0x80; | |
/********************************/ | |
/* GLYPH STRETCHED */ | |
/* Selector Bit: 2 */ | |
/* Return Bit(s): 9 */ | |
/* */ | |
if ( ( args[0] & 4 ) != 0 && exc->tt_metrics.stretched ) | |
K |= 1 << 8; | |
/********************************/ | |
/* HINTING FOR GRAYSCALE */ | |
/* Selector Bit: 5 */ | |
/* Return Bit(s): 12 */ | |
/* */ | |
if ( ( args[0] & 32 ) != 0 && exc->grayscale ) | |
K |= 1 << 12; | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING && | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
args[0] = K; | |
} | |
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_Long ins_counter = 0; /* executed instructions counter */ | |
FT_UShort i; | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING | |
exc->iup_called = FALSE; | |
#endif /* TT_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
/* 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; | |
} | |
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_CONFIG_OPTION_SUBPIXEL_HINTING | |
if ( SUBPIXEL_HINTING ) | |
{ | |
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_CONFIG_OPTION_SUBPIXEL_HINTING */ | |
{ | |
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: /* ???? */ | |
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: | |
Ins_UNKNOWN( exc ); | |
break; | |
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_: | |
return FT_Err_Ok; | |
LErrorCodeOverflow_: | |
exc->error = FT_THROW( Code_Overflow ); | |
LErrorLabel_: | |
/* If any errors have occurred, function tables may be broken. */ | |
/* Force a re-execution of `prep' and `fpgm' tables if no */ | |
/* bytecode debugger is run. */ | |
if ( exc->error && | |
!exc->instruction_trap && | |
exc->curRange == tt_coderange_glyph ) | |
{ | |
FT_TRACE1(( " The interpreter returned error 0x%x\n", exc->error )); | |
exc->size->bytecode_ready = -1; | |
exc->size->cvt_ready = -1; | |
} | |
return exc->error; | |
} | |
#endif /* TT_USE_BYTECODE_INTERPRETER */ | |
/* END */ |