freetype2/src/truetype/ttinterp.c

7756 lines
187 KiB
C
Raw Normal View History

/****************************************************************************
*
* ttinterp.c
*
* TrueType bytecode interpreter (body).
*
2023-01-17 09:18:25 +01:00
* Copyright (C) 1996-2023 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.
*
*/
1999-12-17 00:11:37 +01:00
2000-12-08 17:17:16 +01:00
/* Greg Hitchcock from Microsoft has helped a lot in resolving unclear */
/* issues; many thanks! */
#include <freetype/internal/ftdebug.h>
#include <freetype/internal/ftcalc.h>
#include <freetype/fttrigon.h>
#include <freetype/ftsystem.h>
#include <freetype/ftdriver.h>
#include <freetype/ftmm.h>
Complete redesign of error codes. Please check ftmoderr.h for more details. * include/freetype/internal/cfferrs.h, include/freetype/internal/tterrors.h, include/freetype/internal/t1errors.h: Removed. Replaced with files local to the module. All extra error codes have been moved to `fterrors.h'. * src/sfnt/ttpost.h: Move error codes to `fterrors.h'. * src/autohint/aherrors.h, src/cache/ftcerror.h, src/cff/cfferrs.h, src/cid/ciderrs.h, src/pcf/pcferror.h, src/psaux/psauxerr.h, src/psnames/psnamerr.h, src/raster/rasterrs.h, src/sfnt/sferrors.h, src/smooth/ftsmerrs.h, src/truetype/tterrors.h, src/type1/t1errors.h, src/winfonts/fnterrs.h: New files defining the error names for the module it belongs to. * include/freetype/ftmoderr.h: New file, defining the module error offsets. Its structure is similar to `fterrors.h'. * include/freetype/fterrors.h (FT_NOERRORDEF): New macro. (FT_ERRORDEF): Redefined to use module error offsets. All internal error codes are now public; unused error codes have been removed, some are new. * include/freetype/config/ftheader.h (FT_MODULE_ERRORS_H): New macro. * include/freetype/config/ftoption.h (FT_CONFIG_OPTION_USE_MODULE_ERRORS): New macro. All other source files have been updated to use the new error codes; some already existing (internal) error codes local to a module have been renamed to give them the same name as in the base module. All make files have been updated to include the local error files. * src/cid/cidtokens.h: Replaced with... * src/cid/cidtoken.h: This file for 8+3 consistency. * src/raster/ftraster.c: Use macros for header file names.
2001-06-06 19:30:41 +02:00
#include "ttinterp.h"
Complete redesign of error codes. Please check ftmoderr.h for more details. * include/freetype/internal/cfferrs.h, include/freetype/internal/tterrors.h, include/freetype/internal/t1errors.h: Removed. Replaced with files local to the module. All extra error codes have been moved to `fterrors.h'. * src/sfnt/ttpost.h: Move error codes to `fterrors.h'. * src/autohint/aherrors.h, src/cache/ftcerror.h, src/cff/cfferrs.h, src/cid/ciderrs.h, src/pcf/pcferror.h, src/psaux/psauxerr.h, src/psnames/psnamerr.h, src/raster/rasterrs.h, src/sfnt/sferrors.h, src/smooth/ftsmerrs.h, src/truetype/tterrors.h, src/type1/t1errors.h, src/winfonts/fnterrs.h: New files defining the error names for the module it belongs to. * include/freetype/ftmoderr.h: New file, defining the module error offsets. Its structure is similar to `fterrors.h'. * include/freetype/fterrors.h (FT_NOERRORDEF): New macro. (FT_ERRORDEF): Redefined to use module error offsets. All internal error codes are now public; unused error codes have been removed, some are new. * include/freetype/config/ftheader.h (FT_MODULE_ERRORS_H): New macro. * include/freetype/config/ftoption.h (FT_CONFIG_OPTION_USE_MODULE_ERRORS): New macro. All other source files have been updated to use the new error codes; some already existing (internal) error codes local to a module have been renamed to give them the same name as in the base module. All make files have been updated to include the local error files. * src/cid/cidtokens.h: Replaced with... * src/cid/cidtoken.h: This file for 8+3 consistency. * src/raster/ftraster.c: Use macros for header file names.
2001-06-06 19:30:41 +02:00
#include "tterrors.h"
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include "ttgxvar.h"
#endif
1999-12-17 00:11:37 +01:00
#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.
*/
1999-12-17 00:11:37 +01:00
#undef FT_COMPONENT
#define FT_COMPONENT ttinterp
1999-12-17 00:11:37 +01:00
#define NO_SUBPIXEL_HINTING \
( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \
TT_INTERPRETER_VERSION_35 )
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
#define SUBPIXEL_HINTING_MINIMAL \
( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \
TT_INTERPRETER_VERSION_40 )
#endif
#define PROJECT( v1, v2 ) \
exc->func_project( exc, \
SUB_LONG( (v1)->x, (v2)->x ), \
SUB_LONG( (v1)->y, (v2)->y ) )
#define DUALPROJ( v1, v2 ) \
exc->func_dualproj( exc, \
SUB_LONG( (v1)->x, (v2)->x ), \
SUB_LONG( (v1)->y, (v2)->y ) )
#define FAST_PROJECT( v ) \
exc->func_project( exc, (v)->x, (v)->y )
1999-12-17 00:11:37 +01:00
#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) )
1999-12-17 00:11:37 +01:00
#undef SUCCESS
#define SUCCESS 0
1999-12-17 00:11:37 +01:00
#undef FAILURE
#define FAILURE 1
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* 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 )
2001-06-28 09:17:51 +02:00
TT_Goto_CodeRange( TT_ExecContext exec,
FT_Int range,
FT_Long IP )
1999-12-17 00:11:37 +01:00
{
TT_CodeRange* coderange;
FT_ASSERT( range >= 1 && range <= 3 );
1999-12-17 00:11:37 +01:00
coderange = &exec->codeRangeTable[range - 1];
FT_ASSERT( coderange->base );
1999-12-17 00:11:37 +01:00
/* 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. */
1999-12-17 00:11:37 +01:00
/* */
FT_ASSERT( IP <= coderange->size );
1999-12-17 00:11:37 +01:00
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 )
2001-06-28 09:17:51 +02:00
TT_Set_CodeRange( TT_ExecContext exec,
FT_Int range,
void* base,
FT_Long length )
1999-12-17 00:11:37 +01:00
{
FT_ASSERT( range >= 1 && range <= 3 );
1999-12-17 00:11:37 +01:00
exec->codeRangeTable[range - 1].base = (FT_Byte*)base;
1999-12-17 00:11:37 +01:00
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 )
2001-06-28 09:17:51 +02:00
TT_Clear_CodeRange( TT_ExecContext exec,
FT_Int range )
1999-12-17 00:11:37 +01:00
{
FT_ASSERT( range >= 1 && range <= 3 );
1999-12-17 00:11:37 +01:00
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 )
1999-12-17 00:11:37 +01:00
{
FT_Memory memory = exec->memory;
1999-12-17 00:11:37 +01:00
/* points zone */
exec->maxPoints = 0;
exec->maxContours = 0;
/* free stack */
FT_FREE( exec->stack );
1999-12-17 00:11:37 +01:00
exec->stackSize = 0;
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
/* free glyf cvt working area */
FT_FREE( exec->glyfCvt );
exec->glyfCvtSize = 0;
/* free glyf storage working area */
FT_FREE( exec->glyfStorage );
exec->glyfStoreSize = 0;
1999-12-17 00:11:37 +01:00
/* free call stack */
FT_FREE( exec->callStack );
1999-12-17 00:11:37 +01:00
exec->callSize = 0;
exec->callTop = 0;
/* free glyph code range */
FT_FREE( exec->glyphIns );
1999-12-17 00:11:37 +01:00
exec->glyphSize = 0;
exec->size = NULL;
exec->face = NULL;
FT_FREE( exec );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* @Function:
* TT_Load_Context
*
* @Description:
* Prepare an execution context for glyph hinting.
*
* @Input:
* face ::
* A handle to the source face object.
*
* size ::
* A handle to the source size object.
*
* @InOut:
* exec ::
* A handle to the target execution context.
*
* @Return:
* FreeType error code. 0 means success.
*
* @Note:
* Only the glyph loader and debugger should call this function.
*
* Note that not all members of `TT_ExecContext` get initialized.
*/
FT_LOCAL_DEF( FT_Error )
2001-06-28 09:17:51 +02:00
TT_Load_Context( TT_ExecContext exec,
TT_Face face,
TT_Size size )
1999-12-17 00:11:37 +01:00
{
FT_Int i;
1999-12-17 00:11:37 +01:00
TT_MaxProfile* maxp;
FT_Error error;
FT_Memory memory = exec->memory;
1999-12-17 00:11:37 +01:00
1999-12-17 00:11:37 +01:00
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;
1999-12-17 00:11:37 +01:00
exec->tt_metrics = size->ttmetrics;
exec->metrics = *size->metrics;
1999-12-17 00:11:37 +01:00
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;
1999-12-17 00:11:37 +01:00
}
/* XXX: We reserve a little more elements on the stack to deal safely */
/* with broken fonts like arialbs, courbs, timesbs, etc. */
if ( FT_QRENEW_ARRAY( exec->stack,
exec->stackSize,
maxp->maxStackElements + 32 ) )
1999-12-17 00:11:37 +01:00
return error;
exec->stackSize = maxp->maxStackElements + 32;
1999-12-17 00:11:37 +01:00
/* free previous glyph code range */
FT_FREE( exec->glyphIns );
exec->glyphSize = 0;
1999-12-17 00:11:37 +01:00
exec->pts.n_points = 0;
exec->pts.n_contours = 0;
exec->zp1 = exec->pts;
exec->zp2 = exec->pts;
exec->zp0 = exec->pts;
1999-12-17 00:11:37 +01:00
exec->instruction_trap = FALSE;
return FT_Err_Ok;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* @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 )
2001-06-28 09:17:51 +02:00
TT_Save_Context( TT_ExecContext exec,
TT_Size size )
1999-12-17 00:11:37 +01:00
{
FT_Int i;
1999-12-17 00:11:37 +01:00
/* XXX: Will probably disappear soon with all the code range */
/* management, which is now rather obsolete. */
/* */
1999-12-17 00:11:37 +01:00
size->num_function_defs = exec->numFDefs;
size->num_instruction_defs = exec->numIDefs;
size->max_func = exec->maxFunc;
size->max_ins = exec->maxIns;
for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
size->codeRangeTable[i] = exec->codeRangeTable[i];
}
/**************************************************************************
*
* @Function:
* TT_Run_Context
*
* @Description:
* Executes one or more instructions in the execution context.
*
* @Input:
* exec ::
* A handle to the target execution context.
*
* @Return:
* TrueType error code. 0 means success.
*/
FT_LOCAL_DEF( FT_Error )
TT_Run_Context( TT_ExecContext exec )
1999-12-17 00:11:37 +01:00
{
TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 );
1999-12-17 00:11:37 +01:00
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. */
1999-12-17 00:11:37 +01:00
const TT_GraphicsState tt_default_graphics_state =
{
0, 0, 0,
{ 0x4000, 0 },
{ 0x4000, 0 },
{ 0x4000, 0 },
1999-12-17 00:11:37 +01:00
1, 64, 1,
TRUE, 68, 0, 0, 9, 3,
0, FALSE, 0, 1, 1, 1
1999-12-17 00:11:37 +01:00
};
/* documentation is in ttinterp.h */
2001-06-28 09:17:51 +02:00
FT_EXPORT_DEF( TT_ExecContext )
TT_New_Context( TT_Driver driver )
1999-12-17 00:11:37 +01:00
{
FT_Memory memory;
FT_Error error;
TT_ExecContext exec = NULL;
if ( !driver )
goto Fail;
memory = driver->root.root.memory;
/* allocate object and zero everything inside */
if ( FT_NEW( exec ) )
goto Fail;
1999-12-17 00:11:37 +01:00
/* create callStack here, other allocations delayed */
exec->memory = memory;
exec->callSize = 32;
1999-12-17 00:11:37 +01:00
if ( FT_QNEW_ARRAY( exec->callStack, exec->callSize ) )
FT_FREE( exec );
1999-12-17 00:11:37 +01:00
Fail:
return exec;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* 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.
*
*/
1999-12-17 00:11:37 +01:00
#undef PACK
#define PACK( x, y ) ( ( x << 4 ) | y )
1999-12-17 00:11:37 +01:00
static
const FT_Byte Pop_Push_Count[256] =
1999-12-17 00:11:37 +01:00
{
/* opcodes are gathered in groups of 16 */
/* please keep the spaces as they are */
/* 0x00 */
/* SVTCA[0] */ PACK( 0, 0 ),
/* SVTCA[1] */ PACK( 0, 0 ),
/* SPVTCA[0] */ PACK( 0, 0 ),
/* SPVTCA[1] */ PACK( 0, 0 ),
/* SFVTCA[0] */ PACK( 0, 0 ),
/* SFVTCA[1] */ PACK( 0, 0 ),
/* SPVTL[0] */ PACK( 2, 0 ),
/* SPVTL[1] */ PACK( 2, 0 ),
/* SFVTL[0] */ PACK( 2, 0 ),
/* SFVTL[1] */ PACK( 2, 0 ),
/* SPVFS */ PACK( 2, 0 ),
/* SFVFS */ PACK( 2, 0 ),
/* GPV */ PACK( 0, 2 ),
/* GFV */ PACK( 0, 2 ),
/* SFVTPV */ PACK( 0, 0 ),
1999-12-17 00:11:37 +01:00
/* ISECT */ PACK( 5, 0 ),
/* 0x10 */
1999-12-17 00:11:37 +01:00
/* 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 ),
1999-12-17 00:11:37 +01:00
/* SSW */ PACK( 1, 0 ),
/* 0x20 */
1999-12-17 00:11:37 +01:00
/* 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 ),
1999-12-17 00:11:37 +01:00
/* INS_$28 */ PACK( 0, 0 ),
/* UTP */ PACK( 1, 0 ),
/* LOOPCALL */ PACK( 2, 0 ),
/* CALL */ PACK( 1, 0 ),
/* FDEF */ PACK( 1, 0 ),
/* ENDF */ PACK( 0, 0 ),
/* MDAP[0] */ PACK( 1, 0 ),
/* MDAP[1] */ PACK( 1, 0 ),
/* 0x30 */
1999-12-17 00:11:37 +01:00
/* IUP[0] */ PACK( 0, 0 ),
/* IUP[1] */ PACK( 0, 0 ),
/* SHP[0] */ PACK( 0, 0 ), /* loops */
/* SHP[1] */ PACK( 0, 0 ), /* loops */
1999-12-17 00:11:37 +01:00
/* 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 */
1999-12-17 00:11:37 +01:00
/* MSIRP[0] */ PACK( 2, 0 ),
/* MSIRP[1] */ PACK( 2, 0 ),
/* ALIGNRP */ PACK( 0, 0 ), /* loops */
1999-12-17 00:11:37 +01:00
/* RTDG */ PACK( 0, 0 ),
/* MIAP[0] */ PACK( 2, 0 ),
/* MIAP[1] */ PACK( 2, 0 ),
/* 0x40 */
/* NPUSHB */ PACK( 0, 0 ),
/* NPUSHW */ PACK( 0, 0 ),
1999-12-17 00:11:37 +01:00
/* WS */ PACK( 2, 0 ),
/* RS */ PACK( 1, 1 ),
/* WCVTP */ PACK( 2, 0 ),
/* RCVT */ PACK( 1, 1 ),
1999-12-17 00:11:37 +01:00
/* 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 ),
1999-12-17 00:11:37 +01:00
/* DEBUG */ PACK( 1, 0 ),
/* 0x50 */
1999-12-17 00:11:37 +01:00
/* 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 ),
1999-12-17 00:11:37 +01:00
/* SDB */ PACK( 1, 0 ),
/* SDS */ PACK( 1, 0 ),
/* 0x60 */
1999-12-17 00:11:37 +01:00
/* ADD */ PACK( 2, 1 ),
/* SUB */ PACK( 2, 1 ),
/* DIV */ PACK( 2, 1 ),
/* MUL */ PACK( 2, 1 ),
/* ABS */ PACK( 1, 1 ),
/* NEG */ PACK( 1, 1 ),
/* FLOOR */ PACK( 1, 1 ),
/* CEILING */ PACK( 1, 1 ),
/* ROUND[0] */ PACK( 1, 1 ),
/* ROUND[1] */ PACK( 1, 1 ),
/* ROUND[2] */ PACK( 1, 1 ),
/* ROUND[3] */ PACK( 1, 1 ),
/* NROUND[0] */ PACK( 1, 1 ),
/* NROUND[1] */ PACK( 1, 1 ),
/* NROUND[2] */ PACK( 1, 1 ),
/* NROUND[3] */ PACK( 1, 1 ),
/* 0x70 */
/* WCVTF */ PACK( 2, 0 ),
/* DELTAP2 */ PACK( 1, 0 ),
/* DELTAP3 */ PACK( 1, 0 ),
/* DELTAC1 */ PACK( 1, 0 ),
/* DELTAC2 */ PACK( 1, 0 ),
/* DELTAC3 */ PACK( 1, 0 ),
1999-12-17 00:11:37 +01:00
/* SROUND */ PACK( 1, 0 ),
/* S45ROUND */ PACK( 1, 0 ),
1999-12-17 00:11:37 +01:00
/* JROT */ PACK( 2, 0 ),
/* JROF */ PACK( 2, 0 ),
/* ROFF */ PACK( 0, 0 ),
/* INS_$7B */ PACK( 0, 0 ),
/* RUTG */ PACK( 0, 0 ),
/* RDTG */ PACK( 0, 0 ),
/* SANGW */ PACK( 1, 0 ),
/* AA */ PACK( 1, 0 ),
/* 0x80 */
/* FLIPPT */ PACK( 0, 0 ), /* loops */
/* FLIPRGON */ PACK( 2, 0 ),
/* FLIPRGOFF */ PACK( 2, 0 ),
1999-12-17 00:11:37 +01:00
/* 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 ),
1999-12-17 00:11:37 +01:00
/* IDEF */ PACK( 1, 0 ),
/* ROLL */ PACK( 3, 3 ),
/* MAX */ PACK( 2, 1 ),
/* MIN */ PACK( 2, 1 ),
/* SCANTYPE */ PACK( 1, 0 ),
/* INSTCTRL */ PACK( 2, 0 ),
1999-12-17 00:11:37 +01:00
/* INS_$8F */ PACK( 0, 0 ),
/* 0x90 */
1999-12-17 00:11:37 +01:00
/* INS_$90 */ PACK( 0, 0 ),
/* GETVAR */ PACK( 0, 0 ), /* will be handled specially */
/* GETDATA */ PACK( 0, 1 ),
1999-12-17 00:11:37 +01:00
/* INS_$93 */ PACK( 0, 0 ),
/* INS_$94 */ PACK( 0, 0 ),
/* INS_$95 */ PACK( 0, 0 ),
/* INS_$96 */ PACK( 0, 0 ),
/* INS_$97 */ PACK( 0, 0 ),
/* INS_$98 */ PACK( 0, 0 ),
/* INS_$99 */ PACK( 0, 0 ),
/* INS_$9A */ PACK( 0, 0 ),
/* INS_$9B */ PACK( 0, 0 ),
/* INS_$9C */ PACK( 0, 0 ),
/* INS_$9D */ PACK( 0, 0 ),
/* INS_$9E */ PACK( 0, 0 ),
/* INS_$9F */ PACK( 0, 0 ),
/* 0xA0 */
1999-12-17 00:11:37 +01:00
/* INS_$A0 */ PACK( 0, 0 ),
/* INS_$A1 */ PACK( 0, 0 ),
/* INS_$A2 */ PACK( 0, 0 ),
/* INS_$A3 */ PACK( 0, 0 ),
/* INS_$A4 */ PACK( 0, 0 ),
/* INS_$A5 */ PACK( 0, 0 ),
/* INS_$A6 */ PACK( 0, 0 ),
/* INS_$A7 */ PACK( 0, 0 ),
/* INS_$A8 */ PACK( 0, 0 ),
/* INS_$A9 */ PACK( 0, 0 ),
/* INS_$AA */ PACK( 0, 0 ),
/* INS_$AB */ PACK( 0, 0 ),
/* INS_$AC */ PACK( 0, 0 ),
/* INS_$AD */ PACK( 0, 0 ),
/* INS_$AE */ PACK( 0, 0 ),
/* INS_$AF */ PACK( 0, 0 ),
/* 0xB0 */
/* PUSHB[0] */ PACK( 0, 1 ),
/* PUSHB[1] */ PACK( 0, 2 ),
/* PUSHB[2] */ PACK( 0, 3 ),
/* PUSHB[3] */ PACK( 0, 4 ),
/* PUSHB[4] */ PACK( 0, 5 ),
/* PUSHB[5] */ PACK( 0, 6 ),
/* PUSHB[6] */ PACK( 0, 7 ),
/* PUSHB[7] */ PACK( 0, 8 ),
/* PUSHW[0] */ PACK( 0, 1 ),
/* PUSHW[1] */ PACK( 0, 2 ),
/* PUSHW[2] */ PACK( 0, 3 ),
/* PUSHW[3] */ PACK( 0, 4 ),
/* PUSHW[4] */ PACK( 0, 5 ),
/* PUSHW[5] */ PACK( 0, 6 ),
/* PUSHW[6] */ PACK( 0, 7 ),
/* PUSHW[7] */ PACK( 0, 8 ),
/* 0xC0 */
1999-12-17 00:11:37 +01:00
/* MDRP[00] */ PACK( 1, 0 ),
/* MDRP[01] */ PACK( 1, 0 ),
/* MDRP[02] */ PACK( 1, 0 ),
/* MDRP[03] */ PACK( 1, 0 ),
/* MDRP[04] */ PACK( 1, 0 ),
/* MDRP[05] */ PACK( 1, 0 ),
/* MDRP[06] */ PACK( 1, 0 ),
/* MDRP[07] */ PACK( 1, 0 ),
/* MDRP[08] */ PACK( 1, 0 ),
/* MDRP[09] */ PACK( 1, 0 ),
/* MDRP[10] */ PACK( 1, 0 ),
/* MDRP[11] */ PACK( 1, 0 ),
/* MDRP[12] */ PACK( 1, 0 ),
/* MDRP[13] */ PACK( 1, 0 ),
/* MDRP[14] */ PACK( 1, 0 ),
/* MDRP[15] */ PACK( 1, 0 ),
/* 0xD0 */
1999-12-17 00:11:37 +01:00
/* MDRP[16] */ PACK( 1, 0 ),
/* MDRP[17] */ PACK( 1, 0 ),
/* MDRP[18] */ PACK( 1, 0 ),
/* MDRP[19] */ PACK( 1, 0 ),
/* MDRP[20] */ PACK( 1, 0 ),
/* MDRP[21] */ PACK( 1, 0 ),
/* MDRP[22] */ PACK( 1, 0 ),
/* MDRP[23] */ PACK( 1, 0 ),
/* MDRP[24] */ PACK( 1, 0 ),
/* MDRP[25] */ PACK( 1, 0 ),
/* MDRP[26] */ PACK( 1, 0 ),
/* MDRP[27] */ PACK( 1, 0 ),
/* MDRP[28] */ PACK( 1, 0 ),
/* MDRP[29] */ PACK( 1, 0 ),
/* MDRP[30] */ PACK( 1, 0 ),
/* MDRP[31] */ PACK( 1, 0 ),
/* 0xE0 */
1999-12-17 00:11:37 +01:00
/* MIRP[00] */ PACK( 2, 0 ),
/* MIRP[01] */ PACK( 2, 0 ),
/* MIRP[02] */ PACK( 2, 0 ),
/* MIRP[03] */ PACK( 2, 0 ),
/* MIRP[04] */ PACK( 2, 0 ),
/* MIRP[05] */ PACK( 2, 0 ),
/* MIRP[06] */ PACK( 2, 0 ),
/* MIRP[07] */ PACK( 2, 0 ),
/* MIRP[08] */ PACK( 2, 0 ),
/* MIRP[09] */ PACK( 2, 0 ),
/* MIRP[10] */ PACK( 2, 0 ),
/* MIRP[11] */ PACK( 2, 0 ),
/* MIRP[12] */ PACK( 2, 0 ),
/* MIRP[13] */ PACK( 2, 0 ),
/* MIRP[14] */ PACK( 2, 0 ),
/* MIRP[15] */ PACK( 2, 0 ),
/* 0xF0 */
1999-12-17 00:11:37 +01:00
/* MIRP[16] */ PACK( 2, 0 ),
/* MIRP[17] */ PACK( 2, 0 ),
/* MIRP[18] */ PACK( 2, 0 ),
/* MIRP[19] */ PACK( 2, 0 ),
/* MIRP[20] */ PACK( 2, 0 ),
/* MIRP[21] */ PACK( 2, 0 ),
/* MIRP[22] */ PACK( 2, 0 ),
/* MIRP[23] */ PACK( 2, 0 ),
/* MIRP[24] */ PACK( 2, 0 ),
/* MIRP[25] */ PACK( 2, 0 ),
/* MIRP[26] */ PACK( 2, 0 ),
/* MIRP[27] */ PACK( 2, 0 ),
/* MIRP[28] */ PACK( 2, 0 ),
/* MIRP[29] */ PACK( 2, 0 ),
/* MIRP[30] */ PACK( 2, 0 ),
/* MIRP[31] */ PACK( 2, 0 )
};
#ifdef FT_DEBUG_LEVEL_TRACE
/* the first hex digit gives the length of the opcode name; the space */
/* after the digit is here just to increase readability of the source */
/* code */
static
const char* const opcode_name[256] =
{
/* 0x00 */
"8 SVTCA[y]",
"8 SVTCA[x]",
"9 SPVTCA[y]",
"9 SPVTCA[x]",
"9 SFVTCA[y]",
"9 SFVTCA[x]",
"9 SPVTL[||]",
"8 SPVTL[+]",
"9 SFVTL[||]",
"8 SFVTL[+]",
"5 SPVFS",
"5 SFVFS",
"3 GPV",
"3 GFV",
"6 SFVTPV",
"5 ISECT",
/* 0x10 */
"4 SRP0",
"4 SRP1",
"4 SRP2",
"4 SZP0",
"4 SZP1",
"4 SZP2",
"4 SZPS",
"5 SLOOP",
"3 RTG",
"4 RTHG",
"3 SMD",
"4 ELSE",
"4 JMPR",
"6 SCVTCI",
"5 SSWCI",
"3 SSW",
/* 0x20 */
"3 DUP",
"3 POP",
"5 CLEAR",
"4 SWAP",
"5 DEPTH",
"6 CINDEX",
"6 MINDEX",
"8 ALIGNPTS",
"7 INS_$28",
"3 UTP",
"8 LOOPCALL",
"4 CALL",
"4 FDEF",
"4 ENDF",
"6 MDAP[]",
"9 MDAP[rnd]",
/* 0x30 */
"6 IUP[y]",
"6 IUP[x]",
"8 SHP[rp2]",
"8 SHP[rp1]",
"8 SHC[rp2]",
"8 SHC[rp1]",
"8 SHZ[rp2]",
"8 SHZ[rp1]",
"5 SHPIX",
"2 IP",
"7 MSIRP[]",
"A MSIRP[rp0]",
"7 ALIGNRP",
"4 RTDG",
"6 MIAP[]",
"9 MIAP[rnd]",
/* 0x40 */
"6 NPUSHB",
"6 NPUSHW",
"2 WS",
"2 RS",
"5 WCVTP",
"4 RCVT",
"8 GC[curr]",
"8 GC[orig]",
"4 SCFS",
"8 MD[curr]",
"8 MD[orig]",
"5 MPPEM",
"3 MPS",
"6 FLIPON",
"7 FLIPOFF",
"5 DEBUG",
/* 0x50 */
"2 LT",
"4 LTEQ",
"2 GT",
"4 GTEQ",
"2 EQ",
"3 NEQ",
"3 ODD",
"4 EVEN",
"2 IF",
"3 EIF",
"3 AND",
"2 OR",
"3 NOT",
"7 DELTAP1",
"3 SDB",
"3 SDS",
/* 0x60 */
"3 ADD",
"3 SUB",
"3 DIV",
"3 MUL",
"3 ABS",
"3 NEG",
"5 FLOOR",
"7 CEILING",
"8 ROUND[G]",
"8 ROUND[B]",
"8 ROUND[W]",
"7 ROUND[]",
"9 NROUND[G]",
"9 NROUND[B]",
"9 NROUND[W]",
"8 NROUND[]",
/* 0x70 */
"5 WCVTF",
"7 DELTAP2",
"7 DELTAP3",
"7 DELTAC1",
"7 DELTAC2",
"7 DELTAC3",
"6 SROUND",
"8 S45ROUND",
"4 JROT",
"4 JROF",
"4 ROFF",
"7 INS_$7B",
"4 RUTG",
"4 RDTG",
"5 SANGW",
"2 AA",
/* 0x80 */
"6 FLIPPT",
"8 FLIPRGON",
"9 FLIPRGOFF",
"7 INS_$83",
"7 INS_$84",
"8 SCANCTRL",
"A SDPVTL[||]",
"9 SDPVTL[+]",
"7 GETINFO",
"4 IDEF",
"4 ROLL",
"3 MAX",
"3 MIN",
"8 SCANTYPE",
"8 INSTCTRL",
"7 INS_$8F",
/* 0x90 */
"7 INS_$90",
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
"C GETVARIATION",
"7 GETDATA",
#else
"7 INS_$91",
"7 INS_$92",
#endif
"7 INS_$93",
"7 INS_$94",
"7 INS_$95",
"7 INS_$96",
"7 INS_$97",
"7 INS_$98",
"7 INS_$99",
"7 INS_$9A",
"7 INS_$9B",
"7 INS_$9C",
"7 INS_$9D",
"7 INS_$9E",
"7 INS_$9F",
/* 0xA0 */
"7 INS_$A0",
"7 INS_$A1",
"7 INS_$A2",
"7 INS_$A3",
"7 INS_$A4",
"7 INS_$A5",
"7 INS_$A6",
"7 INS_$A7",
"7 INS_$A8",
"7 INS_$A9",
"7 INS_$AA",
"7 INS_$AB",
"7 INS_$AC",
"7 INS_$AD",
"7 INS_$AE",
"7 INS_$AF",
/* 0xB0 */
"8 PUSHB[0]",
"8 PUSHB[1]",
"8 PUSHB[2]",
"8 PUSHB[3]",
"8 PUSHB[4]",
"8 PUSHB[5]",
"8 PUSHB[6]",
"8 PUSHB[7]",
"8 PUSHW[0]",
"8 PUSHW[1]",
"8 PUSHW[2]",
"8 PUSHW[3]",
"8 PUSHW[4]",
"8 PUSHW[5]",
"8 PUSHW[6]",
"8 PUSHW[7]",
/* 0xC0 */
"7 MDRP[G]",
"7 MDRP[B]",
"7 MDRP[W]",
"6 MDRP[]",
"8 MDRP[rG]",
"8 MDRP[rB]",
"8 MDRP[rW]",
"7 MDRP[r]",
"8 MDRP[mG]",
"8 MDRP[mB]",
"8 MDRP[mW]",
"7 MDRP[m]",
"9 MDRP[mrG]",
"9 MDRP[mrB]",
"9 MDRP[mrW]",
"8 MDRP[mr]",
/* 0xD0 */
2018-08-04 07:03:57 +02:00
"8 MDRP[pG]",
"8 MDRP[pB]",
"8 MDRP[pW]",
"7 MDRP[p]",
2018-08-04 07:03:57 +02:00
"9 MDRP[prG]",
"9 MDRP[prB]",
"9 MDRP[prW]",
"8 MDRP[pr]",
2018-08-04 07:03:57 +02:00
"9 MDRP[pmG]",
"9 MDRP[pmB]",
"9 MDRP[pmW]",
"8 MDRP[pm]",
2018-08-04 07:03:57 +02:00
"A MDRP[pmrG]",
"A MDRP[pmrB]",
"A MDRP[pmrW]",
"9 MDRP[pmr]",
/* 0xE0 */
"7 MIRP[G]",
"7 MIRP[B]",
"7 MIRP[W]",
"6 MIRP[]",
"8 MIRP[rG]",
"8 MIRP[rB]",
"8 MIRP[rW]",
"7 MIRP[r]",
"8 MIRP[mG]",
"8 MIRP[mB]",
"8 MIRP[mW]",
"7 MIRP[m]",
"9 MIRP[mrG]",
"9 MIRP[mrB]",
"9 MIRP[mrW]",
"8 MIRP[mr]",
/* 0xF0 */
"8 MIRP[pG]",
"8 MIRP[pB]",
"8 MIRP[pW]",
"7 MIRP[p]",
"9 MIRP[prG]",
"9 MIRP[prB]",
"9 MIRP[prW]",
"8 MIRP[pr]",
"9 MIRP[pmG]",
"9 MIRP[pmB]",
"9 MIRP[pmW]",
"8 MIRP[pm]",
"A MIRP[pmrG]",
"A MIRP[pmrB]",
"A MIRP[pmrW]",
"9 MIRP[pmr]"
};
#endif /* FT_DEBUG_LEVEL_TRACE */
1999-12-17 00:11:37 +01:00
static
const FT_Char opcode_length[256] =
1999-12-17 00:11:37 +01:00
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 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,
1999-12-17 00:11:37 +01:00
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 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
1999-12-17 00:11:37 +01:00
#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER
#if defined( __arm__ ) && \
( defined( __thumb2__ ) || !defined( __thumb__ ) )
#define TT_MulFix14 TT_MulFix14_arm
2007-02-16 09:10:17 +01:00
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 )
2007-02-16 09:10:17 +01:00
{
FT_Int32 sign;
FT_UInt32 ah, al, mid, lo, hi;
2007-02-16 09:10:17 +01:00
sign = a ^ b;
2007-02-16 09:10:17 +01:00
if ( a < 0 )
a = -a;
if ( b < 0 )
b = -b;
2007-02-16 09:10:17 +01:00
ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU );
al = (FT_UInt32)( a & 0xFFFFU );
2007-02-16 09:10:17 +01:00
lo = al * b;
mid = ah * b;
hi = mid >> 16;
mid = ( mid << 16 ) + ( 1 << 13 ); /* rounding */
lo += mid;
if ( lo < mid )
hi += 1;
2007-02-16 09:10:17 +01:00
mid = ( lo >> 14 ) | ( hi << 18 );
return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid;
}
#endif /* !TT_MulFix14 */
2007-02-16 09:10:17 +01:00
#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;
2002-03-25 18:02:26 +01:00
temp1 += temp2;
temp2 = temp1 >> 63;
temp1 += 0x2000 + temp2;
return (FT_Int32)( temp1 >> 14 );
2002-03-25 18:02:26 +01:00
}
#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
#pragma GCC diagnostic pop
#endif
#endif /* __GNUC__ && (__arm__ || __i386__ || __x86_64__) */
#ifndef TT_DotFix14
2002-03-25 18:02:26 +01:00
/* compute (ax*bx+ay*by)/2^14 with maximum accuracy and rounding */
2002-03-25 18:02:26 +01:00
static FT_Int32
TT_DotFix14( FT_Int32 ax,
FT_Int32 ay,
FT_Int bx,
FT_Int by )
2002-03-25 18:02:26 +01:00
{
FT_Int32 m, s, hi1, hi2, hi;
FT_UInt32 l, lo1, lo2, lo;
2002-03-25 18:02:26 +01:00
/* 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 );
2002-03-25 18:02:26 +01:00
/* 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 );
2002-03-25 18:02:26 +01:00
/* add them */
lo = lo1 + lo2;
hi = hi1 + hi2 + ( lo < lo1 );
2002-03-25 18:02:26 +01:00
/* divide the result by 2^14 with rounding */
s = hi >> 31;
2002-03-25 18:02:26 +01:00
l = lo + (FT_UInt32)s;
hi += s + ( l < lo );
2002-03-25 18:02:26 +01:00
lo = l;
l = lo + 0x2000U;
hi += ( l < lo );
return (FT_Int32)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) );
2002-03-25 18:02:26 +01:00
}
#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 .
*/
2001-06-28 09:17:51 +02:00
static FT_Long
Current_Ratio( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
if ( !exc->tt_metrics.ratio )
1999-12-17 00:11:37 +01:00
{
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;
1999-12-17 00:11:37 +01:00
else
{
FT_F26Dot6 x, y;
2003-04-29 15:23:47 +02:00
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 );
2003-04-29 15:23:47 +02:00
}
}
return exc->tt_metrics.ratio;
1999-12-17 00:11:37 +01:00
}
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 )
1999-12-17 00:11:37 +01:00
{
return FT_MulFix( exc->tt_metrics.ppem, Current_Ratio( exc ) );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* Functions related to the control value table (CVT).
*
*/
1999-12-17 00:11:37 +01:00
2001-06-28 09:17:51 +02:00
FT_CALLBACK_DEF( FT_F26Dot6 )
Read_CVT( TT_ExecContext exc,
FT_ULong idx )
1999-12-17 00:11:37 +01:00
{
return exc->cvt[idx];
1999-12-17 00:11:37 +01:00
}
2001-06-28 09:17:51 +02:00
FT_CALLBACK_DEF( FT_F26Dot6 )
Read_CVT_Stretched( TT_ExecContext exc,
FT_ULong idx )
1999-12-17 00:11:37 +01:00
{
return FT_MulFix( exc->cvt[idx], Current_Ratio( exc ) );
1999-12-17 00:11:37 +01:00
}
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
static void
Modify_CVT_Check( TT_ExecContext exc )
{
if ( exc->iniRange == tt_coderange_glyph &&
exc->cvt != exc->glyfCvt )
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
{
FT_Memory memory = exc->memory;
FT_Error error;
FT_MEM_QRENEW_ARRAY( exc->glyfCvt, exc->glyfCvtSize, exc->cvtSize );
exc->error = error;
if ( error )
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
return;
exc->glyfCvtSize = exc->cvtSize;
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
FT_ARRAY_COPY( exc->glyfCvt, exc->cvt, exc->glyfCvtSize );
exc->cvt = exc->glyfCvt;
}
}
2001-06-28 09:17:51 +02:00
FT_CALLBACK_DEF( void )
Write_CVT( TT_ExecContext exc,
FT_ULong idx,
FT_F26Dot6 value )
1999-12-17 00:11:37 +01:00
{
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
Modify_CVT_Check( exc );
if ( exc->error )
return;
exc->cvt[idx] = value;
1999-12-17 00:11:37 +01:00
}
2001-06-28 09:17:51 +02:00
FT_CALLBACK_DEF( void )
Write_CVT_Stretched( TT_ExecContext exc,
FT_ULong idx,
FT_F26Dot6 value )
1999-12-17 00:11:37 +01:00
{
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
Modify_CVT_Check( exc );
if ( exc->error )
return;
exc->cvt[idx] = FT_DivFix( value, Current_Ratio( exc ) );
1999-12-17 00:11:37 +01:00
}
2001-06-28 09:17:51 +02:00
FT_CALLBACK_DEF( void )
Move_CVT( TT_ExecContext exc,
FT_ULong idx,
FT_F26Dot6 value )
1999-12-17 00:11:37 +01:00
{
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
Modify_CVT_Check( exc );
if ( exc->error )
return;
exc->cvt[idx] = ADD_LONG( exc->cvt[idx], value );
1999-12-17 00:11:37 +01:00
}
2001-06-28 09:17:51 +02:00
FT_CALLBACK_DEF( void )
Move_CVT_Stretched( TT_ExecContext exc,
FT_ULong idx,
FT_F26Dot6 value )
1999-12-17 00:11:37 +01:00
{
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
Modify_CVT_Check( exc );
if ( exc->error )
return;
exc->cvt[idx] = ADD_LONG( exc->cvt[idx],
FT_DivFix( value, Current_Ratio( exc ) ) );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* @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.
*/
2001-06-28 09:17:51 +02:00
static FT_Short
GetShortIns( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
2016-04-09 05:21:34 +02:00
/* 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] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* @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.
*/
2001-06-28 09:17:51 +02:00
static FT_Bool
Ins_Goto_CodeRange( TT_ExecContext exc,
FT_Int aRange,
FT_Long aIP )
1999-12-17 00:11:37 +01:00
{
TT_CodeRange* range;
if ( aRange < 1 || aRange > 3 )
{
exc->error = FT_THROW( Bad_Argument );
1999-12-17 00:11:37 +01:00
return FAILURE;
}
range = &exc->codeRangeTable[aRange - 1];
1999-12-17 00:11:37 +01:00
if ( !range->base ) /* invalid coderange */
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Invalid_CodeRange );
1999-12-17 00:11:37 +01:00
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. */
1999-12-17 00:11:37 +01:00
if ( aIP > range->size )
{
exc->error = FT_THROW( Code_Overflow );
1999-12-17 00:11:37 +01:00
return FAILURE;
}
exc->code = range->base;
exc->codeSize = range->size;
exc->IP = aIP;
exc->curRange = aRange;
1999-12-17 00:11:37 +01:00
return SUCCESS;
}
/*
*
* Apple's TrueType specification at
*
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#order
*
* gives the following order of operations in instructions that move
* points.
*
* - check single width cut-in (MIRP, MDRP)
*
* - check control value cut-in (MIRP, MIAP)
*
* - apply engine compensation (MIRP, MDRP)
*
* - round distance (MIRP, MDRP) or value (MIAP, MDAP)
*
* - check minimum distance (MIRP,MDRP)
*
* - move point (MIRP, MDRP, MIAP, MSIRP, MDAP)
*
* For rounding instructions, engine compensation happens before rounding.
*
*/
/**************************************************************************
*
* @Function:
* Direct_Move
*
* @Description:
* Moves a point by a given distance along the freedom vector. The
* point will be `touched'.
*
* @Input:
* point ::
* The index of the point to move.
*
* distance ::
* The distance to apply.
*
* @InOut:
* zone ::
* The affected glyph zone.
*
* @Note:
* See `ttinterp.h' for details on backward compatibility mode.
* `Touches' the point.
*/
2001-06-28 09:17:51 +02:00
static void
Direct_Move( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 v;
1999-12-17 00:11:37 +01:00
2003-04-29 15:23:47 +02:00
v = exc->GS.freeVector.x;
1999-12-17 00:11:37 +01:00
if ( v != 0 )
{
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
/* Exception to the post-IUP curfew: Allow the x component of */
/* diagonal moves, but only post-IUP. DejaVu tries to adjust */
/* diagonal stems like on `Z' and `z' post-IUP. */
if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility )
zone->cur[point].x = ADD_LONG( zone->cur[point].x,
FT_MulDiv( distance,
v,
exc->F_dot_P ) );
else
#endif
if ( NO_SUBPIXEL_HINTING )
zone->cur[point].x = ADD_LONG( zone->cur[point].x,
FT_MulDiv( distance,
v,
exc->F_dot_P ) );
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
1999-12-17 00:11:37 +01:00
}
v = exc->GS.freeVector.y;
1999-12-17 00:11:37 +01:00
if ( v != 0 )
{
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
if ( !( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility &&
exc->iupx_called &&
exc->iupy_called ) )
#endif
zone->cur[point].y = ADD_LONG( zone->cur[point].y,
FT_MulDiv( distance,
v,
exc->F_dot_P ) );
zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
1999-12-17 00:11:37 +01:00
}
}
/**************************************************************************
*
* @Function:
* Direct_Move_Orig
*
* @Description:
* Moves the *original* position of a point by a given distance along
* the freedom vector. Obviously, the point will not be `touched'.
*
* @Input:
* point ::
* The index of the point to move.
*
* distance ::
* The distance to apply.
*
* @InOut:
* zone ::
* The affected glyph zone.
*/
static void
Direct_Move_Orig( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_F26Dot6 v;
v = exc->GS.freeVector.x;
if ( v != 0 )
zone->org[point].x = ADD_LONG( zone->org[point].x,
FT_MulDiv( distance,
v,
exc->F_dot_P ) );
v = exc->GS.freeVector.y;
if ( v != 0 )
zone->org[point].y = ADD_LONG( zone->org[point].y,
FT_MulDiv( distance,
v,
exc->F_dot_P ) );
}
/**************************************************************************
*
* Special versions of Direct_Move()
*
* The following versions are used whenever both vectors are both
* along one of the coordinate unit vectors, i.e. in 90% of the cases.
* See `ttinterp.h' for details on backward compatibility mode.
*
*/
1999-12-17 00:11:37 +01:00
2001-06-28 09:17:51 +02:00
static void
Direct_Move_X( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
1999-12-17 00:11:37 +01:00
{
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility )
zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance );
else
#endif
1999-12-17 00:11:37 +01:00
if ( NO_SUBPIXEL_HINTING )
zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance );
zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
1999-12-17 00:11:37 +01:00
}
2001-06-28 09:17:51 +02:00
static void
Direct_Move_Y( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
1999-12-17 00:11:37 +01:00
{
FT_UNUSED( exc );
1999-12-17 00:11:37 +01:00
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
if ( !( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility &&
exc->iupx_called && exc->iupy_called ) )
#endif
zone->cur[point].y = ADD_LONG( zone->cur[point].y, distance );
zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* Special versions of Direct_Move_Orig()
*
* The following versions are used whenever both vectors are both
* along one of the coordinate unit vectors, i.e. in 90% of the cases.
*
*/
static void
Direct_Move_Orig_X( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_UNUSED( exc );
zone->org[point].x = ADD_LONG( zone->org[point].x, distance );
}
static void
Direct_Move_Orig_Y( TT_ExecContext exc,
TT_GlyphZone zone,
FT_UShort point,
FT_F26Dot6 distance )
{
FT_UNUSED( exc );
zone->org[point].y = ADD_LONG( zone->org[point].y, distance );
}
/**************************************************************************
*
* @Function:
* Round_None
*
* @Description:
* Does not round, but adds engine compensation.
*
* @Input:
* distance ::
* The distance (not) to round.
*
* color ::
* The engine compensation color.
*
* @Return:
* The compensated distance.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Round_None( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_Int color )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 compensation = exc->tt_metrics.compensations[color];
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
1999-12-17 00:11:37 +01:00
if ( distance >= 0 )
{
val = ADD_LONG( distance, compensation );
if ( val < 0 )
1999-12-17 00:11:37 +01:00
val = 0;
}
2010-06-08 08:37:11 +02:00
else
{
val = SUB_LONG( distance, compensation );
1999-12-17 00:11:37 +01:00
if ( val > 0 )
val = 0;
}
return val;
}
/**************************************************************************
*
* @Function:
* Round_To_Grid
*
* @Description:
* Rounds value to grid after adding engine compensation.
*
* @Input:
* distance ::
* The distance to round.
*
* color ::
* The engine compensation color.
*
* @Return:
* Rounded distance.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Round_To_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_Int color )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 compensation = exc->tt_metrics.compensations[color];
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
1999-12-17 00:11:37 +01:00
if ( distance >= 0 )
{
val = FT_PIX_ROUND_LONG( ADD_LONG( distance, compensation ) );
if ( val < 0 )
1999-12-17 00:11:37 +01:00
val = 0;
}
else
{
val = NEG_LONG( FT_PIX_ROUND_LONG( SUB_LONG( compensation,
distance ) ) );
1999-12-17 00:11:37 +01:00
if ( val > 0 )
val = 0;
}
2015-05-20 12:33:40 +02:00
return val;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* @Function:
* Round_To_Half_Grid
*
* @Description:
* Rounds value to half grid after adding engine compensation.
*
* @Input:
* distance ::
* The distance to round.
*
* color ::
* The engine compensation color.
*
* @Return:
* Rounded distance.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Round_To_Half_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_Int color )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 compensation = exc->tt_metrics.compensations[color];
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
if ( distance >= 0 )
{
val = ADD_LONG( FT_PIX_FLOOR( ADD_LONG( distance, compensation ) ),
32 );
if ( val < 0 )
val = 32;
1999-12-17 00:11:37 +01:00
}
else
{
val = NEG_LONG( ADD_LONG( FT_PIX_FLOOR( SUB_LONG( compensation,
distance ) ),
32 ) );
1999-12-17 00:11:37 +01:00
if ( val > 0 )
val = -32;
1999-12-17 00:11:37 +01:00
}
1999-12-17 00:11:37 +01:00
return val;
}
/**************************************************************************
*
* @Function:
* Round_Down_To_Grid
*
* @Description:
* Rounds value down to grid after adding engine compensation.
*
* @Input:
* distance ::
* The distance to round.
*
* color ::
* The engine compensation color.
*
* @Return:
* Rounded distance.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Round_Down_To_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_Int color )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 compensation = exc->tt_metrics.compensations[color];
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
1999-12-17 00:11:37 +01:00
if ( distance >= 0 )
{
val = FT_PIX_FLOOR( ADD_LONG( distance, compensation ) );
if ( val < 0 )
1999-12-17 00:11:37 +01:00
val = 0;
}
else
{
val = NEG_LONG( FT_PIX_FLOOR( SUB_LONG( compensation, distance ) ) );
1999-12-17 00:11:37 +01:00
if ( val > 0 )
val = 0;
}
1999-12-17 00:11:37 +01:00
return val;
}
/**************************************************************************
*
* @Function:
* Round_Up_To_Grid
*
* @Description:
* Rounds value up to grid after adding engine compensation.
*
* @Input:
* distance ::
* The distance to round.
*
* color ::
* The engine compensation color.
*
* @Return:
* Rounded distance.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Round_Up_To_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_Int color )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 compensation = exc->tt_metrics.compensations[color];
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
1999-12-17 00:11:37 +01:00
if ( distance >= 0 )
{
val = FT_PIX_CEIL_LONG( ADD_LONG( distance, compensation ) );
if ( val < 0 )
1999-12-17 00:11:37 +01:00
val = 0;
}
else
{
val = NEG_LONG( FT_PIX_CEIL_LONG( SUB_LONG( compensation,
distance ) ) );
1999-12-17 00:11:37 +01:00
if ( val > 0 )
val = 0;
}
1999-12-17 00:11:37 +01:00
return val;
}
/**************************************************************************
*
* @Function:
* Round_To_Double_Grid
*
* @Description:
* Rounds value to double grid after adding engine compensation.
*
* @Input:
* distance ::
* The distance to round.
*
* color ::
* The engine compensation color.
*
* @Return:
* Rounded distance.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Round_To_Double_Grid( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_Int color )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 compensation = exc->tt_metrics.compensations[color];
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
1999-12-17 00:11:37 +01:00
if ( distance >= 0 )
{
val = FT_PAD_ROUND_LONG( ADD_LONG( distance, compensation ), 32 );
if ( val < 0 )
1999-12-17 00:11:37 +01:00
val = 0;
}
else
{
val = NEG_LONG( FT_PAD_ROUND_LONG( SUB_LONG( compensation, distance ),
32 ) );
1999-12-17 00:11:37 +01:00
if ( val > 0 )
val = 0;
}
1999-12-17 00:11:37 +01:00
return val;
}
/**************************************************************************
*
* @Function:
* Round_Super
*
* @Description:
* Super-rounds value to grid after adding engine compensation.
*
* @Input:
* distance ::
* The distance to round.
*
* color ::
* The engine compensation color.
*
* @Return:
* Rounded distance.
*
* @Note:
* The TrueType specification says very little about the relationship
* between rounding and engine compensation. However, it seems from
* the description of super round that we should add the compensation
* before rounding.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Round_Super( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_Int color )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 compensation = exc->tt_metrics.compensations[color];
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
if ( distance >= 0 )
{
val = ADD_LONG( distance,
exc->threshold - exc->phase + compensation ) &
-exc->period;
val = ADD_LONG( val, exc->phase );
if ( val < 0 )
val = exc->phase;
1999-12-17 00:11:37 +01:00
}
else
{
val = NEG_LONG( SUB_LONG( exc->threshold - exc->phase + compensation,
distance ) &
-exc->period );
val = SUB_LONG( val, exc->phase );
if ( val > 0 )
val = -exc->phase;
1999-12-17 00:11:37 +01:00
}
1999-12-17 00:11:37 +01:00
return val;
}
/**************************************************************************
*
* @Function:
* Round_Super_45
*
* @Description:
* Super-rounds value to grid after adding engine compensation.
*
* @Input:
* distance ::
* The distance to round.
*
* color ::
* The engine compensation color.
*
* @Return:
* Rounded distance.
*
* @Note:
* There is a separate function for Round_Super_45() as we may need
* greater precision.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Round_Super_45( TT_ExecContext exc,
FT_F26Dot6 distance,
FT_Int color )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 compensation = exc->tt_metrics.compensations[color];
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
if ( distance >= 0 )
{
val = ( ADD_LONG( distance,
exc->threshold - exc->phase + compensation ) /
exc->period ) * exc->period;
val = ADD_LONG( val, exc->phase );
if ( val < 0 )
val = exc->phase;
1999-12-17 00:11:37 +01:00
}
else
{
val = NEG_LONG( ( SUB_LONG( exc->threshold - exc->phase + compensation,
distance ) /
exc->period ) * exc->period );
val = SUB_LONG( val, exc->phase );
if ( val > 0 )
val = -exc->phase;
1999-12-17 00:11:37 +01:00
}
return val;
}
/**************************************************************************
*
* @Function:
* Compute_Round
*
* @Description:
* Sets the rounding mode.
*
* @Input:
* round_mode ::
* The rounding mode to be used.
*/
2001-06-28 09:17:51 +02:00
static void
Compute_Round( TT_ExecContext exc,
FT_Byte round_mode )
1999-12-17 00:11:37 +01:00
{
switch ( round_mode )
{
case TT_Round_Off:
exc->func_round = (TT_Round_Func)Round_None;
1999-12-17 00:11:37 +01:00
break;
case TT_Round_To_Grid:
exc->func_round = (TT_Round_Func)Round_To_Grid;
1999-12-17 00:11:37 +01:00
break;
case TT_Round_Up_To_Grid:
exc->func_round = (TT_Round_Func)Round_Up_To_Grid;
1999-12-17 00:11:37 +01:00
break;
case TT_Round_Down_To_Grid:
exc->func_round = (TT_Round_Func)Round_Down_To_Grid;
1999-12-17 00:11:37 +01:00
break;
case TT_Round_To_Half_Grid:
exc->func_round = (TT_Round_Func)Round_To_Half_Grid;
1999-12-17 00:11:37 +01:00
break;
case TT_Round_To_Double_Grid:
exc->func_round = (TT_Round_Func)Round_To_Double_Grid;
1999-12-17 00:11:37 +01:00
break;
case TT_Round_Super:
exc->func_round = (TT_Round_Func)Round_Super;
1999-12-17 00:11:37 +01:00
break;
case TT_Round_Super_45:
exc->func_round = (TT_Round_Func)Round_Super_45;
1999-12-17 00:11:37 +01:00
break;
}
}
/**************************************************************************
*
* @Function:
* SetSuperRound
*
* @Description:
* Sets Super Round parameters.
*
* @Input:
* GridPeriod ::
* The grid period.
*
* selector ::
* The SROUND opcode.
*/
2001-06-28 09:17:51 +02:00
static void
SetSuperRound( TT_ExecContext exc,
2015-05-20 12:33:40 +02:00
FT_F2Dot14 GridPeriod,
FT_Long selector )
1999-12-17 00:11:37 +01:00
{
switch ( (FT_Int)( selector & 0xC0 ) )
1999-12-17 00:11:37 +01:00
{
case 0:
exc->period = GridPeriod / 2;
1999-12-17 00:11:37 +01:00
break;
case 0x40:
exc->period = GridPeriod;
1999-12-17 00:11:37 +01:00
break;
case 0x80:
exc->period = GridPeriod * 2;
1999-12-17 00:11:37 +01:00
break;
/* This opcode is reserved, but... */
case 0xC0:
exc->period = GridPeriod;
1999-12-17 00:11:37 +01:00
break;
}
switch ( (FT_Int)( selector & 0x30 ) )
1999-12-17 00:11:37 +01:00
{
case 0:
exc->phase = 0;
1999-12-17 00:11:37 +01:00
break;
case 0x10:
exc->phase = exc->period / 4;
1999-12-17 00:11:37 +01:00
break;
case 0x20:
exc->phase = exc->period / 2;
1999-12-17 00:11:37 +01:00
break;
case 0x30:
exc->phase = exc->period * 3 / 4;
1999-12-17 00:11:37 +01:00
break;
}
2007-03-21 14:30:14 +01:00
if ( ( selector & 0x0F ) == 0 )
exc->threshold = exc->period - 1;
1999-12-17 00:11:37 +01:00
else
exc->threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * exc->period / 8;
1999-12-17 00:11:37 +01:00
2015-05-20 12:33:40 +02:00
/* convert to F26Dot6 format */
exc->period >>= 8;
exc->phase >>= 8;
exc->threshold >>= 8;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* @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.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Project( TT_ExecContext exc,
FT_Pos dx,
FT_Pos dy )
2007-02-12 23:01:18 +01:00
{
return TT_DotFix14( dx, dy,
exc->GS.projVector.x,
exc->GS.projVector.y );
2007-02-12 23:01:18 +01:00
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* @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.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Dual_Project( TT_ExecContext exc,
FT_Pos dx,
FT_Pos dy )
1999-12-17 00:11:37 +01:00
{
return TT_DotFix14( dx, dy,
exc->GS.dualVector.x,
exc->GS.dualVector.y );
1999-12-17 00:11:37 +01:00
}
2007-02-12 23:01:18 +01:00
/**************************************************************************
*
* @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.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Project_x( TT_ExecContext exc,
FT_Pos dx,
FT_Pos dy )
1999-12-17 00:11:37 +01:00
{
FT_UNUSED( exc );
FT_UNUSED( dy );
1999-12-17 00:11:37 +01:00
return dx;
1999-12-17 00:11:37 +01:00
}
2007-02-12 23:01:18 +01:00
/**************************************************************************
*
* @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.
*/
2001-06-28 09:17:51 +02:00
static FT_F26Dot6
Project_y( TT_ExecContext exc,
FT_Pos dx,
FT_Pos dy )
1999-12-17 00:11:37 +01:00
{
FT_UNUSED( exc );
FT_UNUSED( dx );
2000-05-17 01:44:38 +02:00
return dy;
1999-12-17 00:11:37 +01:00
}
2007-02-12 23:01:18 +01:00
/**************************************************************************
*
* @Function:
* Compute_Funcs
*
* @Description:
* Computes the projection and movement function pointers according
* to the current graphics state.
*/
2001-06-28 09:17:51 +02:00
static void
Compute_Funcs( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
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;
1999-12-17 00:11:37 +01:00
else
2015-01-11 00:56:55 +01:00
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;
1999-12-17 00:11:37 +01:00
else
exc->func_project = (TT_Project_Func)Project;
1999-12-17 00:11:37 +01:00
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;
1999-12-17 00:11:37 +01:00
else
exc->func_dualproj = (TT_Project_Func)Dual_Project;
1999-12-17 00:11:37 +01:00
exc->func_move = (TT_Move_Func)Direct_Move;
exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig;
1999-12-17 00:11:37 +01:00
if ( exc->F_dot_P == 0x4000L )
1999-12-17 00:11:37 +01:00
{
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 )
1999-12-17 00:11:37 +01:00
{
exc->func_move = (TT_Move_Func)Direct_Move_Y;
exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y;
1999-12-17 00:11:37 +01:00
}
}
/* 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;
1999-12-17 00:11:37 +01:00
/* Disable cached aspect ratio */
exc->tt_metrics.ratio = 0;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* @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.
*/
2001-06-28 09:17:51 +02:00
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;
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* Here we start with the implementation of the various opcodes.
*
*/
1999-12-17 00:11:37 +01:00
#define ARRAY_BOUND_ERROR \
do \
{ \
exc->error = FT_THROW( Invalid_Reference ); \
return; \
} while (0)
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* MPPEM[]: Measure Pixel Per EM
* Opcode range: 0x4B
* Stack: --> Euint16
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MPPEM( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = exc->func_cur_ppem( exc );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MPS[]: Measure Point Size
* Opcode range: 0x4C
* Stack: --> Euint16
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MPS( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
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;
}
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* DUP[]: DUPlicate the stack's top element
* Opcode range: 0x20
* Stack: StkElt --> StkElt StkElt
*/
2001-06-28 09:17:51 +02:00
static void
Ins_DUP( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[1] = args[0];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* POP[]: POP the stack's top element
* Opcode range: 0x21
* Stack: StkElt -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_POP( void )
1999-12-17 00:11:37 +01:00
{
/* nothing to do */
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* CLEAR[]: CLEAR the entire stack
* Opcode range: 0x22
* Stack: StkElt... -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_CLEAR( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
exc->new_top = 0;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SWAP[]: SWAP the stack's top two elements
* Opcode range: 0x23
* Stack: 2 * StkElt --> 2 * StkElt
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SWAP( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_Long L;
L = args[0];
args[0] = args[1];
args[1] = L;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* DEPTH[]: return the stack DEPTH
* Opcode range: 0x24
* Stack: --> uint32
*/
2001-06-28 09:17:51 +02:00
static void
Ins_DEPTH( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = exc->top;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* LT[]: Less Than
* Opcode range: 0x50
* Stack: int32? int32? --> bool
*/
2001-06-28 09:17:51 +02:00
static void
Ins_LT( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( args[0] < args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* LTEQ[]: Less Than or EQual
* Opcode range: 0x51
* Stack: int32? int32? --> bool
*/
2001-06-28 09:17:51 +02:00
static void
Ins_LTEQ( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( args[0] <= args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* GT[]: Greater Than
* Opcode range: 0x52
* Stack: int32? int32? --> bool
*/
2001-06-28 09:17:51 +02:00
static void
Ins_GT( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( args[0] > args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* GTEQ[]: Greater Than or EQual
* Opcode range: 0x53
* Stack: int32? int32? --> bool
*/
2001-06-28 09:17:51 +02:00
static void
Ins_GTEQ( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( args[0] >= args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* EQ[]: EQual
* Opcode range: 0x54
* Stack: StkElt StkElt --> bool
*/
2001-06-28 09:17:51 +02:00
static void
Ins_EQ( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( args[0] == args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* NEQ[]: Not EQual
* Opcode range: 0x55
* Stack: StkElt StkElt --> bool
*/
2001-06-28 09:17:51 +02:00
static void
Ins_NEQ( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( args[0] != args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ODD[]: Is ODD
* Opcode range: 0x56
* Stack: f26.6 --> bool
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ODD( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( ( exc->func_round( exc, args[0], 3 ) & 127 ) == 64 );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* EVEN[]: Is EVEN
* Opcode range: 0x57
* Stack: f26.6 --> bool
*/
2001-06-28 09:17:51 +02:00
static void
Ins_EVEN( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( ( exc->func_round( exc, args[0], 3 ) & 127 ) == 0 );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* AND[]: logical AND
* Opcode range: 0x5A
* Stack: uint32 uint32 --> uint32
*/
2001-06-28 09:17:51 +02:00
static void
Ins_AND( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( args[0] && args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* OR[]: logical OR
* Opcode range: 0x5B
* Stack: uint32 uint32 --> uint32
*/
2001-06-28 09:17:51 +02:00
static void
Ins_OR( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ( args[0] || args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* NOT[]: logical NOT
* Opcode range: 0x5C
* Stack: StkElt --> uint32
*/
2001-06-28 09:17:51 +02:00
static void
Ins_NOT( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = !args[0];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ADD[]: ADD
* Opcode range: 0x60
* Stack: f26.6 f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ADD( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = ADD_LONG( args[0], args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SUB[]: SUBtract
* Opcode range: 0x61
* Stack: f26.6 f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SUB( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = SUB_LONG( args[0], args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* DIV[]: DIVide
* Opcode range: 0x62
* Stack: f26.6 f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_DIV( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[1] == 0 )
exc->error = FT_THROW( Divide_By_Zero );
else
args[0] = FT_MulDiv_No_Round( args[0], 64L, args[1] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MUL[]: MULtiply
* Opcode range: 0x63
* Stack: f26.6 f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MUL( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = FT_MulDiv( args[0], args[1], 64L );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ABS[]: ABSolute value
* Opcode range: 0x64
* Stack: f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ABS( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[0] < 0 )
args[0] = NEG_LONG( args[0] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* NEG[]: NEGate
* Opcode range: 0x65
* Stack: f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_NEG( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = NEG_LONG( args[0] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* FLOOR[]: FLOOR
* Opcode range: 0x66
* Stack: f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_FLOOR( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = FT_PIX_FLOOR( args[0] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* CEILING[]: CEILING
* Opcode range: 0x67
* Stack: f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_CEILING( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = FT_PIX_CEIL_LONG( args[0] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* RS[]: Read Store
* Opcode range: 0x43
* Stack: uint32 --> uint32
*/
2001-06-28 09:17:51 +02:00
static void
Ins_RS( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong I = (FT_ULong)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDSL( I, exc->storeSize ) )
{
if ( exc->pedantic_hinting )
ARRAY_BOUND_ERROR;
else
args[0] = 0;
}
else
args[0] = exc->storage[I];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* WS[]: Write Store
* Opcode range: 0x42
* Stack: uint32 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_WS( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong I = (FT_ULong)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDSL( I, exc->storeSize ) )
{
if ( exc->pedantic_hinting )
ARRAY_BOUND_ERROR;
}
else
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
{
if ( exc->iniRange == tt_coderange_glyph &&
exc->storage != exc->glyfStorage )
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
{
FT_Memory memory = exc->memory;
FT_Error error;
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
FT_MEM_QRENEW_ARRAY( exc->glyfStorage,
exc->glyfStoreSize,
exc->storeSize );
exc->error = error;
if ( error )
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
return;
exc->glyfStoreSize = exc->storeSize;
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
FT_ARRAY_COPY( exc->glyfStorage, exc->storage, exc->glyfStoreSize );
exc->storage = exc->glyfStorage;
}
exc->storage[I] = args[1];
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
}
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* WCVTP[]: Write CVT in Pixel units
* Opcode range: 0x44
* Stack: f26.6 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_WCVTP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
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] );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* WCVTF[]: Write CVT in Funits
* Opcode range: 0x70
* Stack: uint32 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_WCVTF( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
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 );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* RCVT[]: Read CVT
* Opcode range: 0x45
* Stack: uint32 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_RCVT( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
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 );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* AA[]: Adjust Angle
* Opcode range: 0x7F
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_AA( void )
1999-12-17 00:11:37 +01:00
{
/* intentionally no longer supported */
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* DEBUG[]: DEBUG. Unsupported.
* Opcode range: 0x4F
* Stack: uint32 -->
*
* Note: The original instruction pops a value from the stack.
*/
2001-06-28 09:17:51 +02:00
static void
Ins_DEBUG( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Debug_OpCode );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ROUND[ab]: ROUND value
* Opcode range: 0x68-0x6B
* Stack: f26.6 --> f26.6
*/
static void
Ins_ROUND( TT_ExecContext exc,
FT_Long* args )
{
args[0] = exc->func_round( exc, args[0], exc->opcode & 3 );
}
/**************************************************************************
*
* NROUND[ab]: No ROUNDing of value
* Opcode range: 0x6C-0x6F
* Stack: f26.6 --> f26.6
*/
2001-06-28 09:17:51 +02:00
static void
Ins_NROUND( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = Round_None( exc, args[0], exc->opcode & 3 );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MAX[]: MAXimum
* Opcode range: 0x8B
* Stack: int32? int32? --> int32
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MAX( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[1] > args[0] )
args[0] = args[1];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MIN[]: MINimum
* Opcode range: 0x8C
* Stack: int32? int32? --> int32
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MIN( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[1] < args[0] )
args[0] = args[1];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MINDEX[]: Move INDEXed element
* Opcode range: 0x26
* Stack: int32? --> StkElt
*/
static void
Ins_MINDEX( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
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;
}
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* CINDEX[]: Copy INDEXed element
* Opcode range: 0x25
* Stack: int32 --> StkElt
*/
2001-06-28 09:17:51 +02:00
static void
Ins_CINDEX( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
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];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ROLL[]: ROLL top three elements
* Opcode range: 0x8A
* Stack: 3 * StkElt --> 3 * StkElt
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ROLL( FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_Long A, B, C;
A = args[2];
B = args[1];
C = args[0];
args[2] = C;
args[1] = A;
args[0] = B;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MANAGING THE FLOW OF CONTROL
*
*/
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* SLOOP[]: Set LOOP variable
* Opcode range: 0x17
* Stack: int32? -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SLOOP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[0] < 0 )
exc->error = FT_THROW( Bad_Argument );
else
{
/* we heuristically limit the number of loops to 16 bits */
exc->GS.loop = args[0] > 0xFFFFL ? 0xFFFFL : args[0];
}
1999-12-17 00:11:37 +01:00
}
static FT_Bool
SkipCode( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
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;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* IF[]: IF test
* Opcode range: 0x58
* Stack: StkElt -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_IF( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
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 );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ELSE[]: ELSE
* Opcode range: 0x1B
* Stack: -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ELSE( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
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 );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* EIF[]: End IF
* Opcode range: 0x59
* Stack: -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_EIF( void )
1999-12-17 00:11:37 +01:00
{
/* nothing to do */
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* JMPR[]: JuMP Relative
* Opcode range: 0x1C
* Stack: int32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_JMPR( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[0] == 0 && exc->args == 0 )
{
exc->error = FT_THROW( Bad_Argument );
return;
}
exc->IP = ADD_LONG( 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 );
}
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* JROT[]: Jump Relative On True
* Opcode range: 0x78
* Stack: StkElt int32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_JROT( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[1] != 0 )
Ins_JMPR( exc, args );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* JROF[]: Jump Relative On False
* Opcode range: 0x79
* Stack: StkElt int32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_JROF( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[1] == 0 )
Ins_JMPR( exc, args );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS
*
*/
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* FDEF[]: Function DEFinition
* Opcode range: 0x2C
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_FDEF( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong n;
TT_DefRecord* rec;
TT_DefRecord* limit;
1999-12-17 00:11:37 +01:00
/* FDEF is only allowed in `prep' or `fpgm' */
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
if ( exc->iniRange == tt_coderange_glyph )
{
exc->error = FT_THROW( DEF_In_Glyf_Bytecode );
return;
}
/* some font programs are broken enough to redefine functions! */
/* We will then parse the current table. */
rec = exc->FDefs;
limit = FT_OFFSET( 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;
}
1999-12-17 00:11:37 +01:00
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;
1999-12-17 00:11:37 +01:00
if ( n > exc->maxFunc )
exc->maxFunc = (FT_UInt16)n;
/* 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 */
rec->end = exc->IP;
return;
}
}
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ENDF[]: END Function definition
* Opcode range: 0x2D
* Stack: -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ENDF( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
TT_CallRec* pRec;
1999-12-17 00:11:37 +01:00
if ( exc->callTop <= 0 ) /* We encountered an ENDF without a call */
{
exc->error = FT_THROW( ENDF_In_Exec_Stream );
return;
}
1999-12-17 00:11:37 +01:00
exc->callTop--;
1999-12-17 00:11:37 +01:00
pRec = &exc->callStack[exc->callTop];
1999-12-17 00:11:37 +01:00
pRec->Cur_Count--;
1999-12-17 00:11:37 +01:00
exc->step_ins = FALSE;
if ( pRec->Cur_Count > 0 )
1999-12-17 00:11:37 +01:00
{
exc->callTop++;
exc->IP = pRec->Def->start;
1999-12-17 00:11:37 +01:00
}
else
/* Loop through the current function */
Ins_Goto_CodeRange( exc, pRec->Caller_Range, pRec->Caller_IP );
1999-12-17 00:11:37 +01:00
/* Exit the current call frame. */
1999-12-17 00:11:37 +01:00
/* 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! */
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* CALL[]: CALL function
* Opcode range: 0x2B
* Stack: uint32? -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_CALL( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong F;
TT_CallRec* pCrec;
TT_DefRecord* def;
1999-12-17 00:11:37 +01:00
/* first of all, check the index */
1999-12-17 00:11:37 +01:00
F = (FT_ULong)args[0];
if ( BOUNDSL( F, exc->maxFunc + 1 ) )
goto Fail;
1999-12-17 00:11:37 +01:00
if ( !exc->FDefs )
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. */
1999-12-17 00:11:37 +01:00
def = exc->FDefs + F;
if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F )
{
/* look up the FDefs table */
TT_DefRecord* limit;
1999-12-17 00:11:37 +01:00
def = exc->FDefs;
limit = def + exc->numFDefs;
1999-12-17 00:11:37 +01:00
while ( def < limit && def->opc != F )
def++;
if ( def == limit )
goto Fail;
1999-12-17 00:11:37 +01:00
}
/* check that the function is active */
if ( !def->active )
goto Fail;
1999-12-17 00:11:37 +01:00
/* check the call stack */
if ( exc->callTop >= exc->callSize )
{
exc->error = FT_THROW( Stack_Overflow );
return;
}
1999-12-17 00:11:37 +01:00
pCrec = exc->callStack + exc->callTop;
1999-12-17 00:11:37 +01:00
pCrec->Caller_Range = exc->curRange;
pCrec->Caller_IP = exc->IP + 1;
pCrec->Cur_Count = 1;
pCrec->Def = def;
1999-12-17 00:11:37 +01:00
exc->callTop++;
1999-12-17 00:11:37 +01:00
Ins_Goto_CodeRange( exc, def->range, def->start );
1999-12-17 00:11:37 +01:00
exc->step_ins = FALSE;
1999-12-17 00:11:37 +01:00
return;
1999-12-17 00:11:37 +01:00
Fail:
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* LOOPCALL[]: LOOP and CALL function
* Opcode range: 0x2A
* Stack: uint32? Eint16? -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_LOOPCALL( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong F;
TT_CallRec* pCrec;
TT_DefRecord* def;
1999-12-17 00:11:37 +01:00
/* first of all, check the index */
F = (FT_ULong)args[1];
if ( BOUNDSL( F, exc->maxFunc + 1 ) )
goto Fail;
1999-12-17 00:11:37 +01:00
/* 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. */
1999-12-17 00:11:37 +01:00
def = FT_OFFSET( exc->FDefs, F );
if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F )
{
/* look up the FDefs table */
TT_DefRecord* limit;
1999-12-17 00:11:37 +01:00
def = exc->FDefs;
limit = FT_OFFSET( def, exc->numFDefs );
while ( def < limit && def->opc != F )
def++;
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
if ( def == limit )
goto Fail;
}
/* check that the function is active */
if ( !def->active )
goto Fail;
/* check stack */
if ( exc->callTop >= exc->callSize )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Stack_Overflow );
return;
1999-12-17 00:11:37 +01:00
}
2000-05-17 01:44:38 +02:00
if ( args[0] > 0 )
1999-12-17 00:11:37 +01:00
{
pCrec = exc->callStack + exc->callTop;
pCrec->Caller_Range = exc->curRange;
pCrec->Caller_IP = exc->IP + 1;
pCrec->Cur_Count = (FT_Int)args[0];
pCrec->Def = def;
exc->callTop++;
Ins_Goto_CodeRange( exc, def->range, def->start );
exc->step_ins = FALSE;
exc->loopcall_counter += (FT_ULong)args[0];
if ( exc->loopcall_counter > exc->loopcall_counter_max )
exc->error = FT_THROW( Execution_Too_Long );
}
return;
Fail:
exc->error = FT_THROW( Invalid_Reference );
}
/**************************************************************************
*
* IDEF[]: Instruction DEFinition
* Opcode range: 0x89
* Stack: Eint8 -->
*/
static void
Ins_IDEF( TT_ExecContext exc,
FT_Long* args )
{
TT_DefRecord* def;
TT_DefRecord* limit;
/* we enable IDEF only in `prep' or `fpgm' */
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
if ( exc->iniRange == tt_coderange_glyph )
{
exc->error = FT_THROW( DEF_In_Glyf_Bytecode );
return;
}
/* First of all, look for the same function in our table */
def = exc->IDefs;
limit = FT_OFFSET( 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 )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Too_Many_Instruction_Defs );
1999-12-17 00:11:37 +01:00
return;
}
exc->numIDefs++;
1999-12-17 00:11:37 +01:00
}
2000-05-17 01:44:38 +02:00
/* 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;
2000-05-17 01:44:38 +02:00
if ( (FT_ULong)args[0] > exc->maxIns )
exc->maxIns = (FT_Byte)args[0];
1999-12-17 00:11:37 +01:00
/* Now skip the whole function definition. */
/* We don't allow nested IDEFs & FDEFs. */
1999-12-17 00:11:37 +01:00
while ( SkipCode( exc ) == SUCCESS )
1999-12-17 00:11:37 +01:00
{
switch ( exc->opcode )
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
{
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;
}
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
/**************************************************************************
*
* NPUSHW[]: PUSH N Words
* Opcode range: 0x41
* Stack: --> int32...
*/
static void
Ins_NPUSHW( TT_ExecContext exc,
FT_Long* args )
{
FT_UShort L, K;
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
L = (FT_UShort)exc->code[exc->IP + 1];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
{
exc->error = FT_THROW( Stack_Overflow );
return;
1999-12-17 00:11:37 +01:00
}
exc->IP += 2;
for ( K = 0; K < L; K++ )
args[K] = GetShortIns( exc );
exc->step_ins = FALSE;
exc->new_top += L;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* PUSHB[abc]: PUSH Bytes
* Opcode range: 0xB0-0xB7
* Stack: --> uint32...
*/
2001-06-28 09:17:51 +02:00
static void
Ins_PUSHB( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort L, K;
1999-12-17 00:11:37 +01:00
L = (FT_UShort)( exc->opcode - 0xB0 + 1 );
if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Stack_Overflow );
1999-12-17 00:11:37 +01:00
return;
}
for ( K = 1; K <= L; K++ )
args[K - 1] = exc->code[exc->IP + K];
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* PUSHW[abc]: PUSH Words
* Opcode range: 0xB8-0xBF
* Stack: --> int32...
*/
static void
Ins_PUSHW( TT_ExecContext exc,
FT_Long* args )
{
FT_UShort L, K;
1999-12-17 00:11:37 +01:00
L = (FT_UShort)( exc->opcode - 0xB8 + 1 );
if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Stack_Overflow );
return;
1999-12-17 00:11:37 +01:00
}
exc->IP++;
1999-12-17 00:11:37 +01:00
for ( K = 0; K < L; K++ )
args[K] = GetShortIns( exc );
exc->step_ins = FALSE;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MANAGING THE GRAPHICS STATE
*
*/
1999-12-17 00:11:37 +01:00
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;
2000-05-17 01:44:38 +02:00
1999-12-17 00:11:37 +01:00
if ( BOUNDS( aIdx1, exc->zp2.n_points ) ||
BOUNDS( aIdx2, exc->zp1.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
return FAILURE;
}
2000-05-17 01:44:38 +02:00
p1 = exc->zp1.cur + aIdx2;
p2 = exc->zp2.cur + aIdx1;
A = SUB_LONG( p1->x, p2->x );
B = SUB_LONG( p1->y, p2->y );
2000-05-17 01:44:38 +02:00
/* If p1 == p2, SPvTL and SFvTL behave the same as */
/* SPvTCA[X] and SFvTCA[X], respectively. */
/* */
/* Confirmed by Greg Hitchcock. */
2000-05-17 01:44:38 +02:00
if ( A == 0 && B == 0 )
{
A = 0x4000;
opcode = 0;
1999-12-17 00:11:37 +01:00
}
2000-05-17 01:44:38 +02:00
if ( ( opcode & 1 ) != 0 )
{
C = B; /* counter-clockwise rotation */
B = A;
A = NEG_LONG( C );
}
2000-05-17 01:44:38 +02:00
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;
}
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
if ( ( opcode & 2 ) == 0 )
1999-12-17 00:11:37 +01:00
{
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 );
1999-12-17 00:11:37 +01:00
}
}
1999-12-17 00:11:37 +01:00
2000-05-17 01:44:38 +02:00
/**************************************************************************
*
* 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 );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SPvFS[]: Set PVector From Stack
* Opcode range: 0x0A
* Stack: f2.14 f2.14 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SPVFS( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_Short S;
FT_Long X, Y;
1999-12-17 00:11:37 +01:00
/* 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;
2000-05-17 01:44:38 +02:00
Normalize( X, Y, &exc->GS.projVector );
1999-12-17 00:11:37 +01:00
exc->GS.dualVector = exc->GS.projVector;
Compute_Funcs( exc );
}
2000-05-17 01:44:38 +02:00
/**************************************************************************
*
* 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;
2000-05-17 01:44:38 +02:00
/* Only use low 16bits, then sign extend */
S = (FT_Short)args[1];
Y = (FT_Long)S;
S = (FT_Short)args[0];
X = S;
2000-05-17 01:44:38 +02:00
Normalize( X, Y, &exc->GS.freeVector );
Compute_Funcs( exc );
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* 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;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* GFv[]: Get Freedom Vector
* Opcode range: 0x0D
* Stack: ef2.14 --> ef2.14
*/
2001-06-28 09:17:51 +02:00
static void
Ins_GFV( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
args[0] = exc->GS.freeVector.x;
args[1] = exc->GS.freeVector.y;
}
2000-05-17 01:44:38 +02:00
/**************************************************************************
*
* 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];
}
2000-05-17 01:44:38 +02:00
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* 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];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* 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];
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* SCVTCI[]: Set Control Value Table Cut In
* Opcode range: 0x1D
* Stack: f26.6 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SCVTCI( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
exc->GS.control_value_cutin = (FT_F26Dot6)args[0];
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* 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];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SSW[]: Set Single Width
* Opcode range: 0x1F
* Stack: int32? -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SSW( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
exc->GS.single_width_value = FT_MulFix( args[0],
exc->tt_metrics.scale );
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* FLIPON[]: Set auto-FLIP to ON
* Opcode range: 0x4D
* Stack: -->
*/
static void
Ins_FLIPON( TT_ExecContext exc )
{
exc->GS.auto_flip = TRUE;
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* FLIPOFF[]: Set auto-FLIP to OFF
* Opcode range: 0x4E
* Stack: -->
*/
static void
Ins_FLIPOFF( TT_ExecContext exc )
{
exc->GS.auto_flip = FALSE;
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* SANGW[]: Set ANGle Weight
* Opcode range: 0x7E
* Stack: uint32 -->
*/
static void
Ins_SANGW( void )
{
/* instruction not supported anymore */
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SDB[]: Set Delta Base
* Opcode range: 0x5E
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SDB( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
exc->GS.delta_base = (FT_UShort)args[0];
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* 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];
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* 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;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* RTG[]: Round To Grid
* Opcode range: 0x18
* Stack: -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_RTG( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
exc->GS.round_state = TT_Round_To_Grid;
exc->func_round = (TT_Round_Func)Round_To_Grid;
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
* 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;
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
* 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;
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* 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;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* 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;
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* 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!
*/
2001-06-28 09:17:51 +02:00
static void
Ins_GC( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong L;
FT_F26Dot6 R;
1999-12-17 00:11:37 +01:00
L = (FT_ULong)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDSL( L, exc->zp2.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
R = 0;
1999-12-17 00:11:37 +01:00
}
else
{
if ( exc->opcode & 1 )
R = FAST_DUALPROJ( &exc->zp2.org[L] );
1999-12-17 00:11:37 +01:00
else
R = FAST_PROJECT( &exc->zp2.cur[L] );
1999-12-17 00:11:37 +01:00
}
args[0] = R;
}
/**************************************************************************
*
* SCFS[]: Set Coordinate From Stack
* Opcode range: 0x48
* Stack: f26.6 uint32 -->
*
* Formula:
*
* OA := OA + ( value - OA.p )/( f.p ) * f
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SCFS( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_Long K;
FT_UShort L;
1999-12-17 00:11:37 +01:00
L = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( L, exc->zp2.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
K = FAST_PROJECT( &exc->zp2.cur[L] );
1999-12-17 00:11:37 +01:00
exc->func_move( exc, &exc->zp2, L, SUB_LONG( args[1], K ) );
1999-12-17 00:11:37 +01:00
/* 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];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* 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!
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MD( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort K, L;
FT_F26Dot6 D;
1999-12-17 00:11:37 +01:00
K = (FT_UShort)args[1];
L = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( L, exc->zp0.n_points ) ||
BOUNDS( K, exc->zp1.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
D = 0;
}
else
{
if ( exc->opcode & 1 )
D = PROJECT( exc->zp0.cur + L, exc->zp1.cur + K );
1999-12-17 00:11:37 +01:00
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 );
}
}
}
1999-12-17 00:11:37 +01:00
}
args[0] = D;
}
/**************************************************************************
*
* SDPvTL[a]: Set Dual PVector to Line
* Opcode range: 0x86-0x87
* Stack: uint32 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SDPVTL( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_Long A, B, C;
FT_UShort p1, p2; /* was FT_Int in pas type ERROR */
FT_Byte opcode = exc->opcode;
1999-12-17 00:11:37 +01:00
p1 = (FT_UShort)args[1];
p2 = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( p2, exc->zp1.n_points ) ||
BOUNDS( p1, exc->zp2.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
{
FT_Vector* v1 = exc->zp1.org + p2;
FT_Vector* v2 = exc->zp2.org + p1;
1999-12-17 00:11:37 +01:00
A = SUB_LONG( v1->x, v2->x );
B = SUB_LONG( v1->y, v2->y );
2015-01-11 00:56:55 +01:00
/* If v1 == v2, SDPvTL behaves the same as */
/* SVTCA[X], respectively. */
/* */
/* Confirmed by Greg Hitchcock. */
if ( A == 0 && B == 0 )
{
A = 0x4000;
opcode = 0;
}
1999-12-17 00:11:37 +01:00
}
if ( ( opcode & 1 ) != 0 )
1999-12-17 00:11:37 +01:00
{
C = B; /* counter-clockwise rotation */
B = A;
A = NEG_LONG( C );
1999-12-17 00:11:37 +01:00
}
Normalize( A, B, &exc->GS.dualVector );
1999-12-17 00:11:37 +01:00
{
FT_Vector* v1 = exc->zp1.cur + p2;
FT_Vector* v2 = exc->zp2.cur + p1;
1999-12-17 00:11:37 +01:00
A = SUB_LONG( v1->x, v2->x );
B = SUB_LONG( v1->y, v2->y );
if ( A == 0 && B == 0 )
{
A = 0x4000;
opcode = 0;
}
1999-12-17 00:11:37 +01:00
}
if ( ( opcode & 1 ) != 0 )
1999-12-17 00:11:37 +01:00
{
C = B; /* counter-clockwise rotation */
B = A;
A = NEG_LONG( C );
1999-12-17 00:11:37 +01:00
}
Normalize( A, B, &exc->GS.projVector );
Compute_Funcs( exc );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SZP0[]: Set Zone Pointer 0
* Opcode range: 0x13
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SZP0( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
switch ( (FT_Int)args[0] )
1999-12-17 00:11:37 +01:00
{
case 0:
exc->zp0 = exc->twilight;
1999-12-17 00:11:37 +01:00
break;
case 1:
exc->zp0 = exc->pts;
1999-12-17 00:11:37 +01:00
break;
default:
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
exc->GS.gep0 = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SZP1[]: Set Zone Pointer 1
* Opcode range: 0x14
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SZP1( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
switch ( (FT_Int)args[0] )
1999-12-17 00:11:37 +01:00
{
case 0:
exc->zp1 = exc->twilight;
1999-12-17 00:11:37 +01:00
break;
case 1:
exc->zp1 = exc->pts;
1999-12-17 00:11:37 +01:00
break;
default:
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
exc->GS.gep1 = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SZP2[]: Set Zone Pointer 2
* Opcode range: 0x15
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SZP2( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
switch ( (FT_Int)args[0] )
1999-12-17 00:11:37 +01:00
{
case 0:
exc->zp2 = exc->twilight;
1999-12-17 00:11:37 +01:00
break;
case 1:
exc->zp2 = exc->pts;
1999-12-17 00:11:37 +01:00
break;
default:
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
exc->GS.gep2 = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SZPS[]: Set Zone PointerS
* Opcode range: 0x16
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SZPS( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
switch ( (FT_Int)args[0] )
1999-12-17 00:11:37 +01:00
{
case 0:
exc->zp0 = exc->twilight;
1999-12-17 00:11:37 +01:00
break;
case 1:
exc->zp0 = exc->pts;
1999-12-17 00:11:37 +01:00
break;
default:
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
exc->zp1 = exc->zp0;
exc->zp2 = exc->zp0;
1999-12-17 00:11:37 +01:00
exc->GS.gep0 = (FT_UShort)args[0];
exc->GS.gep1 = (FT_UShort)args[0];
exc->GS.gep2 = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* INSTCTRL[]: INSTruction ConTRoL
* Opcode range: 0x8E
* Stack: int32 int32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_INSTCTRL( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong K, L, Kf;
1999-12-17 00:11:37 +01:00
K = (FT_ULong)args[1];
L = (FT_ULong)args[0];
1999-12-17 00:11:37 +01:00
/* selector values cannot be `OR'ed; */
/* they are indices starting with index 1, not flags */
if ( K < 1 || K > 3 )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
/* convert index to flag value */
Kf = 1 << ( K - 1 );
1999-12-17 00:11:37 +01:00
if ( L != 0 )
{
/* arguments to selectors look like flag values */
if ( L != Kf )
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
return;
}
}
1999-12-17 00:11:37 +01:00
/* INSTCTRL should only be used in the CVT program */
if ( exc->iniRange == tt_coderange_cvt )
{
exc->GS.instruct_control &= ~(FT_Byte)Kf;
exc->GS.instruct_control |= (FT_Byte)L;
}
/* except to change the subpixel flags temporarily */
else if ( exc->iniRange == tt_coderange_glyph && K == 3 )
{
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
/* Native ClearType fonts sign a waiver that turns off all backward */
/* compatibility hacks and lets them program points to the grid like */
/* it's 1996. They might sign a waiver for just one glyph, though. */
if ( SUBPIXEL_HINTING_MINIMAL )
exc->backward_compatibility = !FT_BOOL( L == 4 );
#endif
}
else if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* SCANCTRL[]: SCAN ConTRoL
* Opcode range: 0x85
* Stack: uint32? -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SCANCTRL( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_Int A;
1999-12-17 00:11:37 +01:00
/* Get Threshold */
A = (FT_Int)( args[0] & 0xFF );
1999-12-17 00:11:37 +01:00
if ( A == 0xFF )
{
exc->GS.scan_control = TRUE;
1999-12-17 00:11:37 +01:00
return;
}
else if ( A == 0 )
{
exc->GS.scan_control = FALSE;
1999-12-17 00:11:37 +01:00
return;
}
if ( ( args[0] & 0x100 ) != 0 && exc->tt_metrics.ppem <= A )
exc->GS.scan_control = TRUE;
1999-12-17 00:11:37 +01:00
if ( ( args[0] & 0x200 ) != 0 && exc->tt_metrics.rotated )
exc->GS.scan_control = TRUE;
1999-12-17 00:11:37 +01:00
if ( ( args[0] & 0x400 ) != 0 && exc->tt_metrics.stretched )
exc->GS.scan_control = TRUE;
1999-12-17 00:11:37 +01:00
if ( ( args[0] & 0x800 ) != 0 && exc->tt_metrics.ppem > A )
exc->GS.scan_control = FALSE;
1999-12-17 00:11:37 +01:00
if ( ( args[0] & 0x1000 ) != 0 && exc->tt_metrics.rotated )
exc->GS.scan_control = FALSE;
1999-12-17 00:11:37 +01:00
if ( ( args[0] & 0x2000 ) != 0 && exc->tt_metrics.stretched )
exc->GS.scan_control = FALSE;
2001-06-28 09:17:51 +02:00
}
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* SCANTYPE[]: SCAN TYPE
* Opcode range: 0x8D
* Stack: uint16 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SCANTYPE( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
if ( args[0] >= 0 )
exc->GS.scan_type = (FT_Int)args[0] & 0xFFFF;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MANAGING OUTLINES
*
*/
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* FLIPPT[]: FLIP PoinT
* Opcode range: 0x80
* Stack: uint32... -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_FLIPPT( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
FT_UShort point;
1999-12-17 00:11:37 +01:00
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
/* See `ttinterp.h' for details on backward compatibility mode. */
if ( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility &&
exc->iupx_called &&
exc->iupy_called )
goto Fail;
#endif
if ( exc->top < exc->GS.loop )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Too_Few_Arguments );
goto Fail;
1999-12-17 00:11:37 +01:00
}
while ( exc->GS.loop > 0 )
1999-12-17 00:11:37 +01:00
{
exc->args--;
1999-12-17 00:11:37 +01:00
point = (FT_UShort)exc->stack[exc->args];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( point, exc->pts.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
}
else
exc->pts.tags[point] ^= FT_CURVE_TAG_ON;
1999-12-17 00:11:37 +01:00
exc->GS.loop--;
1999-12-17 00:11:37 +01:00
}
Fail:
exc->GS.loop = 1;
exc->new_top = exc->args;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* FLIPRGON[]: FLIP RanGe ON
* Opcode range: 0x81
* Stack: uint32 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_FLIPRGON( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort I, K, L;
1999-12-17 00:11:37 +01:00
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
/* See `ttinterp.h' for details on backward compatibility mode. */
if ( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility &&
exc->iupx_called &&
exc->iupy_called )
return;
#endif
K = (FT_UShort)args[1];
L = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( K, exc->pts.n_points ) ||
BOUNDS( L, exc->pts.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
for ( I = L; I <= K; I++ )
exc->pts.tags[I] |= FT_CURVE_TAG_ON;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* FLIPRGOFF: FLIP RanGe OFF
* Opcode range: 0x82
* Stack: uint32 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_FLIPRGOFF( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort I, K, L;
1999-12-17 00:11:37 +01:00
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
/* See `ttinterp.h' for details on backward compatibility mode. */
if ( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility &&
exc->iupx_called &&
exc->iupy_called )
return;
#endif
K = (FT_UShort)args[1];
L = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( K, exc->pts.n_points ) ||
BOUNDS( L, exc->pts.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
for ( I = L; I <= K; I++ )
exc->pts.tags[I] &= ~FT_CURVE_TAG_ON;
1999-12-17 00:11:37 +01:00
}
2001-06-28 09:17:51 +02:00
static FT_Bool
Compute_Point_Displacement( TT_ExecContext exc,
FT_F26Dot6* x,
FT_F26Dot6* y,
TT_GlyphZone zone,
FT_UShort* refp )
1999-12-17 00:11:37 +01:00
{
TT_GlyphZoneRec zp;
FT_UShort p;
FT_F26Dot6 d;
1999-12-17 00:11:37 +01:00
if ( exc->opcode & 1 )
1999-12-17 00:11:37 +01:00
{
zp = exc->zp0;
p = exc->GS.rp1;
1999-12-17 00:11:37 +01:00
}
else
{
zp = exc->zp1;
p = exc->GS.rp2;
1999-12-17 00:11:37 +01:00
}
if ( BOUNDS( p, zp.n_points ) )
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
*refp = 0;
1999-12-17 00:11:37 +01:00
return FAILURE;
}
*zone = zp;
*refp = p;
d = PROJECT( zp.cur + p, zp.org + p );
1999-12-17 00:11:37 +01:00
*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 );
1999-12-17 00:11:37 +01:00
return SUCCESS;
}
/* See `ttinterp.h' for details on backward compatibility mode. */
2001-06-28 09:17:51 +02:00
static void
Move_Zp2_Point( TT_ExecContext exc,
FT_UShort point,
FT_F26Dot6 dx,
FT_F26Dot6 dy,
FT_Bool touch )
1999-12-17 00:11:37 +01:00
{
if ( exc->GS.freeVector.x != 0 )
1999-12-17 00:11:37 +01:00
{
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
if ( !( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility ) )
#endif
exc->zp2.cur[point].x = ADD_LONG( exc->zp2.cur[point].x, dx );
1999-12-17 00:11:37 +01:00
if ( touch )
exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
1999-12-17 00:11:37 +01:00
}
if ( exc->GS.freeVector.y != 0 )
1999-12-17 00:11:37 +01:00
{
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
if ( !( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility &&
exc->iupx_called &&
exc->iupy_called ) )
#endif
exc->zp2.cur[point].y = ADD_LONG( exc->zp2.cur[point].y, dy );
1999-12-17 00:11:37 +01:00
if ( touch )
exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y;
1999-12-17 00:11:37 +01:00
}
}
/**************************************************************************
*
* SHP[a]: SHift Point by the last point
* Opcode range: 0x32-0x33
* Stack: uint32... -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SHP( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
TT_GlyphZoneRec zp;
FT_UShort refp;
1999-12-17 00:11:37 +01:00
FT_F26Dot6 dx, dy;
FT_UShort point;
1999-12-17 00:11:37 +01:00
if ( exc->top < exc->GS.loop )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
goto Fail;
1999-12-17 00:11:37 +01:00
}
if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
1999-12-17 00:11:37 +01:00
return;
while ( exc->GS.loop > 0 )
1999-12-17 00:11:37 +01:00
{
exc->args--;
point = (FT_UShort)exc->stack[exc->args];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( point, exc->zp2.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
}
else
Move_Zp2_Point( exc, point, dx, dy, TRUE );
1999-12-17 00:11:37 +01:00
exc->GS.loop--;
1999-12-17 00:11:37 +01:00
}
Fail:
exc->GS.loop = 1;
exc->new_top = exc->args;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* 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.
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SHC( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
2012-02-26 08:47:20 +01:00
TT_GlyphZoneRec zp;
FT_UShort refp;
FT_F26Dot6 dx, dy;
1999-12-17 00:11:37 +01:00
FT_Short contour, bounds;
FT_UShort start, limit, i;
1999-12-17 00:11:37 +01:00
contour = (FT_Short)args[0];
bounds = ( exc->GS.gep2 == 0 ) ? 1 : exc->zp2.n_contours;
1999-12-17 00:11:37 +01:00
if ( BOUNDS( contour, bounds ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
1999-12-17 00:11:37 +01:00
return;
if ( contour == 0 )
start = 0;
1999-12-17 00:11:37 +01:00
else
start = (FT_UShort)( exc->zp2.contours[contour - 1] + 1 -
exc->zp2.first_point );
1999-12-17 00:11:37 +01:00
/* 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 );
1999-12-17 00:11:37 +01:00
for ( i = start; i < limit; i++ )
1999-12-17 00:11:37 +01:00
{
if ( zp.cur != exc->zp2.cur || refp != i )
Move_Zp2_Point( exc, i, dx, dy, TRUE );
1999-12-17 00:11:37 +01:00
}
}
/**************************************************************************
*
* SHZ[a]: SHift Zone
* Opcode range: 0x36-37
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SHZ( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
TT_GlyphZoneRec zp;
FT_UShort refp;
FT_F26Dot6 dx,
dy;
1999-12-17 00:11:37 +01:00
FT_UShort limit, i;
1999-12-17 00:11:37 +01:00
if ( BOUNDS( args[0], 2 ) )
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
1999-12-17 00:11:37 +01:00
return;
2012-02-29 13:59:49 +01:00
/* 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 );
1999-12-17 00:11:37 +01:00
else
limit = 0;
1999-12-17 00:11:37 +01:00
/* XXX: UNDOCUMENTED! SHZ doesn't touch the points */
for ( i = 0; i < limit; i++ )
1999-12-17 00:11:37 +01:00
{
if ( zp.cur != exc->zp2.cur || refp != i )
Move_Zp2_Point( exc, i, dx, dy, FALSE );
1999-12-17 00:11:37 +01:00
}
}
/**************************************************************************
*
* SHPIX[]: SHift points by a PIXel amount
* Opcode range: 0x38
* Stack: f26.6 uint32... -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_SHPIX( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 dx, dy;
FT_UShort point;
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
2016-12-25 22:55:25 +01:00
FT_Bool in_twilight = FT_BOOL( exc->GS.gep0 == 0 ||
exc->GS.gep1 == 0 ||
exc->GS.gep2 == 0 );
#endif
1999-12-17 00:11:37 +01:00
if ( exc->top < exc->GS.loop + 1 )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
goto Fail;
1999-12-17 00:11:37 +01:00
}
dx = TT_MulFix14( args[0], exc->GS.freeVector.x );
dy = TT_MulFix14( args[0], exc->GS.freeVector.y );
1999-12-17 00:11:37 +01:00
while ( exc->GS.loop > 0 )
1999-12-17 00:11:37 +01:00
{
exc->args--;
1999-12-17 00:11:37 +01:00
point = (FT_UShort)exc->stack[exc->args];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( point, exc->zp2.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
}
else
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
if ( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility )
{
/* Special case: allow SHPIX to move points in the twilight zone. */
/* Otherwise, treat SHPIX the same as DELTAP. Unbreaks various */
/* fonts such as older versions of Rokkitt and DTL Argo T Light */
2017-01-19 05:12:31 +01:00
/* that would glitch severely after calling ALIGNRP after a */
/* blocked SHPIX. */
if ( in_twilight ||
( !( exc->iupx_called && exc->iupy_called ) &&
( ( exc->is_composite && exc->GS.freeVector.y != 0 ) ||
( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y ) ) ) )
Move_Zp2_Point( exc, point, 0, dy, TRUE );
}
else
#endif
Move_Zp2_Point( exc, point, dx, dy, TRUE );
exc->GS.loop--;
1999-12-17 00:11:37 +01:00
}
Fail:
exc->GS.loop = 1;
exc->new_top = exc->args;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MSIRP[a]: Move Stack Indirect Relative Position
* Opcode range: 0x3A-0x3B
* Stack: f26.6 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MSIRP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort point = 0;
FT_F26Dot6 distance;
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
point = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( point, exc->zp1.n_points ) ||
BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
/* UNDOCUMENTED! The MS rasterizer does that with */
/* twilight points (confirmed by Greg Hitchcock) */
if ( exc->GS.gep1 == 0 )
1999-12-17 00:11:37 +01:00
{
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];
1999-12-17 00:11:37 +01:00
}
distance = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 );
1999-12-17 00:11:37 +01:00
exc->func_move( exc,
&exc->zp1,
point,
SUB_LONG( args[1], distance ) );
1999-12-17 00:11:37 +01:00
exc->GS.rp1 = exc->GS.rp0;
exc->GS.rp2 = point;
1999-12-17 00:11:37 +01:00
if ( ( exc->opcode & 1 ) != 0 )
exc->GS.rp0 = point;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MDAP[a]: Move Direct Absolute Point
* Opcode range: 0x2E-0x2F
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MDAP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort point;
FT_F26Dot6 cur_dist;
FT_F26Dot6 distance;
1999-12-17 00:11:37 +01:00
point = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( point, exc->zp0.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
if ( ( exc->opcode & 1 ) != 0 )
1999-12-17 00:11:37 +01:00
{
cur_dist = FAST_PROJECT( &exc->zp0.cur[point] );
distance = SUB_LONG( exc->func_round( exc, cur_dist, 3 ), cur_dist );
1999-12-17 00:11:37 +01:00
}
else
distance = 0;
exc->func_move( exc, &exc->zp0, point, distance );
1999-12-17 00:11:37 +01:00
exc->GS.rp0 = point;
exc->GS.rp1 = point;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MIAP[a]: Move Indirect Absolute Point
* Opcode range: 0x3E-0x3F
* Stack: uint32 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MIAP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong cvtEntry;
FT_UShort point;
FT_F26Dot6 distance;
FT_F26Dot6 org_dist;
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
1999-12-17 00:11:37 +01:00
cvtEntry = (FT_ULong)args[1];
point = (FT_UShort)args[0];
if ( BOUNDS( point, exc->zp0.n_points ) ||
BOUNDSL( cvtEntry, exc->cvtSize ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
goto Fail;
1999-12-17 00:11:37 +01:00
}
/* 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. */
1999-12-17 00:11:37 +01:00
distance = exc->func_read_cvt( exc, cvtEntry );
1999-12-17 00:11:37 +01:00
if ( exc->GS.gep0 == 0 ) /* If in twilight zone */
1999-12-17 00:11:37 +01:00
{
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];
1999-12-17 00:11:37 +01:00
}
org_dist = FAST_PROJECT( &exc->zp0.cur[point] );
1999-12-17 00:11:37 +01:00
if ( ( exc->opcode & 1 ) != 0 ) /* rounding and control cut-in flag */
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 control_value_cutin = exc->GS.control_value_cutin;
FT_F26Dot6 delta;
delta = SUB_LONG( distance, org_dist );
if ( delta < 0 )
delta = NEG_LONG( delta );
if ( delta > control_value_cutin )
1999-12-17 00:11:37 +01:00
distance = org_dist;
distance = exc->func_round( exc, distance, 3 );
1999-12-17 00:11:37 +01:00
}
exc->func_move( exc, &exc->zp0, point, SUB_LONG( distance, org_dist ) );
1999-12-17 00:11:37 +01:00
Fail:
exc->GS.rp0 = point;
exc->GS.rp1 = point;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MDRP[abcde]: Move Direct Relative Point
* Opcode range: 0xC0-0xDF
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MDRP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort point = 0;
FT_F26Dot6 org_dist, distance;
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
1999-12-17 00:11:37 +01:00
point = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( point, exc->zp1.n_points ) ||
BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
goto Fail;
1999-12-17 00:11:37 +01:00
}
/* XXX: Is there some undocumented feature while in the */
/* twilight zone? */
2007-03-21 14:30:14 +01:00
/* 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];
2007-03-21 14:30:14 +01:00
org_dist = DUALPROJ( vec1, vec2 );
}
else
{
FT_Vector* vec1 = &exc->zp1.orus[point];
FT_Vector* vec2 = &exc->zp0.orus[exc->GS.rp0];
2006-08-19 13:18:09 +02:00
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;
2007-03-21 14:30:14 +01:00
vec.x = FT_MulFix( SUB_LONG( vec1->x, vec2->x ),
exc->metrics.x_scale );
vec.y = FT_MulFix( SUB_LONG( vec1->y, vec2->y ),
exc->metrics.y_scale );
org_dist = FAST_DUALPROJ( &vec );
}
}
2006-08-19 13:18:09 +02:00
/* single width cut-in test */
1999-12-17 00:11:37 +01:00
/* |org_dist - single_width_value| < single_width_cutin */
if ( exc->GS.single_width_cutin > 0 &&
org_dist < exc->GS.single_width_value +
exc->GS.single_width_cutin &&
org_dist > exc->GS.single_width_value -
exc->GS.single_width_cutin )
1999-12-17 00:11:37 +01:00
{
if ( org_dist >= 0 )
org_dist = exc->GS.single_width_value;
1999-12-17 00:11:37 +01:00
else
org_dist = -exc->GS.single_width_value;
1999-12-17 00:11:37 +01:00
}
/* round flag */
if ( ( exc->opcode & 4 ) != 0 )
{
distance = exc->func_round( exc, org_dist, exc->opcode & 3 );
}
1999-12-17 00:11:37 +01:00
else
distance = Round_None( exc, org_dist, exc->opcode & 3 );
1999-12-17 00:11:37 +01:00
/* minimum distance flag */
if ( ( exc->opcode & 8 ) != 0 )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 minimum_distance = exc->GS.minimum_distance;
1999-12-17 00:11:37 +01:00
if ( org_dist >= 0 )
{
if ( distance < minimum_distance )
distance = minimum_distance;
1999-12-17 00:11:37 +01:00
}
else
{
if ( distance > NEG_LONG( minimum_distance ) )
distance = NEG_LONG( minimum_distance );
1999-12-17 00:11:37 +01:00
}
}
/* now move the point */
org_dist = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 );
1999-12-17 00:11:37 +01:00
exc->func_move( exc, &exc->zp1, point, SUB_LONG( distance, org_dist ) );
1999-12-17 00:11:37 +01:00
Fail:
exc->GS.rp1 = exc->GS.rp0;
exc->GS.rp2 = point;
1999-12-17 00:11:37 +01:00
if ( ( exc->opcode & 16 ) != 0 )
exc->GS.rp0 = point;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MIRP[abcde]: Move Indirect Relative Point
* Opcode range: 0xE0-0xFF
* Stack: int32? uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_MIRP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort point;
FT_ULong cvtEntry;
1999-12-17 00:11:37 +01:00
FT_F26Dot6 cvt_dist,
1999-12-17 00:11:37 +01:00
distance,
cur_dist,
org_dist;
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
FT_F26Dot6 delta;
1999-12-17 00:11:37 +01:00
point = (FT_UShort)args[0];
cvtEntry = (FT_ULong)( ADD_LONG( args[1], 1 ) );
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
1999-12-17 00:11:37 +01:00
/* 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 ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
goto Fail;
1999-12-17 00:11:37 +01:00
}
if ( !cvtEntry )
cvt_dist = 0;
else
cvt_dist = exc->func_read_cvt( exc, cvtEntry - 1 );
1999-12-17 00:11:37 +01:00
/* single width test */
delta = SUB_LONG( cvt_dist, exc->GS.single_width_value );
if ( delta < 0 )
delta = NEG_LONG( delta );
if ( delta < exc->GS.single_width_cutin )
1999-12-17 00:11:37 +01:00
{
if ( cvt_dist >= 0 )
cvt_dist = exc->GS.single_width_value;
1999-12-17 00:11:37 +01:00
else
cvt_dist = -exc->GS.single_width_value;
1999-12-17 00:11:37 +01:00
}
/* UNDOCUMENTED! The MS rasterizer does that with */
/* twilight points (confirmed by Greg Hitchcock) */
if ( exc->GS.gep1 == 0 )
1999-12-17 00:11:37 +01:00
{
exc->zp1.org[point].x = ADD_LONG(
exc->zp0.org[exc->GS.rp0].x,
TT_MulFix14( cvt_dist,
exc->GS.freeVector.x ) );
exc->zp1.org[point].y = ADD_LONG(
exc->zp0.org[exc->GS.rp0].y,
TT_MulFix14( cvt_dist,
exc->GS.freeVector.y ) );
exc->zp1.cur[point] = exc->zp1.org[point];
1999-12-17 00:11:37 +01:00
}
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] );
1999-12-17 00:11:37 +01:00
/* auto-flip test */
if ( exc->GS.auto_flip )
1999-12-17 00:11:37 +01:00
{
if ( ( org_dist ^ cvt_dist ) < 0 )
cvt_dist = NEG_LONG( cvt_dist );
1999-12-17 00:11:37 +01:00
}
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
/* control value cut-in and round */
1999-12-17 00:11:37 +01:00
if ( ( exc->opcode & 4 ) != 0 )
1999-12-17 00:11:37 +01:00
{
/* XXX: UNDOCUMENTED! Only perform cut-in test when both points */
/* refer to the same zone. */
if ( exc->GS.gep0 == exc->GS.gep1 )
{
FT_F26Dot6 control_value_cutin = exc->GS.control_value_cutin;
/* 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 `>'. */
delta = SUB_LONG( cvt_dist, org_dist );
if ( delta < 0 )
delta = NEG_LONG( delta );
if ( delta > control_value_cutin )
1999-12-17 00:11:37 +01:00
cvt_dist = org_dist;
}
1999-12-17 00:11:37 +01:00
distance = exc->func_round( exc, cvt_dist, exc->opcode & 3 );
1999-12-17 00:11:37 +01:00
}
else
distance = Round_None( exc, cvt_dist, exc->opcode & 3 );
1999-12-17 00:11:37 +01:00
/* minimum distance test */
if ( ( exc->opcode & 8 ) != 0 )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 minimum_distance = exc->GS.minimum_distance;
1999-12-17 00:11:37 +01:00
if ( org_dist >= 0 )
{
if ( distance < minimum_distance )
distance = minimum_distance;
1999-12-17 00:11:37 +01:00
}
else
{
if ( distance > NEG_LONG( minimum_distance ) )
distance = NEG_LONG( minimum_distance );
1999-12-17 00:11:37 +01:00
}
}
exc->func_move( exc,
&exc->zp1,
point,
SUB_LONG( distance, cur_dist ) );
Fail:
exc->GS.rp1 = exc->GS.rp0;
1999-12-17 00:11:37 +01:00
if ( ( exc->opcode & 16 ) != 0 )
exc->GS.rp0 = point;
1999-12-17 00:11:37 +01:00
exc->GS.rp2 = point;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ALIGNRP[]: ALIGN Relative Point
* Opcode range: 0x3C
* Stack: uint32 uint32... -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ALIGNRP( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
FT_UShort point;
FT_F26Dot6 distance;
1999-12-17 00:11:37 +01:00
2015-01-11 00:56:55 +01:00
if ( exc->top < exc->GS.loop ||
BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
goto Fail;
1999-12-17 00:11:37 +01:00
}
while ( exc->GS.loop > 0 )
1999-12-17 00:11:37 +01:00
{
exc->args--;
1999-12-17 00:11:37 +01:00
point = (FT_UShort)exc->stack[exc->args];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( point, exc->zp1.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
}
else
{
distance = PROJECT( exc->zp1.cur + point,
exc->zp0.cur + exc->GS.rp0 );
1999-12-17 00:11:37 +01:00
exc->func_move( exc, &exc->zp1, point, NEG_LONG( distance ) );
1999-12-17 00:11:37 +01:00
}
exc->GS.loop--;
1999-12-17 00:11:37 +01:00
}
Fail:
exc->GS.loop = 1;
exc->new_top = exc->args;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ISECT[]: moves point to InterSECTion
* Opcode range: 0x0F
* Stack: 5 * uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ISECT( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort point,
1999-12-17 00:11:37 +01:00
a0, a1,
b0, b1;
FT_F26Dot6 discriminant, dotproduct;
1999-12-17 00:11:37 +01:00
FT_F26Dot6 dx, dy,
1999-12-17 00:11:37 +01:00
dax, day,
dbx, dby;
FT_F26Dot6 val;
1999-12-17 00:11:37 +01:00
FT_Vector R;
1999-12-17 00:11:37 +01:00
point = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
a0 = (FT_UShort)args[1];
a1 = (FT_UShort)args[2];
b0 = (FT_UShort)args[3];
b1 = (FT_UShort)args[4];
1999-12-17 00:11:37 +01:00
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 ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
/* Cramer's rule */
dbx = SUB_LONG( exc->zp0.cur[b1].x, exc->zp0.cur[b0].x );
dby = SUB_LONG( exc->zp0.cur[b1].y, exc->zp0.cur[b0].y );
1999-12-17 00:11:37 +01:00
dax = SUB_LONG( exc->zp1.cur[a1].x, exc->zp1.cur[a0].x );
day = SUB_LONG( exc->zp1.cur[a1].y, exc->zp1.cur[a0].y );
1999-12-17 00:11:37 +01:00
dx = SUB_LONG( exc->zp0.cur[b0].x, exc->zp1.cur[a0].x );
dy = SUB_LONG( exc->zp0.cur[b0].y, exc->zp1.cur[a0].y );
1999-12-17 00:11:37 +01:00
discriminant = ADD_LONG( FT_MulDiv( dax, NEG_LONG( dby ), 0x40 ),
FT_MulDiv( day, dbx, 0x40 ) );
dotproduct = ADD_LONG( FT_MulDiv( dax, dbx, 0x40 ),
FT_MulDiv( day, dby, 0x40 ) );
/* The discriminant above is actually a cross product of vectors */
/* da and db. Together with the dot product, they can be used as */
/* surrogates for sine and cosine of the angle between the vectors. */
/* Indeed, */
/* dotproduct = |da||db|cos(angle) */
/* discriminant = |da||db|sin(angle) . */
/* We use these equations to reject grazing intersections by */
/* thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. */
if ( MUL_LONG( 19, FT_ABS( discriminant ) ) > FT_ABS( dotproduct ) )
1999-12-17 00:11:37 +01:00
{
val = ADD_LONG( FT_MulDiv( dx, NEG_LONG( dby ), 0x40 ),
FT_MulDiv( dy, dbx, 0x40 ) );
1999-12-17 00:11:37 +01:00
R.x = FT_MulDiv( val, dax, discriminant );
R.y = FT_MulDiv( val, day, discriminant );
1999-12-17 00:11:37 +01:00
/* XXX: Block in backward_compatibility and/or post-IUP? */
exc->zp2.cur[point].x = ADD_LONG( exc->zp1.cur[a0].x, R.x );
exc->zp2.cur[point].y = ADD_LONG( exc->zp1.cur[a0].y, R.y );
1999-12-17 00:11:37 +01:00
}
else
{
/* else, take the middle of the middles of A and B */
/* XXX: Block in backward_compatibility and/or post-IUP? */
exc->zp2.cur[point].x =
ADD_LONG( ADD_LONG( exc->zp1.cur[a0].x, exc->zp1.cur[a1].x ),
ADD_LONG( exc->zp0.cur[b0].x, exc->zp0.cur[b1].x ) ) / 4;
exc->zp2.cur[point].y =
ADD_LONG( ADD_LONG( exc->zp1.cur[a0].y, exc->zp1.cur[a1].y ),
ADD_LONG( exc->zp0.cur[b0].y, exc->zp0.cur[b1].y ) ) / 4;
1999-12-17 00:11:37 +01:00
}
2015-09-01 05:14:46 +02:00
exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_BOTH;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* ALIGNPTS[]: ALIGN PoinTS
* Opcode range: 0x27
* Stack: uint32 uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_ALIGNPTS( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort p1, p2;
FT_F26Dot6 distance;
1999-12-17 00:11:37 +01:00
p1 = (FT_UShort)args[0];
p2 = (FT_UShort)args[1];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( p1, exc->zp1.n_points ) ||
BOUNDS( p2, exc->zp0.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
distance = PROJECT( exc->zp0.cur + p2, exc->zp1.cur + p1 ) / 2;
1999-12-17 00:11:37 +01:00
exc->func_move( exc, &exc->zp1, p1, distance );
exc->func_move( exc, &exc->zp0, p2, NEG_LONG( distance ) );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* IP[]: Interpolate Point
* Opcode range: 0x39
* Stack: uint32... -->
*/
/* SOMETIMES, DUMBER CODE IS BETTER CODE */
2007-02-16 09:10:17 +01:00
2001-06-28 09:17:51 +02:00
static void
Ins_IP( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 old_range, cur_range;
FT_Vector* orus_base;
FT_Vector* cur_base;
FT_Int twilight;
1999-12-17 00:11:37 +01:00
if ( exc->top < exc->GS.loop )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
goto Fail;
1999-12-17 00:11:37 +01:00
}
2007-02-16 09:10:17 +01:00
/*
* 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.
2007-02-16 09:10:17 +01:00
*/
2016-12-25 22:55:25 +01:00
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;
}
2007-03-21 14:30:14 +01:00
if ( twilight )
orus_base = &exc->zp0.org[exc->GS.rp1];
else
orus_base = &exc->zp0.orus[exc->GS.rp1];
2006-08-19 13:18:09 +02:00
cur_base = &exc->zp0.cur[exc->GS.rp1];
2007-03-21 14:30:14 +01:00
/* 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 ) )
1999-12-17 00:11:37 +01:00
{
old_range = 0;
cur_range = 0;
1999-12-17 00:11:37 +01:00
}
else
{
2007-03-21 14:30:14 +01:00
if ( twilight )
old_range = DUALPROJ( &exc->zp1.org[exc->GS.rp2], orus_base );
else if ( exc->metrics.x_scale == exc->metrics.y_scale )
old_range = DUALPROJ( &exc->zp1.orus[exc->GS.rp2], orus_base );
else
{
FT_Vector vec;
vec.x = FT_MulFix( SUB_LONG( exc->zp1.orus[exc->GS.rp2].x,
orus_base->x ),
exc->metrics.x_scale );
vec.y = FT_MulFix( SUB_LONG( exc->zp1.orus[exc->GS.rp2].y,
orus_base->y ),
exc->metrics.y_scale );
old_range = FAST_DUALPROJ( &vec );
}
cur_range = PROJECT( &exc->zp1.cur[exc->GS.rp2], cur_base );
}
for ( ; exc->GS.loop > 0; exc->GS.loop-- )
{
FT_UInt point = (FT_UInt)exc->stack[--exc->args];
FT_F26Dot6 org_dist, cur_dist, new_dist;
2006-08-19 13:18:09 +02:00
2007-02-16 09:10:17 +01:00
/* check point bounds */
if ( BOUNDS( point, exc->zp2.n_points ) )
{
if ( exc->pedantic_hinting )
{
exc->error = FT_THROW( Invalid_Reference );
return;
}
continue;
}
2006-08-19 13:18:09 +02:00
2007-03-21 14:30:14 +01:00
if ( twilight )
org_dist = DUALPROJ( &exc->zp2.org[point], orus_base );
else if ( exc->metrics.x_scale == exc->metrics.y_scale )
org_dist = DUALPROJ( &exc->zp2.orus[point], orus_base );
else
{
FT_Vector vec;
vec.x = FT_MulFix( SUB_LONG( exc->zp2.orus[point].x,
orus_base->x ),
exc->metrics.x_scale );
vec.y = FT_MulFix( SUB_LONG( exc->zp2.orus[point].y,
orus_base->y ),
exc->metrics.y_scale );
org_dist = FAST_DUALPROJ( &vec );
}
cur_dist = PROJECT( &exc->zp2.cur[point], cur_base );
if ( org_dist )
{
if ( old_range )
new_dist = FT_MulDiv( org_dist, cur_range, old_range );
else
{
/* This is the same as what MS does for the invalid case: */
/* */
/* delta = (Original_Pt - Original_RP1) - */
/* (Current_Pt - Current_RP1) ; */
/* */
/* In FreeType speak: */
/* */
/* delta = org_dist - cur_dist . */
/* */
/* We move `point' by `new_dist - cur_dist' after leaving */
/* this block, thus we have */
/* */
/* new_dist - cur_dist = delta , */
/* new_dist - cur_dist = org_dist - cur_dist , */
/* new_dist = org_dist . */
new_dist = org_dist;
}
}
else
new_dist = 0;
exc->func_move( exc,
&exc->zp2,
(FT_UShort)point,
SUB_LONG( new_dist, cur_dist ) );
}
Fail:
exc->GS.loop = 1;
exc->new_top = exc->args;
}
2007-02-16 09:10:17 +01:00
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* UTP[a]: UnTouch Point
* Opcode range: 0x29
* Stack: uint32 -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_UTP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_UShort point;
FT_Byte mask;
1999-12-17 00:11:37 +01:00
point = (FT_UShort)args[0];
1999-12-17 00:11:37 +01:00
if ( BOUNDS( point, exc->zp0.n_points ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
mask = 0xFF;
if ( exc->GS.freeVector.x != 0 )
mask &= ~FT_CURVE_TAG_TOUCH_X;
1999-12-17 00:11:37 +01:00
if ( exc->GS.freeVector.y != 0 )
mask &= ~FT_CURVE_TAG_TOUCH_Y;
1999-12-17 00:11:37 +01:00
exc->zp0.tags[point] &= mask;
1999-12-17 00:11:37 +01:00
}
/* Local variables for Ins_IUP: */
2008-05-29 00:17:28 +02:00
typedef struct IUP_WorkerRec_
1999-12-17 00:11:37 +01:00
{
FT_Vector* orgs; /* original and current coordinate */
FT_Vector* curs; /* arrays */
FT_Vector* orus;
FT_UInt max_points;
} IUP_WorkerRec, *IUP_Worker;
1999-12-17 00:11:37 +01:00
2001-06-28 09:17:51 +02:00
static void
iup_worker_shift_( IUP_Worker worker,
FT_UInt p1,
FT_UInt p2,
FT_UInt p )
1999-12-17 00:11:37 +01:00
{
FT_UInt i;
FT_F26Dot6 dx;
1999-12-17 00:11:37 +01:00
dx = SUB_LONG( worker->curs[p].x, worker->orgs[p].x );
if ( dx != 0 )
{
for ( i = p1; i < p; i++ )
worker->curs[i].x = ADD_LONG( worker->curs[i].x, dx );
1999-12-17 00:11:37 +01:00
for ( i = p + 1; i <= p2; i++ )
worker->curs[i].x = ADD_LONG( worker->curs[i].x, dx );
}
1999-12-17 00:11:37 +01:00
}
2001-06-28 09:17:51 +02:00
static void
iup_worker_interpolate_( IUP_Worker worker,
FT_UInt p1,
FT_UInt p2,
FT_UInt ref1,
FT_UInt ref2 )
1999-12-17 00:11:37 +01:00
{
FT_UInt i;
FT_F26Dot6 orus1, orus2, org1, org2, cur1, cur2, delta1, delta2;
1999-12-17 00:11:37 +01:00
1999-12-17 00:11:37 +01:00
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;
1999-12-17 00:11:37 +01:00
2006-08-19 13:18:09 +02:00
if ( orus1 > orus2 )
1999-12-17 00:11:37 +01:00
{
FT_F26Dot6 tmp_o;
FT_UInt tmp_r;
2006-08-19 13:18:09 +02:00
tmp_o = orus1;
orus1 = orus2;
orus2 = tmp_o;
tmp_r = ref1;
ref1 = ref2;
ref2 = tmp_r;
}
org1 = worker->orgs[ref1].x;
org2 = worker->orgs[ref2].x;
cur1 = worker->curs[ref1].x;
cur2 = worker->curs[ref2].x;
delta1 = SUB_LONG( cur1, org1 );
delta2 = SUB_LONG( cur2, org2 );
if ( cur1 == cur2 || orus1 == orus2 )
{
/* trivial snap or shift of untouched points */
1999-12-17 00:11:37 +01:00
for ( i = p1; i <= p2; i++ )
{
FT_F26Dot6 x = worker->orgs[i].x;
1999-12-17 00:11:37 +01:00
2006-08-19 13:18:09 +02:00
if ( x <= org1 )
x = ADD_LONG( x, delta1 );
else if ( x >= org2 )
x = ADD_LONG( x, delta2 );
1999-12-17 00:11:37 +01:00
else
x = cur1;
worker->curs[i].x = x;
1999-12-17 00:11:37 +01:00
}
}
else
1999-12-17 00:11:37 +01:00
{
FT_Fixed scale = 0;
FT_Bool scale_valid = 0;
2006-08-19 13:18:09 +02:00
/* interpolation */
1999-12-17 00:11:37 +01:00
for ( i = p1; i <= p2; i++ )
{
FT_F26Dot6 x = worker->orgs[i].x;
1999-12-17 00:11:37 +01:00
2006-08-19 13:18:09 +02:00
if ( x <= org1 )
x = ADD_LONG( x, delta1 );
1999-12-17 00:11:37 +01:00
else if ( x >= org2 )
x = ADD_LONG( x, delta2 );
1999-12-17 00:11:37 +01:00
else
{
if ( !scale_valid )
{
scale_valid = 1;
scale = FT_DivFix( SUB_LONG( cur2, cur1 ),
SUB_LONG( orus2, orus1 ) );
}
x = ADD_LONG( cur1,
FT_MulFix( SUB_LONG( worker->orus[i].x, orus1 ),
scale ) );
}
worker->curs[i].x = x;
1999-12-17 00:11:37 +01:00
}
}
}
/**************************************************************************
*
* IUP[a]: Interpolate Untouched Points
* Opcode range: 0x30-0x31
* Stack: -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_IUP( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
IUP_WorkerRec V;
FT_Byte mask;
1999-12-17 00:11:37 +01:00
FT_UInt first_point; /* first point of contour */
FT_UInt end_point; /* end point (last+1) of contour */
1999-12-17 00:11:37 +01:00
FT_UInt first_touched; /* first touched point in contour */
FT_UInt cur_touched; /* current touched point in contour */
1999-12-17 00:11:37 +01:00
FT_UInt point; /* current point */
FT_Short contour; /* current contour */
1999-12-17 00:11:37 +01:00
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
/* See `ttinterp.h' for details on backward compatibility mode. */
/* Allow IUP until it has been called on both axes. Immediately */
/* return on subsequent ones. */
if ( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility )
{
if ( exc->iupx_called && exc->iupy_called )
return;
if ( exc->opcode & 1 )
exc->iupx_called = TRUE;
else
exc->iupy_called = TRUE;
}
#endif
/* ignore empty outlines */
if ( exc->pts.n_contours == 0 )
return;
if ( exc->opcode & 1 )
1999-12-17 00:11:37 +01:00
{
mask = FT_CURVE_TAG_TOUCH_X;
V.orgs = exc->pts.org;
V.curs = exc->pts.cur;
V.orus = exc->pts.orus;
1999-12-17 00:11:37 +01:00
}
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 );
1999-12-17 00:11:37 +01:00
}
V.max_points = exc->pts.n_points;
1999-12-17 00:11:37 +01:00
contour = 0;
point = 0;
do
{
end_point = exc->pts.contours[contour] - exc->pts.first_point;
1999-12-17 00:11:37 +01:00
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 )
1999-12-17 00:11:37 +01:00
point++;
if ( point <= end_point )
{
first_touched = point;
cur_touched = point;
point++;
while ( point <= end_point )
{
if ( ( exc->pts.tags[point] & mask ) != 0 )
1999-12-17 00:11:37 +01:00
{
iup_worker_interpolate_( &V,
cur_touched + 1,
point - 1,
cur_touched,
point );
1999-12-17 00:11:37 +01:00
cur_touched = point;
}
point++;
}
if ( cur_touched == first_touched )
iup_worker_shift_( &V, first_point, end_point, cur_touched );
1999-12-17 00:11:37 +01:00
else
{
iup_worker_interpolate_( &V,
(FT_UShort)( cur_touched + 1 ),
end_point,
cur_touched,
first_touched );
1999-12-17 00:11:37 +01:00
if ( first_touched > 0 )
iup_worker_interpolate_( &V,
first_point,
first_touched - 1,
cur_touched,
first_touched );
1999-12-17 00:11:37 +01:00
}
}
contour++;
} while ( contour < exc->pts.n_contours );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* DELTAPn[]: DELTA exceptions P1, P2, P3
* Opcode range: 0x5D,0x71,0x72
* Stack: uint32 (2 * uint32)... -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_DELTAP( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong nump, k;
FT_UShort A;
FT_ULong C, P;
FT_Long B;
P = (FT_ULong)exc->func_cur_ppem( exc );
nump = (FT_ULong)args[0]; /* some points theoretically may occur more
1999-12-17 00:11:37 +01:00
than once, thus UShort isn't enough */
for ( k = 1; k <= nump; k++ )
{
if ( exc->args < 2 )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Too_Few_Arguments );
exc->args = 0;
goto Fail;
1999-12-17 00:11:37 +01:00
}
exc->args -= 2;
1999-12-17 00:11:37 +01:00
A = (FT_UShort)exc->stack[exc->args + 1];
B = exc->stack[exc->args];
1999-12-17 00:11:37 +01:00
/* 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. */
1999-12-17 00:11:37 +01:00
if ( !BOUNDS( A, exc->zp0.n_points ) )
1999-12-17 00:11:37 +01:00
{
C = ( (FT_ULong)B & 0xF0 ) >> 4;
1999-12-17 00:11:37 +01:00
switch ( exc->opcode )
1999-12-17 00:11:37 +01:00
{
case 0x5D:
break;
case 0x71:
C += 16;
break;
case 0x72:
C += 32;
break;
}
C += exc->GS.delta_base;
1999-12-17 00:11:37 +01:00
if ( P == C )
1999-12-17 00:11:37 +01:00
{
B = ( (FT_ULong)B & 0xF ) - 8;
1999-12-17 00:11:37 +01:00
if ( B >= 0 )
B++;
B *= 1L << ( 6 - exc->GS.delta_shift );
1999-12-17 00:11:37 +01:00
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
/* See `ttinterp.h' for details on backward compatibility */
/* mode. */
if ( SUBPIXEL_HINTING_MINIMAL &&
exc->backward_compatibility )
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
{
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 );
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
}
else
#endif
exc->func_move( exc, &exc->zp0, A, B );
1999-12-17 00:11:37 +01:00
}
}
else
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
}
Fail:
exc->new_top = exc->args;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* DELTACn[]: DELTA exceptions C1, C2, C3
* Opcode range: 0x73,0x74,0x75
* Stack: uint32 (2 * uint32)... -->
*/
2001-06-28 09:17:51 +02:00
static void
Ins_DELTAC( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_ULong nump, k;
FT_ULong A, C, P;
FT_Long B;
1999-12-17 00:11:37 +01:00
2003-04-29 15:23:47 +02:00
P = (FT_ULong)exc->func_cur_ppem( exc );
nump = (FT_ULong)args[0];
1999-12-17 00:11:37 +01:00
for ( k = 1; k <= nump; k++ )
{
if ( exc->args < 2 )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
exc->error = FT_THROW( Too_Few_Arguments );
exc->args = 0;
goto Fail;
1999-12-17 00:11:37 +01:00
}
exc->args -= 2;
1999-12-17 00:11:37 +01:00
A = (FT_ULong)exc->stack[exc->args + 1];
B = exc->stack[exc->args];
1999-12-17 00:11:37 +01:00
if ( BOUNDSL( A, exc->cvtSize ) )
1999-12-17 00:11:37 +01:00
{
if ( exc->pedantic_hinting )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
return;
}
}
else
{
C = ( (FT_ULong)B & 0xF0 ) >> 4;
1999-12-17 00:11:37 +01:00
switch ( exc->opcode )
1999-12-17 00:11:37 +01:00
{
case 0x73:
break;
case 0x74:
C += 16;
break;
case 0x75:
C += 32;
break;
}
C += exc->GS.delta_base;
1999-12-17 00:11:37 +01:00
if ( P == C )
1999-12-17 00:11:37 +01:00
{
B = ( (FT_ULong)B & 0xF ) - 8;
1999-12-17 00:11:37 +01:00
if ( B >= 0 )
B++;
B *= 1L << ( 6 - exc->GS.delta_shift );
1999-12-17 00:11:37 +01:00
exc->func_move_cvt( exc, A, B );
1999-12-17 00:11:37 +01:00
}
}
}
Fail:
exc->new_top = exc->args;
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* MISC. INSTRUCTIONS
*
*/
1999-12-17 00:11:37 +01:00
/**************************************************************************
*
* GETINFO[]: GET INFOrmation
* Opcode range: 0x88
* Stack: uint32 --> uint32
*/
2001-06-28 09:17:51 +02:00
static void
Ins_GETINFO( TT_ExecContext exc,
FT_Long* args )
1999-12-17 00:11:37 +01:00
{
FT_Long K;
TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( exc->face );
1999-12-17 00:11:37 +01:00
K = 0;
if ( ( args[0] & 1 ) != 0 )
K = driver->interpreter_version;
[truetype] Support subpixel hinting. This is the large, famous `Infinality' patch to support ClearType bytecode which has been available from http://www.infinality.net/blog/ for some time, and which has been refined over the last years. While still experimental, it is now mature enough to be included directly into FreeType. Most of the code is based on the ClearType whitepaper written by Greg Hitchcock http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx which gives a detailed overview of the necessary changes to the Microsoft rasterizer so that older fonts are supported. However, a lot of details are still missing, and this patches provides a framework to easily handle rendering issues down to the glyph level of certain fonts. Note that ClearType support is not completely implemented! In particular, full support for the options `compatible_widths', `symmetrical_smoothing, and `bgr' (via the GETINFO bytecode instruction) is missing. * src/truetype/ttsubpix.c: New file, providing code to handle `tweaks', this is, rules for certain glyphs in certain fonts (including wildcards) which need a special treatment. * src/truetype/ttsubpix.h: New file, holding the tweaking rules. * include/freetype/config/ftoption.h, src/devel/ftoption.h (TT_CONFIG_OPTION_SUBPIXEL_HINTING): New macro. * include/freetype/internal/ftobjs.h (FT_PIX_FLOOR_GRID, FT_PIX_ROUND_GRID, FT_PIX_CEIL_GRID): New macros. * src/truetype/truetype.c [TT_USE_BYTECODE_INTERPRETER]: Include `ttsubpix.c'. * src/truetype/ttgload.c: Include `ttsubpix.h'. [All changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (tt_get_metrics): Set tweak flags. (TT_Hint_Glyph): Call `FT_Outline_EmboldenXY' if necessary. (TT_Process_Simple_Glyph): Compensate emboldening if necessary. (compute_glyph_metrics): Handle `compatible widths' option. (tt_loader_init): Handle ClearType GETINFO information bits. * src/truetype/rules.mk (TT_DRC_SRC): Updated. * src/truetype/ttinterp.c: Include `ttsubpix.h'. [Where necessary, changes below are guarded by TT_CONFIG_OPTION_SUBPIXEL_HINTING.] (Direct_Move, Direct_Move_X): Extended. (Round_None, Round_To_Grid, Round_To_Half_Grid, Round_Down_To_Grid, Round_Up_To_Grid, Round_To_Double_Grid, Round_Super, Round_Super_45, SetSuperRound): Add parameter to handle the number of grid lines per pixel. (SET_SuperRound, ROUND_None, CUR_Func_round): Updated. (DO_SROUND, DOS45ROUND, DO_ODD, DO_EVEN): Updated. (DO_ROUND, DO_NROUND): Updated. (DO_RS): Take care of `Typeman' bytecode patterns. (Ins_FDEF): Add some debugging code. Commented out. (Ins_ENDF): Restore state. (Ins_CALL, Ins_LOOPCALL): Handle inline delta functions. (Ins_MD): Handle `Vacuform' rounds. (Move_Zp2_Point, Ins_SHPIX, Ins_MSIRP, Ins_MDAP, Ins_MIAP, Ins_MDRP, Ins_MIRP): Handle tweaks. (Ins_ALIGNRP): Add tweak guard. (Ins_IUP, Ins_DELTAP): Handle tweaks. (Ins_GETINFO): Handle new ClearType bits. (TT_RunIns): Handle tweaks. * src/truetype/ttinterp.h: Updated. (SPH_TweakRule, SPH_ScaleRule): New structures for tweaks. (TT_ExecContextRec): Add members for subpixel hinting support. * src/truetype/ttobjs.h (TT_DefRecord): Add `inline_delta' member.
2012-06-18 10:36:06 +02:00
/*********************************
* GLYPH ROTATED
* Selector Bit: 1
* Return Bit(s): 8
*/
if ( ( args[0] & 2 ) != 0 && exc->tt_metrics.rotated )
K |= 1 << 8;
1999-12-17 00:11:37 +01:00
/*********************************
* 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
*/
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 )
2006-11-22 10:37:03 +01:00
K |= 1 << 12;
1999-12-17 00:11:37 +01:00
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
/* Toggle the following flags only outside of monochrome mode. */
/* Otherwise, instructions may behave weirdly and rendering results */
/* may differ between v35 and v40 mode, e.g., in `Times New Roman */
/* Bold Italic'. */
if ( SUBPIXEL_HINTING_MINIMAL && exc->subpixel_hinting_lean )
{
/*********************************
* HINTING FOR SUBPIXEL
* Selector Bit: 6
* Return Bit(s): 13
*
* v40 does subpixel hinting by default.
*/
if ( ( args[0] & 64 ) != 0 )
K |= 1 << 13;
/*********************************
* VERTICAL LCD SUBPIXELS?
* Selector Bit: 8
* Return Bit(s): 15
*/
if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd_lean )
K |= 1 << 15;
/*********************************
* SUBPIXEL POSITIONED?
* Selector Bit: 10
* Return Bit(s): 17
*
* XXX: FreeType supports it, dependent on what client does?
*/
if ( ( args[0] & 1024 ) != 0 )
K |= 1 << 17;
/*********************************
* SYMMETRICAL SMOOTHING
* Selector Bit: 11
* Return Bit(s): 18
*
* The only smoothing method FreeType supports unless someone sets
* FT_LOAD_TARGET_MONO.
*/
if ( ( args[0] & 2048 ) != 0 && exc->subpixel_hinting_lean )
K |= 1 << 18;
/*********************************
* CLEARTYPE HINTING AND
* GRAYSCALE RENDERING
* Selector Bit: 12
* Return Bit(s): 19
*
* Grayscale rendering is what FreeType does anyway unless someone
* sets FT_LOAD_TARGET_MONO or FT_LOAD_TARGET_LCD(_V)
*/
if ( ( args[0] & 4096 ) != 0 && exc->grayscale_cleartype )
K |= 1 << 19;
}
#endif
1999-12-17 00:11:37 +01:00
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;
}
if ( coords )
{
for ( i = 0; i < num_axes; i++ )
args[i] = coords[i] >> 2; /* convert 16.16 to 2.14 format */
}
else
{
for ( i = 0; i < num_axes; i++ )
args[i] = 0;
}
}
/**************************************************************************
*
* 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 */
2001-06-28 09:17:51 +02:00
static void
Ins_UNKNOWN( TT_ExecContext exc )
1999-12-17 00:11:37 +01:00
{
TT_DefRecord* def = exc->IDefs;
TT_DefRecord* limit = FT_OFFSET( def, exc->numIDefs );
2000-05-17 01:44:38 +02:00
1999-12-17 00:11:37 +01:00
for ( ; def < limit; def++ )
{
if ( (FT_Byte)def->opc == exc->opcode && def->active )
1999-12-17 00:11:37 +01:00
{
TT_CallRec* call;
2000-05-17 01:44:38 +02:00
if ( exc->callTop >= exc->callSize )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Stack_Overflow );
1999-12-17 00:11:37 +01:00
return;
}
2000-05-17 01:44:38 +02:00
call = exc->callStack + exc->callTop++;
1999-12-17 00:11:37 +01:00
call->Caller_Range = exc->curRange;
call->Caller_IP = exc->IP + 1;
1999-12-17 00:11:37 +01:00
call->Cur_Count = 1;
call->Def = def;
2000-05-17 01:44:38 +02:00
Ins_Goto_CodeRange( exc, def->range, def->start );
2000-05-17 01:44:38 +02:00
exc->step_ins = FALSE;
1999-12-17 00:11:37 +01:00
return;
}
}
exc->error = FT_THROW( Invalid_Opcode );
1999-12-17 00:11:37 +01:00
}
/**************************************************************************
*
* 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.
*
*/
1999-12-17 00:11:37 +01:00
/* documentation is in ttinterp.h */
2001-06-28 09:17:51 +02:00
FT_EXPORT_DEF( FT_Error )
2023-05-07 08:34:32 +02:00
TT_RunIns( void* exec )
1999-12-17 00:11:37 +01:00
{
2023-05-07 08:34:32 +02:00
TT_ExecContext exc = (TT_ExecContext)exec;
FT_ULong ins_counter = 0; /* executed instructions counter */
FT_ULong num_twilight_points;
FT_UShort i;
1999-12-17 00:11:37 +01:00
/* 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" ));
FT_TRACE5(( " from %d to the more reasonable value %ld\n",
exc->twilight.n_points,
num_twilight_points ));
exc->twilight.n_points = (FT_UShort)num_twilight_points;
}
/* Set up loop detectors. We restrict the number of LOOPCALL loops */
/* and the number of JMPR, JROT, and JROF calls with a negative */
/* argument to values that depend on various parameters like the */
/* size of the CVT table or the number of points in the current */
/* glyph (if applicable). */
/* */
/* The idea is that in real-world bytecode you either iterate over */
/* all CVT entries (in the `prep' table), or over all points (or */
/* contours, in the `glyf' table) of a glyph, and such iterations */
/* don't happen very often. */
exc->loopcall_counter = 0;
exc->neg_jump_counter = 0;
/* The maximum values are heuristic. */
if ( exc->pts.n_points )
exc->loopcall_counter_max = FT_MAX( 50,
10 * exc->pts.n_points ) +
FT_MAX( 50,
exc->cvtSize / 10 );
else
exc->loopcall_counter_max = 300 + 22 * exc->cvtSize;
/* as a protection against an unreasonable number of CVT entries */
/* we assume at most 100 control values per glyph for the counter */
if ( exc->loopcall_counter_max >
100 * (FT_ULong)exc->face->root.num_glyphs )
exc->loopcall_counter_max = 100 * (FT_ULong)exc->face->root.num_glyphs;
FT_TRACE5(( "TT_RunIns: Limiting total number of loops in LOOPCALL"
" to %ld\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 %ld\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 )
1999-12-17 00:11:37 +01:00
{
/* 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;
1999-12-17 00:11:37 +01:00
}
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;
1999-12-17 00:11:37 +01:00
}
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
exc->iniRange = exc->curRange;
Compute_Funcs( exc );
Compute_Round( exc, (FT_Byte)exc->GS.round_state );
1999-12-17 00:11:37 +01:00
/* These flags cancel execution of some opcodes after IUP is called */
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
exc->iupx_called = FALSE;
exc->iupy_called = FALSE;
#endif
1999-12-17 00:11:37 +01:00
do
{
exc->opcode = exc->code[exc->IP];
1999-12-17 00:11:37 +01:00
#ifdef FT_DEBUG_LEVEL_TRACE
if ( ft_trace_levels[trace_ttinterp] >= 6 )
{
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(( "%06ld ", exc->IP ));
FT_TRACE6(( "%s", 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(( " %ld", exc->stack[exc->top - n] ));
FT_TRACE6(( "\n" ));
}
#endif /* FT_DEBUG_LEVEL_TRACE */
if ( ( exc->length = opcode_length[exc->opcode] ) < 0 )
1999-12-17 00:11:37 +01:00
{
if ( exc->IP + 1 >= exc->codeSize )
1999-12-17 00:11:37 +01:00
goto LErrorCodeOverflow_;
exc->length = 2 - exc->length * exc->code[exc->IP + 1];
1999-12-17 00:11:37 +01:00
}
if ( exc->IP + exc->length > exc->codeSize )
1999-12-17 00:11:37 +01:00
goto LErrorCodeOverflow_;
/* First, let's check for empty stack and overflow */
exc->args = exc->top - ( Pop_Push_Count[exc->opcode] >> 4 );
1999-12-17 00:11:37 +01:00
/* `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 )
1999-12-17 00:11:37 +01:00
{
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;
1999-12-17 00:11:37 +01:00
}
#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, */
2023-04-26 13:15:57 +02:00
/* that 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 );
1999-12-17 00:11:37 +01:00
/* `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 )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Stack_Overflow );
1999-12-17 00:11:37 +01:00
goto LErrorLabel_;
}
exc->step_ins = TRUE;
exc->error = FT_Err_Ok;
1999-12-17 00:11:37 +01:00
{
FT_Long* args = exc->stack + exc->args;
FT_Byte opcode = exc->opcode;
1999-12-17 00:11:37 +01:00
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 );
1999-12-17 00:11:37 +01:00
break;
case 0x06: /* SPvTL // */
case 0x07: /* SPvTL + */
Ins_SPVTL( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x08: /* SFvTL // */
case 0x09: /* SFvTL + */
Ins_SFVTL( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x0A: /* SPvFS */
Ins_SPVFS( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x0B: /* SFvFS */
Ins_SFVFS( exc, args );
1999-12-17 00:11:37 +01:00
break;
2015-01-11 00:56:55 +01:00
case 0x0C: /* GPv */
Ins_GPV( exc, args );
1999-12-17 00:11:37 +01:00
break;
2015-01-11 00:56:55 +01:00
case 0x0D: /* GFv */
Ins_GFV( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x0E: /* SFvTPv */
Ins_SFVTPV( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x0F: /* ISECT */
Ins_ISECT( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x10: /* SRP0 */
Ins_SRP0( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x11: /* SRP1 */
Ins_SRP1( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x12: /* SRP2 */
Ins_SRP2( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x13: /* SZP0 */
Ins_SZP0( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x14: /* SZP1 */
Ins_SZP1( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x15: /* SZP2 */
Ins_SZP2( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x16: /* SZPS */
Ins_SZPS( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x17: /* SLOOP */
Ins_SLOOP( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x18: /* RTG */
Ins_RTG( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x19: /* RTHG */
Ins_RTHG( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x1A: /* SMD */
Ins_SMD( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x1B: /* ELSE */
Ins_ELSE( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x1C: /* JMPR */
Ins_JMPR( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x1D: /* SCVTCI */
Ins_SCVTCI( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x1E: /* SSWCI */
Ins_SSWCI( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x1F: /* SSW */
Ins_SSW( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x20: /* DUP */
Ins_DUP( args );
1999-12-17 00:11:37 +01:00
break;
case 0x21: /* POP */
Ins_POP();
1999-12-17 00:11:37 +01:00
break;
case 0x22: /* CLEAR */
Ins_CLEAR( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x23: /* SWAP */
Ins_SWAP( args );
1999-12-17 00:11:37 +01:00
break;
case 0x24: /* DEPTH */
Ins_DEPTH( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x25: /* CINDEX */
Ins_CINDEX( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x26: /* MINDEX */
Ins_MINDEX( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x27: /* ALIGNPTS */
Ins_ALIGNPTS( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x28: /* RAW */
Ins_UNKNOWN( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x29: /* UTP */
Ins_UTP( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x2A: /* LOOPCALL */
Ins_LOOPCALL( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x2B: /* CALL */
Ins_CALL( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x2C: /* FDEF */
Ins_FDEF( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x2D: /* ENDF */
Ins_ENDF( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x2E: /* MDAP */
case 0x2F: /* MDAP */
Ins_MDAP( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x30: /* IUP */
case 0x31: /* IUP */
Ins_IUP( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x32: /* SHP */
case 0x33: /* SHP */
Ins_SHP( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x34: /* SHC */
case 0x35: /* SHC */
Ins_SHC( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x36: /* SHZ */
case 0x37: /* SHZ */
Ins_SHZ( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x38: /* SHPIX */
Ins_SHPIX( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x39: /* IP */
Ins_IP( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x3A: /* MSIRP */
case 0x3B: /* MSIRP */
Ins_MSIRP( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x3C: /* AlignRP */
Ins_ALIGNRP( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x3D: /* RTDG */
Ins_RTDG( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x3E: /* MIAP */
case 0x3F: /* MIAP */
Ins_MIAP( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x40: /* NPUSHB */
Ins_NPUSHB( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x41: /* NPUSHW */
Ins_NPUSHW( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x42: /* WS */
Ins_WS( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x43: /* RS */
Ins_RS( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x44: /* WCVTP */
Ins_WCVTP( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x45: /* RCVT */
Ins_RCVT( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x46: /* GC */
case 0x47: /* GC */
Ins_GC( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x48: /* SCFS */
Ins_SCFS( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x49: /* MD */
case 0x4A: /* MD */
Ins_MD( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x4B: /* MPPEM */
Ins_MPPEM( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x4C: /* MPS */
Ins_MPS( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x4D: /* FLIPON */
Ins_FLIPON( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x4E: /* FLIPOFF */
Ins_FLIPOFF( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x4F: /* DEBUG */
Ins_DEBUG( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x50: /* LT */
Ins_LT( args );
1999-12-17 00:11:37 +01:00
break;
case 0x51: /* LTEQ */
Ins_LTEQ( args );
1999-12-17 00:11:37 +01:00
break;
case 0x52: /* GT */
Ins_GT( args );
1999-12-17 00:11:37 +01:00
break;
case 0x53: /* GTEQ */
Ins_GTEQ( args );
1999-12-17 00:11:37 +01:00
break;
case 0x54: /* EQ */
Ins_EQ( args );
1999-12-17 00:11:37 +01:00
break;
case 0x55: /* NEQ */
Ins_NEQ( args );
1999-12-17 00:11:37 +01:00
break;
case 0x56: /* ODD */
Ins_ODD( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x57: /* EVEN */
Ins_EVEN( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x58: /* IF */
Ins_IF( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x59: /* EIF */
Ins_EIF();
1999-12-17 00:11:37 +01:00
break;
case 0x5A: /* AND */
Ins_AND( args );
1999-12-17 00:11:37 +01:00
break;
case 0x5B: /* OR */
Ins_OR( args );
1999-12-17 00:11:37 +01:00
break;
case 0x5C: /* NOT */
Ins_NOT( args );
1999-12-17 00:11:37 +01:00
break;
case 0x5D: /* DELTAP1 */
Ins_DELTAP( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x5E: /* SDB */
Ins_SDB( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x5F: /* SDS */
Ins_SDS( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x60: /* ADD */
Ins_ADD( args );
1999-12-17 00:11:37 +01:00
break;
case 0x61: /* SUB */
Ins_SUB( args );
1999-12-17 00:11:37 +01:00
break;
case 0x62: /* DIV */
Ins_DIV( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x63: /* MUL */
Ins_MUL( args );
1999-12-17 00:11:37 +01:00
break;
case 0x64: /* ABS */
Ins_ABS( args );
1999-12-17 00:11:37 +01:00
break;
case 0x65: /* NEG */
Ins_NEG( args );
1999-12-17 00:11:37 +01:00
break;
case 0x66: /* FLOOR */
Ins_FLOOR( args );
1999-12-17 00:11:37 +01:00
break;
case 0x67: /* CEILING */
Ins_CEILING( args );
1999-12-17 00:11:37 +01:00
break;
case 0x68: /* ROUND */
case 0x69: /* ROUND */
case 0x6A: /* ROUND */
case 0x6B: /* ROUND */
Ins_ROUND( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x6C: /* NROUND */
case 0x6D: /* NROUND */
case 0x6E: /* NRRUND */
case 0x6F: /* NROUND */
Ins_NROUND( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x70: /* WCVTF */
Ins_WCVTF( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x71: /* DELTAP2 */
case 0x72: /* DELTAP3 */
Ins_DELTAP( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x73: /* DELTAC0 */
case 0x74: /* DELTAC1 */
case 0x75: /* DELTAC2 */
Ins_DELTAC( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x76: /* SROUND */
Ins_SROUND( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x77: /* S45Round */
Ins_S45ROUND( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x78: /* JROT */
Ins_JROT( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x79: /* JROF */
Ins_JROF( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x7A: /* ROFF */
Ins_ROFF( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x7B: /* ???? */
Ins_UNKNOWN( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x7C: /* RUTG */
Ins_RUTG( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x7D: /* RDTG */
Ins_RDTG( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x7E: /* SANGW */
Ins_SANGW();
break;
case 0x7F: /* AA */
Ins_AA();
1999-12-17 00:11:37 +01:00
break;
case 0x80: /* FLIPPT */
Ins_FLIPPT( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x81: /* FLIPRGON */
Ins_FLIPRGON( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x82: /* FLIPRGOFF */
Ins_FLIPRGOFF( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x83: /* UNKNOWN */
case 0x84: /* UNKNOWN */
Ins_UNKNOWN( exc );
1999-12-17 00:11:37 +01:00
break;
case 0x85: /* SCANCTRL */
Ins_SCANCTRL( exc, args );
1999-12-17 00:11:37 +01:00
break;
2015-01-11 00:56:55 +01:00
case 0x86: /* SDPvTL */
case 0x87: /* SDPvTL */
Ins_SDPVTL( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x88: /* GETINFO */
Ins_GETINFO( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x89: /* IDEF */
Ins_IDEF( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x8A: /* ROLL */
Ins_ROLL( args );
1999-12-17 00:11:37 +01:00
break;
case 0x8B: /* MAX */
Ins_MAX( args );
1999-12-17 00:11:37 +01:00
break;
case 0x8C: /* MIN */
Ins_MIN( args );
1999-12-17 00:11:37 +01:00
break;
case 0x8D: /* SCANTYPE */
Ins_SCANTYPE( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x8E: /* INSTCTRL */
Ins_INSTCTRL( exc, args );
1999-12-17 00:11:37 +01:00
break;
case 0x8F: /* ADJUST */
case 0x90: /* ADJUST */
Ins_UNKNOWN( exc );
1999-12-17 00:11:37 +01:00
break;
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
case 0x91:
/* it is the job of the application to `activate' GX handling, */
2023-04-26 13:15:57 +02:00
/* that 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
1999-12-17 00:11:37 +01:00
default:
if ( opcode >= 0xE0 )
Ins_MIRP( exc, args );
1999-12-17 00:11:37 +01:00
else if ( opcode >= 0xC0 )
Ins_MDRP( exc, args );
1999-12-17 00:11:37 +01:00
else if ( opcode >= 0xB8 )
Ins_PUSHW( exc, args );
1999-12-17 00:11:37 +01:00
else if ( opcode >= 0xB0 )
Ins_PUSHB( exc, args );
1999-12-17 00:11:37 +01:00
else
Ins_UNKNOWN( exc );
1999-12-17 00:11:37 +01:00
}
}
if ( exc->error )
1999-12-17 00:11:37 +01:00
{
switch ( exc->error )
1999-12-17 00:11:37 +01:00
{
/* looking for redefined instructions */
case FT_ERR( Invalid_Opcode ):
1999-12-17 00:11:37 +01:00
{
TT_DefRecord* def = exc->IDefs;
TT_DefRecord* limit = FT_OFFSET( def, exc->numIDefs );
2000-05-17 01:44:38 +02:00
1999-12-17 00:11:37 +01:00
for ( ; def < limit; def++ )
{
if ( def->active && exc->opcode == (FT_Byte)def->opc )
1999-12-17 00:11:37 +01:00
{
TT_CallRec* callrec;
2000-05-17 01:44:38 +02:00
if ( exc->callTop >= exc->callSize )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Invalid_Reference );
1999-12-17 00:11:37 +01:00
goto LErrorLabel_;
}
2000-05-17 01:44:38 +02:00
callrec = &exc->callStack[exc->callTop];
2000-05-17 01:44:38 +02:00
callrec->Caller_Range = exc->curRange;
callrec->Caller_IP = exc->IP + 1;
1999-12-17 00:11:37 +01:00
callrec->Cur_Count = 1;
callrec->Def = def;
2000-05-17 01:44:38 +02:00
if ( Ins_Goto_CodeRange( exc,
def->range,
def->start ) == FAILURE )
1999-12-17 00:11:37 +01:00
goto LErrorLabel_;
2000-05-17 01:44:38 +02:00
1999-12-17 00:11:37 +01:00
goto LSuiteLabel_;
}
}
}
exc->error = FT_THROW( Invalid_Opcode );
1999-12-17 00:11:37 +01:00
goto LErrorLabel_;
#if 0
break; /* Unreachable code warning suppression. */
/* Leave to remind in case a later change the editor */
/* to consider break; */
#endif
1999-12-17 00:11:37 +01:00
default:
goto LErrorLabel_;
#if 0
break;
#endif
1999-12-17 00:11:37 +01:00
}
}
exc->top = exc->new_top;
1999-12-17 00:11:37 +01:00
if ( exc->step_ins )
exc->IP += exc->length;
1999-12-17 00:11:37 +01:00
/* increment instruction counter and check if we didn't */
/* run this program for too long (e.g. infinite loops). */
2022-07-01 06:33:50 +02:00
if ( ++ins_counter > TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES )
{
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
exc->error = FT_THROW( Execution_Too_Long );
goto LErrorLabel_;
}
1999-12-17 00:11:37 +01:00
LSuiteLabel_:
if ( exc->IP >= exc->codeSize )
1999-12-17 00:11:37 +01:00
{
if ( exc->callTop > 0 )
1999-12-17 00:11:37 +01:00
{
exc->error = FT_THROW( Code_Overflow );
1999-12-17 00:11:37 +01:00
goto LErrorLabel_;
}
else
goto LNo_Error_;
}
} while ( !exc->instruction_trap );
1999-12-17 00:11:37 +01:00
LNo_Error_:
FT_TRACE4(( " %ld instruction%s executed\n",
ins_counter,
ins_counter == 1 ? "" : "s" ));
[truetype] Prevent glyph program state from persisting. `FDEF` instructions are specified as allowed only in 'prep' or 'fpgm'. FreeType has attempted to prevent their use in the glyph program, but they were still allowed in glyph programs if defined in a function defined in 'prep' or 'fpgm' and called from the glyph program. Similarly, `IDEF` instructions are specified not to be able to modify any existing instruction. FreeType has attempted to prevent their use in the glyph program, but they can still be used like `FDEF`. This change stores the initial bytecode range type and disallows the use of `FDEF` and `IDEF` while running the glyph program. Most other state is copied from the `TT_Size` into the execution context. However, it is possible for a glyph program to use `WS` to write to the storage area or `WCVTP`, `WCVTF`, and `DELTAC[123]` to write to the control value table. Allowing any change to the global state from the glyph program is problematic as the outlines of any given glyph may change based on the order the glyphs are loaded or even how many times they are loaded. There exist fonts that write to the storage area or the control value table in the glyph program, so their use should not be an error. Possible solutions to using these in the glyph program are * ignore the writes; * value-level copy on write, discard modified values when finished; * array-level copy on write, discard the copy when finished; * array-level copy up-front. Ignoring the writes may break otherwise good uses. A full copy up-front was implemented, but was quite heavy as even well behaved fonts required a full copy and the memory management that goes along with it. Value-level copy on write could use less memory but requires a great deal more record keeping and complexity. This change implements array-level copy on write. If any attempt is made to write to the control value table or the storage area when the initial bytecode range was in a glyph program, the relevant array will be copied to a designated storage area and the copy used for the rest of the glyph program's execution. * src/truetype/ttinterp.h (TT_ExecContextRec): New fields `iniRange`, `glyfCvtSize`, `glyfCvt`, `origCvt`, `glyfStoreSize`, `glyfStorage`, and `origStorage`. * src/truetype/ttinterp.c (Modify_CVT_Check): New function to handle `exc->glyfCvt`. (Write_CVT, Write_CVT_Stretched, Move_CVT, Move_CVT_Stretched): Use it. (Ins_WS): Handle `exc->glyfStorage`. (Ins_FDEF, Ins_IDEF): Updated. (TT_RunIns): Updated. (TT_Done_Context): Free 'glyf' CVT working and storage area. (TT_Load_Context): Fix/add casts. * src/truetype/ttgload.c (TT_Load_Simple_Glyph): Fix cast.
2021-04-01 04:31:44 +02:00
return FT_Err_Ok;
1999-12-17 00:11:37 +01:00
LErrorCodeOverflow_:
exc->error = FT_THROW( Code_Overflow );
1999-12-17 00:11:37 +01:00
LErrorLabel_:
if ( exc->error && !exc->instruction_trap )
FT_TRACE1(( " The interpreter returned error 0x%x\n", exc->error ));
return exc->error;
1999-12-17 00:11:37 +01:00
}
#else /* !TT_USE_BYTECODE_INTERPRETER */
1999-12-17 00:11:37 +01:00
/* ANSI C doesn't like empty source files */
typedef int tt_interp_dummy_;
#endif /* !TT_USE_BYTECODE_INTERPRETER */
1999-12-17 00:11:37 +01:00
1999-12-17 00:11:37 +01:00
/* END */