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