TTFILE.H 、、、、、、、、、、、、、、、、、、、、、、、、、、
/*******************************************************************
*
* ttfile.h 1.3
*
* File I/O Component (specification).
*
* Copyright 1996-1999 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.
*
* Changes between 1.3 and 1.2:
*
* - all functions report error values now
*
* - the stream semantics have also changed
*
* Changes between 1.2 and 1.1:
*
* - added macros to support re-entrant builds
*
* - added the TT_Duplicate_File function to duplicate streams
* (re-entrant builds only)
*
******************************************************************/
#ifndef TTFILE_H
#define TTFILE_H
#include "ttconfig.h"
#include "freetype.h"
#include "ttengine.h"
#include "ttdebug.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Initialize file component */
LOCAL_DEF
TT_Error TTFile_Init( PEngine_Instance engine );
/* Done with file component */
LOCAL_DEF
TT_Error TTFile_Done( PEngine_Instance engine );
/**********************************************************************/
/* */
/* Stream functions. */
/* */
/**********************************************************************/
/* Open a file and return a stream handle for it. */
/* Should only be used for a new face object's main stream. */
LOCAL_DEF
TT_Error TT_Open_Stream( const TT_Text* name,
TT_Stream* stream );
/* Closes, then discards, a stream when it's no longer needed. */
/* Should only be used for a stream opend with TT_Open_Stream(). */
LOCAL_DEF
TT_Error TT_Close_Stream( TT_Stream* stream );
/* Informs the component that we're going to use the file */
/* opened in 'org_stream', and report errors to the 'error' */
/* variable. */
/* in non re-entrant builds, 'org_stream' is simply copied */
/* to 'stream'. Otherwise, the latter is a duplicate handle */
/* for the file opened with 'org_stream' */
EXPORT_DEF
TT_Error TT_Use_Stream( TT_Stream org_stream,
TT_Stream* stream );
/* Informs the component that we don't need to perform file */
/* operations on the stream 'stream' anymore. This must be */
/* used with streams "opened" with TT_Use_Stream() only! */
/* in re-entrant builds, this will really discard the stream */
EXPORT_DEF
TT_Error TT_Done_Stream( TT_Stream* stream );
/* Closes the stream's file handle to release system resources */
/* The function TT_Use_Stream automatically re-activates a */
/* flushed stream when it uses one */
EXPORT_DEF
TT_Error TT_Flush_Stream( TT_Stream* stream );
/* The macros STREAM_ARGS and STREAM_ARG let us build a thread-safe */
/* or re-entrant implementation depending on a single configuration */
/*define. */
#ifdef TT_CONFIG_OPTION_THREAD_SAFE
#define STREAM_ARGS TT_Stream stream,
#define STREAM_ARG TT_Stream stream
#else
#define STREAM_ARGS /* void */
#define STREAM_ARG void
#endif /* TT_CONFIG_OPTION_THREAD_SAFE */
/****************************************************************/
/* */
/* File Functions. */
/* */
/* The following functions perform file operations on the */
/* currently 'used' stream. In thread-safe builds, only one */
/* stream can be used at a time. Synchronisation is performed */
/* through the Use_Stream()/Done_Stream() functions. */
/* */
/****************************************************************/
/* Read 'count' bytes from file into 'buffer' */
EXPORT_DEF
TT_Error TT_Read_File( STREAM_ARGS void* buffer,
Long count );
/* Seek file cursor to a given position */
EXPORT_DEF
TT_Error TT_Seek_File( STREAM_ARGS Long position );
/* Skip the next 'distance' bytes in file */
EXPORT_DEF
TT_Error TT_Skip_File( STREAM_ARGS Long distance );
/* Read the 'count' bytes at 'position' into 'buffer' */
EXPORT_DEF
TT_Error TT_Read_At_File( STREAM_ARGS Long position,
void* buffer,
Long count );
/* Return current file position */
EXPORT_DEF
Long TT_File_Pos( STREAM_ARG );
/* Return length of a given stream, even if it is flushed */
EXPORT_DEF
Long TT_Stream_Size( TT_Stream stream );
/********************************************************************/
/* */
/* Frame operations. */
/* */
/* For a comprehensive explanation of frames, please refer to the */
/* documentation files. */
/* */
/********************************************************************/
/* Frame type declaration.*/
struct TFileFrame_
{
Byte* address; /* frame buffer */
Byte* cursor; /* current cursor position in frame */
Long size; /* frame size */
};
typedef struct TFileFrame_ TFileFrame;
EXPORT_DEF
const TFileFrame TT_Null_FileFrame;
/* The macro ZERO_Frame is used to define and init a frame. */
/* It is important to have a default frame of { NULL, NULL, 0 } */
/* before a call to TT_Access_Frame(). Otherwise, the call will */
/* fail with a TT_Err_Nested_Frame_Accesses error. */
#define ZERO_Frame( frame ) \
{ \
(frame).address = NULL; \
(frame).cursor = NULL; \
(frame).size = 0; \
}
/* The macros FRAME_ARGS and FRAME_ARG let us build a thread-safe */
/* or re-entrant implementation depending on a single configuration */
/* define */
#ifdef TT_CONFIG_OPTION_THREAD_SAFE
#define FRAME_ARGS TFileFrame* frame,
#define FRAME_ARG TFileFrame* frame
#else
#define FRAME_ARGS /* void */
#define FRAME_ARG void
#endif /* TT_CONFIG_OPTION_THREAD_SAFE */
/* Access the next 'size' bytes from current position. */
/* Fails if all bytes cannot be read/accessed. */
EXPORT_DEF
TT_Error TT_Access_Frame( STREAM_ARGS FRAME_ARGS Long size );
/* Access the bytes located in the next 'size' bytes of the file. */
/* Doesn't fail if less than 'size' bytes are accessible (like */
/* at the end of the file). */
EXPORT_DEF
TT_Error TT_Check_And_Access_Frame( STREAM_ARGS FRAME_ARGS Long size );
/* Forget frame */
EXPORT_DEF
TT_Error TT_Forget_Frame( FRAME_ARG );
/* primitive routines for data accessing */
EXPORT_DEF
Char TT_Get_Char ( FRAME_ARG );
EXPORT_DEF
Short TT_Get_Short( FRAME_ARG );
EXPORT_DEF
Long TT_Get_Long ( FRAME_ARG );
#ifdef TT_CONFIG_OPTION_THREAD_SAFE
#define TT_Get_Byte( frame ) ( (Byte )TT_Get_Char ( frame ) )
#define TT_Get_UShort( frame ) ( (UShort)TT_Get_Short( frame ) )
#define TT_Get_ULong( frame ) ( (ULong )TT_Get_Long ( frame ) )
#else
#define TT_Get_Byte() ( (Byte )TT_Get_Char () )
#define TT_Get_UShort() ( (UShort)TT_Get_Short() )
#define TT_Get_ULong() ( (ULong )TT_Get_Long () )
#endif /* TT_CONFIG_OPTION_THREAD_SAFE */
#ifdef __cplusplus
}
#endif
#endif /* TTFILE_H */
/* END */
TTGLOAD.C 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
/*******************************************************************
*
* ttgload.c 1.0
*
* TrueType Glyph Loader.
*
* Copyright 1996-1999 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.
*
******************************************************************/
#include "tttypes.h"
#include "ttdebug.h"
#include "ttcalc.h"
#include "ttfile.h"
#include "tttables.h"
#include "ttobjs.h"
#include "ttgload.h"
#include "ttmemory.h"
#include "tttags.h"
#include "ttload.h"
/* required by the tracing mode */
#undef TT_COMPONENT
#define TT_COMPONENT trace_gload
/* composite font flags */
#define ARGS_ARE_WORDS 0x001
#define ARGS_ARE_XY_VALUES 0x002
#define ROUND_XY_TO_GRID 0x004
#define WE_HAVE_A_SCALE 0x008
/* reserved 0x010 */
#define MORE_COMPONENTS 0x020
#define WE_HAVE_AN_XY_SCALE 0x040
#define WE_HAVE_A_2X2 0x080
#define WE_HAVE_INSTR 0x100
#define USE_MY_METRICS 0x200
/********************************************************/
/* Return horizontal or vertical metrics in font units */
/* for a given glyph. The metrics are the left side */
/* bearing (resp. top side bearing) and advance width */
/* (resp. advance height). */
/* */
/* This function will much probably move to another */
/* component in the short future, but I haven't decided */
/* which yet... */
LOCAL_FUNC
void TT_Get_Metrics( TT_Horizontal_Header* header,
UShort index,
Short* bearing,
UShort* advance )
{
PLongMetrics longs_m;
UShort k = header->number_Of_HMetrics;
if ( index < k )
{
longs_m = (PLongMetrics)header->long_metrics + index;
*bearing = longs_m->bearing;
*advance = longs_m->advance;
}
else
{
*bearing = ((PShortMetrics)header->short_metrics)[index - k];
*advance = ((PLongMetrics)header->long_metrics)[k - 1].advance;
}
}
/********************************************************/
/* Return horizontal metrics in font units for a given */
/* glyph. If `check' is true, take care of mono-spaced */
/* fonts by returning the advance width max. */
static void Get_HMetrics( PFace face,
UShort index,
Bool check,
Short* lsb,
UShort* aw )
{
TT_Get_Metrics( &face->horizontalHeader, index, lsb, aw );
if ( check && face->postscript.isFixedPitch )
*aw = face->horizontalHeader.advance_Width_Max;
}
/********************************************************/
/* Return advance width table for a given pixel size */
/* if it is found in the font's `hdmx' table (if any). */
static PByte Get_Advance_Widths( PFace face,
UShort ppem )
{
UShort n;
for ( n = 0; n < face->hdmx.num_records; n++ )
if ( face->hdmx.records[n].ppem == ppem )
return face->hdmx.records[n].widths;
return NULL;
}
/********************************************************/
/* Copy current glyph into original one. */
#define cur_to_org( n, zone ) \
MEM_Copy( (zone)->org, (zone)->cur, (n) * sizeof ( TT_Vector ) )
/********************************************************/
/* copy original glyph into current one */
#define org_to_cur( n, zone ) \
MEM_Copy( (zone)->cur, (zone)->org, (n) * sizeof ( TT_Vector ) )
/********************************************************/
/* translate an array of coordinates */
static void translate_array( UShort n,
TT_Vector* coords,
TT_Pos delta_x,
TT_Pos delta_y )
{
UShort k;
if ( delta_x )
for ( k = 0; k < n; k++ )
coords[k].x += delta_x;
if ( delta_y )
for ( k = 0; k < n; k++ )
coords[k].y += delta_y;
}
/********************************************************/
/* mount one zone on top of another */
static void mount_zone( PGlyph_Zone source,
PGlyph_Zone target )
{
UShort np;
Short nc;
np = source->n_points;
nc = source->n_contours;
target->org = source->org + np;
target->cur = source->cur + np;
target->touch = source->touch + np;
target->contours = source->contours + nc;
target->n_points = 0;
target->n_contours = 0;
}
/*******************************************************************
*
* Function: Load_Simple_Glyph
*
******************************************************************/
static TT_Error Load_Simple_Glyph( PExecution_Context exec,
TT_Stream input,
Short n_contours,
Short left_contours,
UShort left_points,
UShort load_flags,
PSubglyph_Record subg )
{
DEFINE_LOAD_LOCALS( input );
PGlyph_Zone pts;
Short k;
UShort j;
UShort n_points, n_ins;
PFace face;
Byte* flag;
TT_Vector* vec;
TT_F26Dot6 x, y;
face = exec->face;
/* simple check */
if ( n_contours > left_contours )
{
PTRACE0(( "ERROR: Glyph index %ld has %d contours > left %d\n",
subg->index, n_contours, left_contours ));
return TT_Err_Too_Many_Contours;
}
/* preparing the execution context */
mount_zone( &subg->zone, &exec->pts );
/* reading the contours endpoints */
if ( ACCESS_Frame( (n_contours + 1) * 2L ) )
return error;
PTRACE4(( " Contour endpoints:" ));
for ( k = 0; k < n_contours; k++ )
{
exec->pts.contours[k] = GET_UShort();
PTRACE4(( " %d", exec->pts.contours[k] ));
}
PTRACE4(( "\n" ));
if ( n_contours > 0 )
n_points = exec->pts.contours[n_contours - 1] + 1;
else
n_points = 0;
n_ins = GET_UShort();
FORGET_Frame();
if ( n_points > left_points )
{
PTRACE0(( "ERROR: Too many points in glyph %ld\n", subg->index ));
return TT_Err_Too_Many_Points;
}
/* loading instructions */
PTRACE4(( " Instructions size: %d\n", n_ins ));
if ( n_ins > face->maxProfile.maxSizeOfInstructions )
{
PTRACE0(( "ERROR: Too many instructions!\n" ));
return TT_Err_Too_Many_Ins;
}
if ( FILE_Read( exec->glyphIns, n_ins ) )
return error;
if ( (error = Set_CodeRange( exec,
TT_CodeRange_Glyph,
exec->glyphIns,
n_ins )) != TT_Err_Ok )
return error;
/* read the flags */
if ( CHECK_ACCESS_Frame( n_points * 5L ) )
return error;
j = 0;
flag = exec->pts.touch;
while ( j < n_points )
{
Byte c, cnt;
flag[j] = c = GET_Byte();
j++;
if ( c & 8 )
{
cnt = GET_Byte();
while( cnt > 0 )
{
flag[j++] = c;
cnt--;
}
}
}
/* read the X */
x = 0;
vec = exec->pts.org;
for ( j = 0; j < n_points; j++ )
{
if ( flag[j] & 2 )
{
if ( flag[j] & 16 )
x += GET_Byte();
else
x -= GET_Byte();
}
else
{
if ( (flag[j] & 16) == 0 )
x += GET_Short();
}
vec[j].x = x;
}
/* read the Y */
y = 0;
for ( j = 0; j < n_points; j++ )
{
if ( flag[j] & 4 )
{
if ( flag[j] & 32 )
y += GET_Byte();
else
y -= GET_Byte();
}
else
{
if ( (flag[j] & 32) == 0 )
y += GET_Short();
}
vec[j].y = y;
}
FORGET_Frame();
/* Now add the two shadow points at n and n + 1. */
/* We need the left side bearing and advance width. */
/* pp1 = xMin - lsb */
vec[n_points].x = subg->metrics.bbox.xMin - subg->metrics.horiBearingX;
vec[n_points].y = 0;
/* pp2 = pp1 + aw */
vec[n_points+1].x = vec[n_points].x + subg->metrics.horiAdvance;
vec[n_points+1].y = 0;
/* clear the touch flags */
for ( j = 0; j < n_points; j++ )
exec->pts.touch[j] &= TT_Flag_On_Curve;
exec->pts.touch[n_points ] = 0;
exec->pts.touch[n_points + 1] = 0;
/* Note that we return two more points that are not */
/* part of the glyph outline. */
n_points += 2;
/* now eventually scale and hint the glyph */
pts = &exec->pts;
pts->n_points = n_points;
pts->n_contours = n_contours;
if ( (load_flags & TTLOAD_SCALE_GLYPH) == 0 )
{
/* no scaling, just copy the orig arrays into the cur ones */
org_to_cur( n_points, pts );
}
else
{
/* first scale the glyph points */
for ( j = 0; j < n_points; j++ )
{
pts->org[j].x = Scale_X( &exec->metrics, pts->org[j].x );
pts->org[j].y = Scale_Y( &exec->metrics, pts->org[j].y );
}
/* if hinting, round pp1, and shift the glyph accordingly */
if ( subg->is_hinted )
{
x = pts->org[n_points - 2].x;
x = ((x+32) & -64) - x;
translate_array( n_points, pts->org, x, 0 );
org_to_cur( n_points, pts );
pts->cur[n_points - 1].x = (pts->cur[n_points - 1].x + 32) & -64;
/* now consider hinting */
if ( n_ins > 0 )
{
exec->is_composite = FALSE;
exec->pedantic_hinting = load_flags & TTLOAD_PEDANTIC;
error = Context_Run( exec, FALSE );
if (error && exec->pedantic_hinting)
return error;
}
}
else
org_to_cur( n_points, pts );
}
/* save glyph phantom points */
if (!subg->preserve_pps)
{
subg->pp1 = pts->cur[n_points - 2];
subg->pp2 = pts->cur[n_points - 1];
}
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : Load_Composite_End
*
******************************************************************/
static
TT_Error Load_Composite_End( UShort n_points,
Short n_contours,
PExecution_Context exec,
PSubglyph_Record subg,
UShort load_flags,
TT_Stream input )
{
DEFINE_LOAD_LOCALS( input );
UShort k, n_ins;
PGlyph_Zone pts;
if ( subg->is_hinted &&
subg->element_flag & WE_HAVE_INSTR )
{
if ( ACCESS_Frame( 2L ) )
return error;
n_ins = GET_UShort(); /* read size of instructions */
FORGET_Frame();
PTRACE4(( " Instructions size: %d\n", n_ins ));
if ( n_ins > exec->face->maxProfile.maxSizeOfInstructions )
{
PTRACE0(( "ERROR: Too many instructions in composite glyph %ld\n",
subg->index ));
return TT_Err_Too_Many_Ins;
}
if ( FILE_Read( exec->glyphIns, n_ins ) )
return error;
error = Set_CodeRange( exec,
TT_CodeRange_Glyph,
exec->glyphIns,
n_ins );
if ( error )
return error;
}
else
n_ins = 0;
/* prepare the execution context */
n_points += 2;
exec->pts = subg->zone;
pts = &exec->pts;
pts->n_points = n_points;
pts->n_contours = n_contours;
/* add phantom points */
pts->cur[n_points - 2] = subg->pp1;
pts->cur[n_points - 1] = subg->pp2;
pts->touch[n_points - 1] = 0;
pts->touch[n_points - 2] = 0;
/* if hinting, round the phantom points */
if ( subg->is_hinted )
{
pts->cur[n_points - 2].x = (subg->pp1.x + 32) & -64;
pts->cur[n_points - 1].x = (subg->pp2.x + 32) & -64;
}
for ( k = 0; k < n_points; k++ )
pts->touch[k] &= TT_Flag_On_Curve;
cur_to_org( n_points, pts );
/* now consider hinting */
if ( subg->is_hinted && n_ins > 0 )
{
exec->is_composite = TRUE;
exec->pedantic_hinting = load_flags & TTLOAD_PEDANTIC;
error = Context_Run( exec, FALSE );
if (error && exec->pedantic_hinting)
return error;
}
/* save glyph origin and advance points */
subg->pp1 = pts->cur[n_points - 2];
subg->pp2 = pts->cur[n_points - 1];
return TT_Err_Ok;
}
/*******************************************************************
*
* Function : Init_Glyph_Component
*
******************************************************************/
static
void Init_Glyph_Component( PSubglyph_Record element,
PSubglyph_Record original,
PExecution_Context exec )
{
element->index = -1;
element->is_scaled = FALSE;
element->is_hinted = FALSE;
if ( original )
mount_zone( &original->zone, &element->zone );
else
element->zone = exec->pts;
element->zone.n_contours = 0;
element->zone.n_points = 0;
element->arg1 = 0;
element->arg2 = 0;
element->element_flag = 0;
element->preserve_pps = FALSE;
element->transform.xx = 1L << 16;
element->transform.xy = 0;
element->transform.yx = 0;
element->transform.yy = 1L << 16;
element->transform.ox = 0;
element->transform.oy = 0;
element->metrics.horiBearingX = 0;
element->metrics.horiAdvance = 0;
}
LOCAL_FUNC
TT_Error Load_TrueType_Glyph( PInstance instance,
PGlyph glyph,
UShort glyph_index,
UShort load_flags )
{
enum TPhases_
{
Load_Exit,
Load_Glyph,
Load_Header,
Load_Simple,
Load_Composite,
Load_End
};
typedef enum TPhases_ TPhases;
DEFINE_ALL_LOCALS;
PFace face;
UShort num_points;
Short num_contours;
UShort left_points;
Short left_contours;
UShort num_elem_points;
Long table;
UShort load_top;
Long k, l;
UShort new_flags;
Long index;
UShort u, v;
Long glyph_offset, offset;
TT_F26Dot6 x, y, nx, ny;
Fixed xx, xy, yx, yy;
PExecution_Context exec;
PSubglyph_Record subglyph, subglyph2;
TGlyph_Zone base_pts;
TPhases phase;
PByte widths;
/* TT_Glyph_Loader_Callback cacheCb; */
/* TT_Outline cached_outline; */
/* first of all, check arguments */
if ( !glyph )
return TT_Err_Invalid_Glyph_Handle;
face = glyph->face;
if ( !face )
return TT_Err_Invalid_Glyph_Handle;
if ( glyph_index >= face->numGlyphs )
return TT_Err_Invalid_Glyph_Index;
if ( instance && (load_flags & TTLOAD_SCALE_GLYPH) == 0 )
{
instance = 0;
load_flags &= ~( TTLOAD_SCALE_GLYPH | TTLOAD_HINT_GLYPH );
}
table = TT_LookUp_Table( face, TTAG_glyf );
if ( table < 0 )
{
PTRACE0(( "ERROR: There is no glyph table in this font file!\n" ));
return TT_Err_Glyf_Table_Missing;
}
glyph_offset = face->dirTables[table].Offset;
/* query new execution context */
if ( instance && instance->debug )
exec = instance->context;
else
exec = New_Context( face );
if ( !exec )
return TT_Err_Could_Not_Find_Context;
Context_Load( exec, face, instance );
if ( instance )
{
if ( instance->GS.instruct_control & 2 )
exec->GS = Default_GraphicsState;
else
exec->GS = instance->GS;
/* load default graphics state */
glyph->outline.high_precision = ( instance->metrics.y_ppem < 24 );
}
/* save its critical pointers, as they'll be modified during load */
base_pts = exec->pts;
/* init variables */
left_points = face->maxPoints;
left_contours = face->maxContours;
num_points = 0;
num_contours = 0;
load_top = 0;
subglyph = exec->loadStack;
Init_Glyph_Component( subglyph, NULL, exec );
subglyph->index = glyph_index;
subglyph->is_hinted = load_flags & TTLOAD_HINT_GLYPH;
/* when the cvt program has disabled hinting, the argument */
/* is ignored. */
if ( instance && instance->GS.instruct_control & 1 )
subglyph->is_hinted = FALSE;
/* now access stream */
if ( USE_Stream( face->stream, stream ) )
goto Fin;
/* Main loading loop */
phase = Load_Glyph;
index = 0;
while ( phase != Load_Exit )
{
subglyph = exec->loadStack + load_top;
switch ( phase )
{
/************************************************************/
/* */
/* Load_Glyph state */
/* */
/* reading a glyph's generic header to determine */
/* whether it's simple or composite */
/* */
/* exit states: Load_Header and Load_End */
case Load_Glyph:
/* check glyph index and table */
index = subglyph->index;
if ( index < 0 || index >= face->numGlyphs )
{
error = TT_Err_Invalid_Glyph_Index;
goto Fail;
}
/* get horizontal metrics */
{
Short left_bearing;
UShort advance_width;
Get_HMetrics( face, (UShort)index,
!(load_flags & TTLOAD_IGNORE_GLOBAL_ADVANCE_WIDTH),
&left_bearing,
&advance_width );
subglyph->metrics.horiBearingX = left_bearing;
subglyph->metrics.horiAdvance = advance_width;
}
phase = Load_Header;
/* The cache callback isn't part of the FreeType release yet */
/* It is discarded for the moment.. */
/* */
#if 0
if ( instance )
{
/* is the glyph in an outline cache ? */
cacheCb = instance->owner->engine->glCallback;
if ( cacheCb && 0 ) /* disabled */
{
/* we have a callback */
error = cacheCb( instance->generic,
index, &cached_outline, &x, &y );
if ( !error )
{
/* no error, then append the outline to the current subglyph */
/* error = Append_Outline( subglyph,
&left_points,
&left_contours,
&cached_outline ); */
phase = Load_End;
}
}
}
#endif
break;
/************************************************************/
/* */
/* Load_Header state */
/* */
/* reading a glyph's generic header to determine */
/* wether it's simple or composite */
/* */
/* exit states: Load_Simple and Load_Composite */
/* */
case Load_Header: /* load glyph */
if ( index + 1 < face->numLocations &&
face->glyphLocations[index] == face->glyphLocations[index + 1] )
{
/* as described by Frederic Loyer, these are spaces, and */
/* not the unknown glyph. */
num_contours = 0;
num_points = 0;
subglyph->metrics.bbox.xMin = 0;
subglyph->metrics.bbox.xMax = 0;
subglyph->metrics.bbox.yMin = 0;
subglyph->metrics.bbox.yMax = 0;
subglyph->pp1.x = 0;
subglyph->pp2.x = subglyph->metrics.horiAdvance;
if (load_flags & TTLOAD_SCALE_GLYPH)
subglyph->pp2.x = Scale_X( &exec->metrics, subglyph->pp2.x );
exec->glyphSize = 0;
phase = Load_End;
break;
}
offset = glyph_offset + face->glyphLocations[index];
/* read first glyph header */
if ( FILE_Seek( offset ) ||
ACCESS_Frame( 10L ) )
goto Fail_File;
num_contours = GET_Short();
subglyph->metrics.bbox.xMin = GET_Short();
subglyph->metrics.bbox.yMin = GET_Short();
subglyph->metrics.bbox.xMax = GET_Short();
subglyph->metrics.bbox.yMax = GET_Short();
FORGET_Frame();
PTRACE6(( "Glyph %ld:\n", index ));
PTRACE6(( " # of contours: %d\n", num_contours ));
PTRACE6(( " xMin: %4d xMax: %4d\n",
subglyph->metrics.bbox.xMin,
subglyph->metrics.bbox.xMax ));
PTRACE6(( " yMin: %4d yMax: %4d\n",
subglyph->metrics.bbox.yMin,
subglyph->metrics.bbox.yMax ));
if ( num_contours > left_contours )
{
PTRACE0(( "ERROR: Too many contours for glyph %ld\n", index ));
error = TT_Err_Too_Many_Contours;
goto Fail;
}
subglyph->pp1.x = subglyph->metrics.bbox.xMin -
subglyph->metrics.horiBearingX;
subglyph->pp1.y = 0;
subglyph->pp2.x = subglyph->pp1.x + subglyph->metrics.horiAdvance;
if (load_flags & TTLOAD_SCALE_GLYPH)
{
subglyph->pp1.x = Scale_X( &exec->metrics, subglyph->pp1.x );
subglyph->pp2.x = Scale_X( &exec->metrics, subglyph->pp2.x );
}
/* is it a simple glyph ? */
if ( num_contours > 0 )
phase = Load_Simple;
else
phase = Load_Composite;
break;
/************************************************************/
/* */
/* Load_Simple state */
/* */
/* reading a simple glyph (num_contours must be set to */
/* the glyph's number of contours.) */
/* */
/* exit states : Load_End */
/* */
case Load_Simple:
new_flags = load_flags;
/* disable hinting when scaling */
if ( !subglyph->is_hinted )
new_flags &= ~TTLOAD_HINT_GLYPH;
error = Load_Simple_Glyph( exec,
stream,
num_contours,
left_contours,
left_points,
new_flags,
subglyph );
if ( error )
goto Fail;
/* Note: We could have put the simple loader source there */
/* but the code is fat enough already :-) */
num_points = exec->pts.n_points - 2;
phase = Load_End;
break;
/************************************************************/
/* */
/* Load_Composite state */
/* */
/* reading a composite glyph header a pushing a new */
/* load element on the stack. */
/* */
/* exit states: Load_Glyph */
/* */
case Load_Composite:
/* create a new element on the stack */
load_top++;
if ( load_top > face->maxComponents )
{
error = TT_Err_Invalid_Composite;
goto Fail;
}
subglyph2 = exec->loadStack + load_top;
Init_Glyph_Component( subglyph2, subglyph, NULL );
subglyph2->is_hinted = subglyph->is_hinted;
/* now read composite header */
if ( ACCESS_Frame( 4L ) )
goto Fail_File;
subglyph->element_flag = new_flags = GET_UShort();
subglyph2->index = GET_UShort();
FORGET_Frame();
k = 1 + 1;
if ( new_flags & ARGS_ARE_WORDS )
k *= 2;
if ( new_flags & WE_HAVE_A_SCALE )
k += 2;
else if ( new_flags & WE_HAVE_AN_XY_SCALE )
k += 4;
else if ( new_flags & WE_HAVE_A_2X2 )
k += 8;
if ( ACCESS_Frame( k ) )
goto Fail_File;
if ( new_flags & ARGS_ARE_WORDS )
{
k = GET_Short();
l = GET_Short();
}
else
{
k = GET_Char();
l = GET_Char();
}
subglyph->arg1 = k;
subglyph->arg2 = l;
if ( new_flags & ARGS_ARE_XY_VALUES )
{
subglyph->transform.ox = k;
subglyph->transform.oy = l;
}
xx = 1L << 16;
xy = 0;
yx = 0;
yy = 1L << 16;
if ( new_flags & WE_HAVE_A_SCALE )
{
xx = (Fixed)GET_Short() << 2;
yy = xx;
subglyph2->is_scaled = TRUE;
}
else if ( new_flags & WE_HAVE_AN_XY_SCALE )
{
xx = (Fixed)GET_Short() << 2;
yy = (Fixed)GET_Short() << 2;
subglyph2->is_scaled = TRUE;
}
else if ( new_flags & WE_HAVE_A_2X2 )
{
xx = (Fixed)GET_Short() << 2;
xy = (Fixed)GET_Short() << 2;
yx = (Fixed)GET_Short() << 2;
yy = (Fixed)GET_Short() << 2;
subglyph2->is_scaled = TRUE;
}
FORGET_Frame();
subglyph->transform.xx = xx;
subglyph->transform.xy = xy;
subglyph->transform.yx = yx;
subglyph->transform.yy = yy;
k = TT_MulFix( xx, yy ) - TT_MulFix( xy, yx );
/* disable hinting in case of scaling/slanting */
if ( ABS( k ) != (1L << 16) )
subglyph2->is_hinted = FALSE;
subglyph->file_offset = FILE_Pos();
phase = Load_Glyph;
break;
/************************************************************/
/* */
/* Load_End state */
/* */
/* after loading a glyph, apply transformation and offset */
/* where necessary, pops element and continue or */
/* stop process. */
/* */
/* exit states : Load_Composite and Load_Exit */
/* */
case Load_End:
if ( load_top > 0 )
{
subglyph2 = subglyph;
load_top--;
subglyph = exec->loadStack + load_top;
/* check advance width and left side bearing */
if ( !subglyph->preserve_pps &&
subglyph->element_flag & USE_MY_METRICS )
{
subglyph->metrics.horiBearingX = subglyph2->metrics.horiBearingX;
subglyph->metrics.horiAdvance = subglyph2->metrics.horiAdvance;
subglyph->pp1 = subglyph2->pp1;
subglyph->pp2 = subglyph2->pp2;
subglyph->preserve_pps = TRUE;
}
/* apply scale */
if ( subglyph2->is_scaled )
{
TT_Vector* cur = subglyph2->zone.cur;
TT_Vector* org = subglyph2->zone.org;
for ( u = 0; u < num_points; u++ )
{
nx = TT_MulFix( cur->x, subglyph->transform.xx ) +
TT_MulFix( cur->y, subglyph->transform.yx );
ny = TT_MulFix( cur->x, subglyph->transform.xy ) +
TT_MulFix( cur->y, subglyph->transform.yy );
cur->x = nx;
cur->y = ny;
nx = TT_MulFix( org->x, subglyph->transform.xx ) +
TT_MulFix( org->y, subglyph->transform.yx );
ny = TT_MulFix( org->x, subglyph->transform.xy ) +
TT_MulFix( org->y, subglyph->transform.yy );
org->x = nx;
org->y = ny;
cur++;
org++;
}
}
/* adjust counts */
num_elem_points = subglyph->zone.n_points;
for ( k = 0; k < num_contours; k++ )
subglyph2->zone.contours[k] += num_elem_points;
subglyph->zone.n_points += num_points;
subglyph->zone.n_contours += num_contours;
left_points -= num_points;
left_contours -= num_contours;
if ( !(subglyph->element_flag & ARGS_ARE_XY_VALUES) )
{
/* move second glyph according to control points */
/* the attach points are relative to the specific component */
u = (UShort)subglyph->arg1;
v = (UShort)subglyph->arg2;
if ( u >= num_elem_points ||
v >= num_points )
{
error = TT_Err_Invalid_Composite;
goto Fail;
}
/* adjust count */
v += num_elem_points;
x = subglyph->zone.cur[u].x - subglyph->zone.cur[v].x;
y = subglyph->zone.cur[u].y - subglyph->zone.cur[v].y;
}
else
{
/* apply offset */
x = subglyph->transform.ox;
y = subglyph->transform.oy;
if ( load_flags & TTLOAD_SCALE_GLYPH )
{
x = Scale_X( &exec->metrics, x );
y = Scale_Y( &exec->metrics, y );
if ( subglyph->element_flag & ROUND_XY_TO_GRID )
{
x = (x+32) & -64;
y = (y+32) & -64;
}
}
}
translate_array( num_points, subglyph2->zone.cur, x, y );
cur_to_org( num_points, &subglyph2->zone );
num_points = subglyph->zone.n_points;
num_contours = subglyph->zone.n_contours;
/* check for last component */
if ( FILE_Seek( subglyph->file_offset ) )
goto Fail_File;
if ( subglyph->element_flag & MORE_COMPONENTS )
phase = Load_Composite;
else
{
error = Load_Composite_End( num_points,
num_contours,
exec,
subglyph,
load_flags,
stream );
if ( error )
goto Fail;
phase = Load_End;
}
}
else
phase = Load_Exit;
break;
case Load_Exit:
break;
}
}
/* finally, copy the points arrays to the glyph object */
exec->pts = base_pts;
for ( u = 0; u < num_points + 2; u++ )
{
glyph->outline.points[u] = exec->pts.cur[u];
glyph->outline.flags [u] = exec->pts.touch[u];
}
for ( k = 0; k < num_contours; k++ )
glyph->outline.contours[k] = exec->pts.contours[k];
glyph->outline.n_points = num_points;
glyph->outline.n_contours = num_contours;
glyph->outline.second_pass = TRUE;
/* translate array so that (0,0) is the glyph's origin */
translate_array( num_points + 2,
glyph->outline.points,
-subglyph->pp1.x,
0 );
TT_Get_Outline_BBox( &glyph->outline, &glyph->metrics.bbox );
if ( subglyph->is_hinted )
{
/* grid-fit the bounding box */
glyph->metrics.bbox.xMin &= -64;
glyph->metrics.bbox.yMin &= -64;
glyph->metrics.bbox.xMax = (glyph->metrics.bbox.xMax+63) & -64;
glyph->metrics.bbox.yMax = (glyph->metrics.bbox.yMax+63) & -64;
}
/* get the device-independent scaled horizontal metrics */
/* take care of fixed-pitch fonts... */
{
TT_Pos left_bearing;
TT_Pos advance;
left_bearing = subglyph->metrics.horiBearingX;
advance = subglyph->metrics.horiAdvance;
if ( face->postscript.isFixedPitch )
advance = face->horizontalHeader.advance_Width_Max;
if ( load_flags & TTLOAD_SCALE_GLYPH )
{
left_bearing = Scale_X( &exec->metrics, left_bearing );
advance = Scale_X( &exec->metrics, advance );
}
glyph->metrics.linearHoriBearingX = left_bearing;
glyph->metrics.linearHoriAdvance = advance;
}
glyph->metrics.horiBearingX = glyph->metrics.bbox.xMin;
glyph->metrics.horiBearingY = glyph->metrics.bbox.yMax;
glyph->metrics.horiAdvance = subglyph->pp2.x - subglyph->pp1.x;
/* Now take care of vertical metrics. In the case where there is */
/* no vertical information within the font (relatively common), make */
/* up some metrics `by hand' ... */
{
Short top_bearing; /* vertical top side bearing (EM units) */
UShort advance_height; /* vertical advance height (EM units) */
TT_Pos left; /* scaled vertical left side bearing */
TT_Pos Top; /* scaled original vertical top side bearing */
TT_Pos top; /* scaled vertical top side bearing */
TT_Pos advance; /* scaled vertical advance height */
/* Get the unscaled `tsb' and `ah' values */
if ( face->verticalInfo &&
face->verticalHeader.number_Of_VMetrics > 0 )
{
/* Don't assume that both the vertical header and vertical */
/* metrics are present in the same font :-) */
TT_Get_Metrics( (TT_Horizontal_Header*)&face->verticalHeader,
glyph_index,
&top_bearing,
&advance_height );
}
else
{
/* Make up the distances from the horizontal header.. */
/* NOTE: The OS/2 values are the only `portable' ones, */
/* which is why we use them... */
/* */
/* NOTE2: The sTypoDescender is negative, which is why */
/* we compute the baseline-to-baseline distance */
/* here with : */
/* ascender - descender + linegap */
/* */
top_bearing = (Short) (face->os2.sTypoLineGap / 2);
advance_height = (UShort)(face->os2.sTypoAscender -
face->os2.sTypoDescender +
face->os2.sTypoLineGap);
}
/* We must adjust the top_bearing value from the bounding box given
in the glyph header to te bounding box calculated with
TT_Get_Outline_BBox() */
/* scale the metrics */
if ( load_flags & TTLOAD_SCALE_GLYPH )
{
Top = Scale_Y( &exec->metrics, top_bearing );
top = Scale_Y( &exec->metrics,
top_bearing + subglyph->metrics.bbox.yMax ) -
glyph->metrics.bbox.yMax;
advance = Scale_Y( &exec->metrics, advance_height );
}
else
{
Top = top_bearing;
top = top_bearing + subglyph->metrics.bbox.yMax -
glyph->metrics.bbox.yMax;
advance = advance_height;
}
glyph->metrics.linearVertBearingY = Top;
glyph->metrics.linearVertAdvance = advance;
/* XXX : for now, we have no better algo for the lsb, but it should */
/* work ok.. */
/* */
left = ( glyph->metrics.bbox.xMin - glyph->metrics.bbox.xMax ) / 2;
/* grid-fit them if necessary */
if ( subglyph->is_hinted )
{
left &= -64;
top = (top + 63) & -64;
advance = (advance + 32) & -64;
}
glyph->metrics.vertBearingX = left;
glyph->metrics.vertBearingY = top;
glyph->metrics.vertAdvance = advance;
}
/* Adjust advance width to the value contained in the hdmx table. */
if ( !exec->face->postscript.isFixedPitch && instance &&
subglyph->is_hinted )
{
widths = Get_Advance_Widths( exec->face,
exec->instance->metrics.x_ppem );
if ( widths )
glyph->metrics.horiAdvance = widths[glyph_index] << 6;
}
glyph->outline.dropout_mode = (Char)exec->GS.scan_type;
error = TT_Err_Ok;
Fail_File:
Fail:
DONE_Stream( stream );
Fin:
/* reset the execution context */
exec->pts = base_pts;
if ( !instance || !instance->debug )
Done_Context( exec );
return error;
}
/* END */
TTGLOAD.H 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
/*******************************************************************
*
* ttgload.h 1.0
*
* TrueType Glyph Loader.
*
* Copyright 1996-1999 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.
*
******************************************************************/
#ifndef TTGLOAD_H
#define TTGLOAD_H
#include "ttconfig.h"
#include "tttypes.h"
#include "ttobjs.h"
#ifdef __cplusplus
extern "C" {
#endif
LOCAL_DEF
void TT_Get_Metrics( TT_Horizontal_Header* header,
UShort index,
Short* bearing,
UShort* advance );
LOCAL_DEF
TT_Error Load_TrueType_Glyph( PInstance instance,
PGlyph glyph,
UShort glyph_index,
UShort load_flags );
#ifdef __cplusplus
}
#endif
#endif /* TTGLOAD_H */
/* END */
TTINTERP.C 、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
/*******************************************************************
*
* ttinterp.c 3.1
*
* TrueType bytecode intepreter.
*
* Copyright 1996-1999 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.
*
*
* Changes between 3.1 and 3.0:
*
* - A more relaxed version of the interpreter. It is now able to
* ignore errors like out-of-bound array access and writes in order
* to silently support broken glyphs (even if the results are not
* always pretty).
*
* Note that one can use the flag TTLOAD_PEDANTIC to force
* TrueType-compliant interpretation.
*
* - A big #if used to completely disable the interpreter, which
* is due to the Apple patents issues which emerged recently.
*
******************************************************************/
#include "freetype.h"
#include "tttypes.h"
#include "ttdebug.h"
#include "ttcalc.h"
#include "ttmemory.h"
#include "ttinterp.h"
#ifdef TT_CONFIG_OPTION_NO_INTERPRETER
LOCAL_FUNC
TT_Error RunIns( PExecution_Context exc )
{
/* do nothing - always successful */
(void)exc;
return TT_Err_Ok;
}
#else
#ifdef DEBUG_INTERPRETER
//#include <memory.h>
#include "ttdebug.h"
/* Define the `getch()' function. On Unix systems, it is an alias */
/* for `getchar()', and the debugger front end must ensure that the */
/* `stdin' file descriptor is not in line-by-line input mode. */
#ifdef OS2
//#include <conio.h>
#else
#define getch getchar
#endif
#endif /* DEBUG_INTEPRETER */
/* required by the tracing mode */
#undef TT_COMPONENT
#define TT_COMPONENT trace_interp
/* In order to detect infinite loops in the code, we set-up */
/* a counter within the run loop. a singly stroke of interpretation */
/* is now limited to a maximum number of opcodes defined below.. */
/* */
#define MAX_RUNNABLE_OPCODES 1000000
/* There are two kinds of implementations there: */
/* */
/* a. static implementation: */
/* */
/* The current execution context is a static variable, */
/* which fields are accessed directly by the interpreter */
/* during execution. The context is named 'cur'. */
/* */
/* This version is non-reentrant, of course. */
/* */
/* */
/* b. indirect implementation: */
/* */
/* The current execution context is passed to _each_ */
/* function as its first argument, and each field is */
/* thus accessed indirectly. */
/* */
/* This version is, however, fully re-entrant. */
/* */
/* */
/* The idea is that an indirect implementation may be */
/* slower to execute on the low-end processors that are */
/* used in some systems (like 386s or even 486s). */
/* */
/* When the interpreter started, we had no idea of the */
/* time that glyph hinting (i.e. executing instructions) */
/* could take in the whole process of rendering a glyph, */
/* and a 10 to 30% performance penalty on low-end systems */
/* didn't seem much of a good idea. This question led us */
/* to provide two distinct builds of the C version from */
/* a single source, with the use of macros (again). */
/* */
/* Now that the engine is working (and working really */
/* well!), it seems that the greatest time-consuming */
/* factors are: file i/o, glyph loading, rasterizing and */
/* _then_ glyph hinting! */
/* */
/* Tests performed with two versions of the 'fttimer' */
/* program seem to indicate that hinting takes less than 5% */
/* of the rendering process, which is dominated by glyph */
/* loading and scan-line conversion by an high order of */
/* magnitude. */
/* */
/* As a consequence, the indirect implementation is now the */
/* default, as its performance costs can be considered */
/* negligible in our context. Note, however, that we */
/* kept the same source with macros because: */
/* */
/* - the code is kept very close in design to the */
/* Pascal one used for development. */
/* */
/* - it's much more readable that way! */
/* */
/* - it's still open to later experimentation and tuning */
#ifndef TT_CONFIG_OPTION_STATIC_INTERPRETER /* indirect implementation */
#define CUR (*exc) /* see ttobjs.h */
#else /* static implementation */
#define CUR cur
static TExecution_Context cur; /* static exec. context variable */
/* apparently, we have a _lot_ of direct indexing when accessing */
/* the static 'cur', which makes the code bigger (due to all the */
/* four bytes addresses). */
#endif /* !TT_CONFIG_OPTION_STATIC_INTERPRETER */
#define INS_ARG EXEC_OPS PStorage args /* see ttobjs.h */
#define SKIP_Code() SkipCode( EXEC_ARG )
#define GET_ShortIns() GetShortIns( EXEC_ARG )
#define COMPUTE_Funcs() Compute_Funcs( EXEC_ARG )
#define NORMalize( x, y, v ) Normalize( EXEC_ARGS x, y, v )
#define SET_SuperRound( scale, flags ) \
SetSuperRound( EXEC_ARGS scale, flags )
#define INS_Goto_CodeRange( range, ip ) \
Ins_Goto_CodeRange( EXEC_ARGS range, ip )
#define CUR_Func_project( x, y ) CUR.func_project( EXEC_ARGS x, y )
#define CUR_Func_move( z, p, d ) CUR.func_move( EXEC_ARGS z, p, d )
#define CUR_Func_dualproj( x, y ) CUR.func_dualproj( EXEC_ARGS x, y )
#define CUR_Func_freeProj( x, y ) CUR.func_freeProj( EXEC_ARGS x, y )
#define CUR_Func_round( d, c ) CUR.func_round( EXEC_ARGS d, c )
#define CUR_Func_read_cvt( index ) \
CUR.func_read_cvt( EXEC_ARGS index )
#define CUR_Func_write_cvt( index, val ) \
CUR.func_write_cvt( EXEC_ARGS index, val )
#define CUR_Func_move_cvt( index, val ) \
CUR.func_move_cvt( EXEC_ARGS index, val )
#define CURRENT_Ratio() Current_Ratio( EXEC_ARG )
#define CURRENT_Ppem() Current_Ppem( EXEC_ARG )
#define CALC_Length() Calc_Length( EXEC_ARG )
#define INS_SxVTL( a, b, c, d ) Ins_SxVTL( EXEC_ARGS a, b, c, d )
#define COMPUTE_Point_Displacement( a, b, c, d ) \
Compute_Point_Displacement( EXEC_ARGS a, b, c, d )
#define MOVE_Zp2_Point( a, b, c, t ) Move_Zp2_Point( EXEC_ARGS a, b, c, t )
#define CUR_Ppem() Cur_PPEM( EXEC_ARG )
/* Instruction dispatch function, as used by the interpreter */
typedef void (*TInstruction_Function)( INS_ARG );
#define BOUNDS( x, n ) ( (x) >= (n) )
/*********************************************************************/
/* */
/* 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. */
/* */
/* Note that for opcodes with a varying number of parameters, */
/* either 0 or 1 arg is verified before execution, depending */
/* on the nature of the instruction: */
/* */
/* - if the number of arguments is given by the bytecode */
/* stream or the loop variable, 0 is chosen. */
/* */
/* - if the first argument is a count n that is followed */
/* by arguments a1..an, then 1 is chosen. */
/* */
/*********************************************************************/
#undef PACK
#define PACK( x, y ) ((x << 4) | y)
static const Byte Pop_Push_Count[256] =
{
/* opcodes are gathered in groups of 16 */
/* please keep the spaces as they are */
/* SVTCA y */ PACK( 0, 0 ),
/* SVTCA x */ PACK( 0, 0 ),
/* SPvTCA y */ PACK( 0, 0 ),
/* SPvTCA x */ PACK( 0, 0 ),
/* SFvTCA y */ PACK( 0, 0 ),
/* SFvTCA x */ PACK( 0, 0 ),
/* SPvTL // */ PACK( 2, 0 ),
/* SPvTL + */ PACK( 2, 0 ),
/* SFvTL // */ PACK( 2, 0 ),
/* SFvTL + */ PACK( 2, 0 ),
/* SPvFS */ PACK( 2, 0 ),
/* SFvFS */ PACK( 2, 0 ),
/* GPV */ PACK( 0, 2 ),
/* GFV */ PACK( 0, 2 ),
/* SFvTPv */ PACK( 0, 0 ),
/* ISECT */ PACK( 5, 0 ),
/* SRP0 */ PACK( 1, 0 ),
/* SRP1 */ PACK( 1, 0 ),
/* SRP2 */ PACK( 1, 0 ),
/* SZP0 */ PACK( 1, 0 ),
/* SZP1 */ PACK( 1, 0 ),
/* SZP2 */ PACK( 1, 0 ),
/* SZPS */ PACK( 1, 0 ),
/* SLOOP */ PACK( 1, 0 ),
/* RTG */ PACK( 0, 0 ),
/* RTHG */ PACK( 0, 0 ),
/* SMD */ PACK( 1, 0 ),
/* ELSE */ PACK( 0, 0 ),
/* JMPR */ PACK( 1, 0 ),
/* SCvTCi */ PACK( 1, 0 ),
/* SSwCi */ PACK( 1, 0 ),
/* SSW */ PACK( 1, 0 ),
/* DUP */ PACK( 1, 2 ),
/* POP */ PACK( 1, 0 ),
/* CLEAR */ PACK( 0, 0 ),
/* SWAP */ PACK( 2, 2 ),
/* DEPTH */ PACK( 0, 1 ),
/* CINDEX */ PACK( 1, 1 ),
/* MINDEX */ PACK( 1, 0 ),
/* AlignPTS */ PACK( 2, 0 ),
/* INS_$28 */ PACK( 0, 0 ),
/* UTP */ PACK( 1, 0 ),
/* LOOPCALL */ PACK( 2, 0 ),
/* CALL */ PACK( 1, 0 ),
/* FDEF */ PACK( 1, 0 ),
/* ENDF */ PACK( 0, 0 ),
/* MDAP[0] */ PACK( 1, 0 ),
/* MDAP[1] */ PACK( 1, 0 ),
/* IUP[0] */ PACK( 0, 0 ),
/* IUP[1] */ PACK( 0, 0 ),
/* SHP[0] */ PACK( 0, 0 ),
/* SHP[1] */ PACK( 0, 0 ),
/* SHC[0] */ PACK( 1, 0 ),
/* SHC[1] */ PACK( 1, 0 ),
/* SHZ[0] */ PACK( 1, 0 ),
/* SHZ[1] */ PACK( 1, 0 ),
/* SHPIX */ PACK( 1, 0 ),
/* IP */ PACK( 0, 0 ),
/* MSIRP[0] */ PACK( 2, 0 ),
/* MSIRP[1] */ PACK( 2, 0 ),
/* AlignRP */ PACK( 0, 0 ),
/* RTDG */ PACK( 0, 0 ),
/* MIAP[0] */ PACK( 2, 0 ),
/* MIAP[1] */ PACK( 2, 0 ),
/* NPushB */ PACK( 0, 0 ),
/* NPushW */ PACK( 0, 0 ),
/* WS */ PACK( 2, 0 ),
/* RS */ PACK( 1, 1 ),
/* WCvtP */ PACK( 2, 0 ),
/* RCvt */ PACK( 1, 1 ),
/* GC[0] */ PACK( 1, 1 ),
/* GC[1] */ PACK( 1, 1 ),
/* SCFS */ PACK( 2, 0 ),
/* MD[0] */ PACK( 2, 1 ),
/* MD[1] */ PACK( 2, 1 ),
/* MPPEM */ PACK( 0, 1 ),
/* MPS */ PACK( 0, 1 ),
/* FlipON */ PACK( 0, 0 ),
/* FlipOFF */ PACK( 0, 0 ),
/* DEBUG */ PACK( 1, 0 ),
/* LT */ PACK( 2, 1 ),
/* LTEQ */ PACK( 2, 1 ),
/* GT */ PACK( 2, 1 ),
/* GTEQ */ PACK( 2, 1 ),
/* EQ */ PACK( 2, 1 ),
/* NEQ */ PACK( 2, 1 ),
/* ODD */ PACK( 1, 1 ),
/* EVEN */ PACK( 1, 1 ),
/* IF */ PACK( 1, 0 ),
/* EIF */ PACK( 0, 0 ),
/* AND */ PACK( 2, 1 ),
/* OR */ PACK( 2, 1 ),
/* NOT */ PACK( 1, 1 ),
/* DeltaP1 */ PACK( 1, 0 ),
/* SDB */ PACK( 1, 0 ),
/* SDS */ PACK( 1, 0 ),
/* ADD */ PACK( 2, 1 ),
/* SUB */ PACK( 2, 1 ),
/* DIV */ PACK( 2, 1 ),
/* MUL */ PACK( 2, 1 ),
/* ABS */ PACK( 1, 1 ),
/* NEG */ PACK( 1, 1 ),
/* FLOOR */ PACK( 1, 1 ),
/* CEILING */ PACK( 1, 1 ),
/* ROUND[0] */ PACK( 1, 1 ),
/* ROUND[1] */ PACK( 1, 1 ),
/* ROUND[2] */ PACK( 1, 1 ),
/* ROUND[3] */ PACK( 1, 1 ),
/* NROUND[0] */ PACK( 1, 1 ),
/* NROUND[1] */ PACK( 1, 1 ),
/* NROUND[2] */ PACK( 1, 1 ),
/* NROUND[3] */ PACK( 1, 1 ),
/* WCvtF */ PACK( 2, 0 ),
/* DeltaP2 */ PACK( 1, 0 ),
/* DeltaP3 */ PACK( 1, 0 ),
/* DeltaCn[0] */ PACK( 1, 0 ),
/* DeltaCn[1] */ PACK( 1, 0 ),
/* DeltaCn[2] */ PACK( 1, 0 ),
/* SROUND */ PACK( 1, 0 ),
/* S45Round */ PACK( 1, 0 ),
/* JROT */ PACK( 2, 0 ),
/* JROF */ PACK( 2, 0 ),
/* ROFF */ PACK( 0, 0 ),
/* INS_$7B */ PACK( 0, 0 ),
/* RUTG */ PACK( 0, 0 ),
/* RDTG */ PACK( 0, 0 ),
/* SANGW */ PACK( 1, 0 ),
/* AA */ PACK( 1, 0 ),
/* FlipPT */ PACK( 0, 0 ),
/* FlipRgON */ PACK( 2, 0 ),
/* FlipRgOFF */ PACK( 2, 0 ),
/* INS_$83 */ PACK( 0, 0 ),
/* INS_$84 */ PACK( 0, 0 ),
/* ScanCTRL */ PACK( 1, 0 ),
/* SDVPTL[0] */ PACK( 2, 0 ),
/* SDVPTL[1] */ PACK( 2, 0 ),
/* GetINFO */ PACK( 1, 1 ),
/* IDEF */ PACK( 1, 0 ),
/* ROLL */ PACK( 3, 3 ),
/* MAX */ PACK( 2, 1 ),
/* MIN */ PACK( 2, 1 ),
/* ScanTYPE */ PACK( 1, 0 ),
/* InstCTRL */ PACK( 2, 0 ),
/* INS_$8F */ PACK( 0, 0 ),
/* INS_$90 */ PACK( 0, 0 ),
/* INS_$91 */ PACK( 0, 0 ),
/* INS_$92 */ PACK( 0, 0 ),
/* INS_$93 */ PACK( 0, 0 ),
/* INS_$94 */ PACK( 0, 0 ),
/* INS_$95 */ PACK( 0, 0 ),
/* INS_$96 */ PACK( 0, 0 ),
/* INS_$97 */ PACK( 0, 0 ),
/* INS_$98 */ PACK( 0, 0 ),
/* INS_$99 */ PACK( 0, 0 ),
/* INS_$9A */ PACK( 0, 0 ),
/* INS_$9B */ PACK( 0, 0 ),
/* INS_$9C */ PACK( 0, 0 ),
/* INS_$9D */ PACK( 0, 0 ),
/* INS_$9E */ PACK( 0, 0 ),
/* INS_$9F */ PACK( 0, 0 ),
/* INS_$A0 */ PACK( 0, 0 ),
/* INS_$A1 */ PACK( 0, 0 ),
/* INS_$A2 */ PACK( 0, 0 ),
/* INS_$A3 */ PACK( 0, 0 ),
/* INS_$A4 */ PACK( 0, 0 ),
/* INS_$A5 */ PACK( 0, 0 ),
/* INS_$A6 */ PACK( 0, 0 ),
/* INS_$A7 */ PACK( 0, 0 ),
/* INS_$A8 */ PACK( 0, 0 ),
/* INS_$A9 */ PACK( 0, 0 ),
/* INS_$AA */ PACK( 0, 0 ),
/* INS_$AB */ PACK( 0, 0 ),
/* INS_$AC */ PACK( 0, 0 ),
/* INS_$AD */ PACK( 0, 0 ),
/* INS_$AE */ PACK( 0, 0 ),
/* INS_$AF */ PACK( 0, 0 ),
/* PushB[0] */ PACK( 0, 1 ),
/* PushB[1] */ PACK( 0, 2 ),
/* PushB[2] */ PACK( 0, 3 ),
/* PushB[3] */ PACK( 0, 4 ),
/* PushB[4] */ PACK( 0, 5 ),
/* PushB[5] */ PACK( 0, 6 ),
/* PushB[6] */ PACK( 0, 7 ),
/* PushB[7] */ PACK( 0, 8 ),
/* PushW[0] */ PACK( 0, 1 ),
/* PushW[1] */ PACK( 0, 2 ),
/* PushW[2] */ PACK( 0, 3 ),
/* PushW[3] */ PACK( 0, 4 ),
/* PushW[4] */ PACK( 0, 5 ),
/* PushW[5] */ PACK( 0, 6 ),
/* PushW[6] */ PACK( 0, 7 ),
/* PushW[7] */ PACK( 0, 8 ),
/* MDRP[00] */ PACK( 1, 0 ),
/* MDRP[01] */ PACK( 1, 0 ),
/* MDRP[02] */ PACK( 1, 0 ),
/* MDRP[03] */ PACK( 1, 0 ),
/* MDRP[04] */ PACK( 1, 0 ),
/* MDRP[05] */ PACK( 1, 0 ),
/* MDRP[06] */ PACK( 1, 0 ),
/* MDRP[07] */ PACK( 1, 0 ),
/* MDRP[08] */ PACK( 1, 0 ),
/* MDRP[09] */ PACK( 1, 0 ),
/* MDRP[10] */ PACK( 1, 0 ),
/* MDRP[11] */ PACK( 1, 0 ),
/* MDRP[12] */ PACK( 1, 0 ),
/* MDRP[13] */ PACK( 1, 0 ),
/* MDRP[14] */ PACK( 1, 0 ),
/* MDRP[15] */ PACK( 1, 0 ),
/* MDRP[16] */ PACK( 1, 0 ),
/* MDRP[17] */ PACK( 1, 0 ),
/* MDRP[18] */ PACK( 1, 0 ),
/* MDRP[19] */ PACK( 1, 0 ),
/* MDRP[20] */ PACK( 1, 0 ),
/* MDRP[21] */ PACK( 1, 0 ),
/* MDRP[22] */ PACK( 1, 0 ),
/* MDRP[23] */ PACK( 1, 0 ),
/* MDRP[24] */ PACK( 1, 0 ),
/* MDRP[25] */ PACK( 1, 0 ),
/* MDRP[26] */ PACK( 1, 0 ),
/* MDRP[27] */ PACK( 1, 0 ),
/* MDRP[28] */ PACK( 1, 0 ),
/* MDRP[29] */ PACK( 1, 0 ),
/* MDRP[30] */ PACK( 1, 0 ),
/* MDRP[31] */ PACK( 1, 0 ),
/* MIRP[00] */ PACK( 2, 0 ),
/* MIRP[01] */ PACK( 2, 0 ),
/* MIRP[02] */ PACK( 2, 0 ),
/* MIRP[03] */ PACK( 2, 0 ),
/* MIRP[04] */ PACK( 2, 0 ),
/* MIRP[05] */ PACK( 2, 0 ),
/* MIRP[06] */ PACK( 2, 0 ),
/* MIRP[07] */ PACK( 2, 0 ),
/* MIRP[08] */ PACK( 2, 0 ),
/* MIRP[09] */ PACK( 2, 0 ),
/* MIRP[10] */ PACK( 2, 0 ),
/* MIRP[11] */ PACK( 2, 0 ),
/* MIRP[12] */ PACK( 2, 0 ),
/* MIRP[13] */ PACK( 2, 0 ),
/* MIRP[14] */ PACK( 2, 0 ),
/* MIRP[15] */ PACK( 2, 0 ),
/* MIRP[16] */ PACK( 2, 0 ),
/* MIRP[17] */ PACK( 2, 0 ),
/* MIRP[18] */ PACK( 2, 0 ),
/* MIRP[19] */ PACK( 2, 0 ),
/* MIRP[20] */ PACK( 2, 0 ),
/* MIRP[21] */ PACK( 2, 0 ),
/* MIRP[22] */ PACK( 2, 0 ),
/* MIRP[23] */ PACK( 2, 0 ),
/* MIRP[24] */ PACK( 2, 0 ),
/* MIRP[25] */ PACK( 2, 0 ),
/* MIRP[26] */ PACK( 2, 0 ),
/* MIRP[27] */ PACK( 2, 0 ),
/* MIRP[28] */ PACK( 2, 0 ),
/* MIRP[29] */ PACK( 2, 0 ),
/* MIRP[30] */ PACK( 2, 0 ),
/* MIRP[31] */ PACK( 2, 0 )
};
static const TT_Vector Null_Vector = {0,0};
#undef NULL_Vector
#define NULL_Vector (TT_Vector*)&Null_Vector
/*******************************************************************
*
* Function : Norm
*
* Description : Returns the norm (length) of a vector.
*
* Input : X, Y vector
*
* Output : Returns length in F26dot6.
*
*****************************************************************/
static TT_F26Dot6 Norm( TT_F26Dot6 X, TT_F26Dot6 Y )
{
TT_Int64 T1, T2;
MUL_64( X, X, T1 );
MUL_64( Y, Y, T2 );
ADD_64( T1, T2, T1 );
return (TT_F26Dot6)SQRT_64( T1 );
}
/*******************************************************************
*
* Function : FUnits_To_Pixels
*
* Description : Scale a distance in FUnits to pixel coordinates.
*
* Input : Distance in FUnits
*
* Output : Distance in 26.6 format.
*
*****************************************************************/
static TT_F26Dot6 FUnits_To_Pixels( EXEC_OPS Short distance )
{
return TT_MulDiv( distance,
CUR.metrics.scale1,
CUR.metrics.scale2 );
}
/*******************************************************************
*
* Function : Current_Ratio
*
* Description : Return the current aspect ratio scaling factor
* depending on the projection vector's state and
* device resolutions.
*
* Input : None
*
* Output : Aspect ratio in 16.16 format, always <= 1.0 .
*
*****************************************************************/
static Long Current_Ratio( EXEC_OP )
{
if ( CUR.metrics.ratio )
return CUR.metrics.ratio;
if ( CUR.GS.projVector.y == 0 )
CUR.metrics.ratio = CUR.metrics.x_ratio;
else if ( CUR.GS.projVector.x == 0 )
CUR.metrics.ratio = CUR.metrics.y_ratio;
else
{
Long x, y;
x = TT_MulDiv( CUR.GS.projVector.x, CUR.metrics.x_ratio, 0x4000 );
y = TT_MulDiv( CUR.GS.projVector.y, CUR.metrics.y_ratio, 0x4000 );
CUR.metrics.ratio = Norm( x, y );
}
return CUR.metrics.ratio;
}
static Long Current_Ppem( EXEC_OP )
{
return TT_MulFix( CUR.metrics.ppem, CURRENT_Ratio() );
}
static TT_F26Dot6 Read_CVT( EXEC_OPS ULong index )
{
return CUR.cvt[index];
}
static TT_F26Dot6 Read_CVT_Stretched( EXEC_OPS ULong index )
{
return TT_MulFix( CUR.cvt[index], CURRENT_Ratio() );
}
static void Write_CVT( EXEC_OPS ULong index, TT_F26Dot6 value )
{
CUR.cvt[index] = value;
}
static void Write_CVT_Stretched( EXEC_OPS ULong index, TT_F26Dot6 value )
{
CUR.cvt[index] = TT_MulDiv( value, 0x10000, CURRENT_Ratio() );
}
static void Move_CVT( EXEC_OPS ULong index, TT_F26Dot6 value )
{
CUR.cvt[index] += value;
}
static void Move_CVT_Stretched( EXEC_OPS ULong index, TT_F26Dot6 value )
{
CUR.cvt[index] += TT_MulDiv( value, 0x10000, CURRENT_Ratio() );
}
/******************************************************************
*
* Function : Calc_Length
*
* Description : Computes the length in bytes of current opcode.
*
*****************************************************************/
static Bool Calc_Length( EXEC_OP )
{
CUR.opcode = CUR.code[CUR.IP];
switch ( CUR.opcode )
{
case 0x40:
if ( CUR.IP + 1 >= CUR.codeSize )
return FAILURE;
CUR.length = CUR.code[CUR.IP + 1] + 2;
break;
case 0x41:
if ( CUR.IP + 1 >= CUR.codeSize )
return FAILURE;
CUR.length = CUR.code[CUR.IP + 1] * 2 + 2;
break;
case 0xB0:
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB6:
case 0xB7:
CUR.length = CUR.opcode - 0xB0 + 2;
break;
case 0xB8:
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBE:
case 0xBF:
CUR.length = (CUR.opcode - 0xB8) * 2 + 3;
break;
default:
CUR.length = 1;
break;
}
/* make sure result is in range */
if ( CUR.IP + CUR.length > CUR.codeSize )
return FAILURE;
return SUCCESS_OK;
}
/*******************************************************************
*
* Function : GetShortIns
*
* Description : Returns a short integer taken from the instruction
* stream at address IP.
*
* Input : None
*
* Output : Short read at Code^[IP..IP+1]
*
* Notes : This one could become a Macro in the C version.
*
*****************************************************************/
static Short GetShortIns( EXEC_OP )
{
/* Reading a byte stream so there is no endianess (DaveP) */
CUR.IP += 2;
return (Short)((CUR.code[CUR.IP - 2] << 8) + CUR.code[CUR.IP - 1]);
}
/*******************************************************************
*
* Function : Ins_Goto_CodeRange
*
* Description : Goes to a certain code range in the instruction
* stream.
*
*
* Input : aRange
* aIP
*
* Output : SUCCESS or FAILURE.
*
*****************************************************************/
static Bool Ins_Goto_CodeRange( EXEC_OPS Int aRange, ULong aIP )
{
TCodeRange* WITH;
if ( aRange < 1 || aRange > 3 )
{
CUR.error = TT_Err_Bad_Argument;
return FAILURE;
}
WITH = &CUR.codeRangeTable[aRange - 1];
if ( WITH->Base == NULL ) /* invalid coderange */
{
CUR.error = TT_Err_Invalid_CodeRange;
return FAILURE;
}
/* NOTE: Because the last instruction of a program may be a CALL */
/* which will return to the first byte *after* the code */
/* range, we test for aIP <= Size, instead of aIP < Size. */
if ( aIP > WITH->Size )
{
CUR.error = TT_Err_Code_Overflow;
return FAILURE;
}
CUR.code = WITH->Base;
CUR.codeSize = WITH->Size;
CUR.IP = aIP;
CUR.curRange = aRange;
return SUCCESS_OK;
}
/*******************************************************************
*
* Function : Direct_Move
*
* Description : Moves a point by a given distance along the
* freedom vector. The point will be touched.
*
* Input : point index of point to move
* distance distance to apply
* zone affected glyph zone
*
* Output : None
*
*****************************************************************/
static void Direct_Move( EXEC_OPS PGlyph_Zone zone,
UShort point,
TT_F26Dot6 distance )
{
TT_F26Dot6 v;
v = CUR.GS.freeVector.x;
if ( v != 0 )
{
zone->cur[point].x += TT_MulDiv( distance,
v * 0x10000L,
CUR.F_dot_P );
zone->touch[point] |= TT_Flag_Touched_X;
}
v = CUR.GS.freeVector.y;
if ( v != 0 )
{
zone->cur[point].y += TT_MulDiv( distance,
v * 0x10000L,
CUR.F_dot_P );
zone->touch[point] |= TT_Flag_Touched_Y;
}
}
/******************************************************************/
/* */
/* The following versions are used whenever both vectors are both */
/* along one of the coordinate unit vectors, i.e. in 90% cases. */
/* */
/******************************************************************/
/*******************************************************************
* Direct_Move_X
*
*******************************************************************/
static void Direct_Move_X( EXEC_OPS PGlyph_Zone zone,
UShort point,
TT_F26Dot6 distance )
{
zone->cur[point].x += distance;
zone->touch[point] |= TT_Flag_Touched_X;
}
/*******************************************************************
* Direct_Move_Y
*
*******************************************************************/
static void Direct_Move_Y( EXEC_OPS PGlyph_Zone zone,
UShort point,
TT_F26Dot6 distance )
{
zone->cur[point].y += distance;
zone->touch[point] |= TT_Flag_Touched_Y;
}
/*******************************************************************
*
* Function : Round_None
*
* Description : Does not round, but adds engine compensation.
*
* Input : distance : distance to round
* compensation : engine compensation
*
* Output : rounded distance.
*
* NOTE : The spec says very few about the relationship between
* rounding and engine compensation. However, it seems
* from the description of super round that we should
* should add the compensation before rounding.
*
******************************************************************/
static TT_F26Dot6 Round_None( EXEC_OPS TT_F26Dot6 distance,
TT_F26Dot6 compensation )
{
TT_F26Dot6 val;
if ( distance >= 0 )
{
val = distance + compensation;
if ( val < 0 )
val = 0;
}
else {
val = distance - compensation;
if ( val > 0 )
val = 0;
}
return val;
}
/*******************************************************************
*
* Function : Round_To_Grid
*
* Description : Rounds value to grid after adding engine
* compensation
*
* Input : distance : distance to round
* compensation : engine compensation
*
* Output : Rounded distance.
*
*****************************************************************/
static TT_F26Dot6 Round_To_Grid( EXEC_OPS TT_F26Dot6 distance,
TT_F26Dot6 compensation )
{
TT_F26Dot6 val;
if ( distance >= 0 )
{
val = distance + compensation + 32;
if ( val > 0 )
val &= ~63;
else
val = 0;
}
else
{
val = -( (compensation - distance + 32) & (-64) );
if ( val > 0 )
val = 0;
}
return val;
}
/*******************************************************************
*
* Function : Round_To_Half_Grid
*
* Description : Rounds value to half grid after adding engine
* compensation.
*
* Input : distance : distance to round
* compensation : engine compensation
*
* Output : Rounded distance.
*
*****************************************************************/
static TT_F26Dot6 Round_To_Half_Grid( EXEC_OPS TT_F26Dot6 distance,
TT_F26Dot6 compensation )
{
TT_F26Dot6 val;
if ( distance >= 0 )
{
val = ((distance + compensation) & (-64)) + 32;
if ( val < 0 )
val = 0;
}
else
{
val = -( ((compensation - distance) & (-64)) + 32 );
if ( val > 0 )
val = 0;
}
return val;
}
/*******************************************************************
*
* Function : Round_Down_To_Grid
*
* Description : Rounds value down to grid after adding engine
* compensation.
*
* Input : distance : distance to round
* compensation : engine compensation
*
* Output : Rounded distance.
*
*****************************************************************/
static TT_F26Dot6 Round_Down_To_Grid( EXEC_OPS TT_F26Dot6 distance,
TT_F26Dot6 compensation )
{
TT_F26Dot6 val;
if ( distance >= 0 )
{
val = distance + compensation;
if ( val > 0 )
val &= ~63;
else
val = 0;
}
else
{
val = -( (compensation - distance) & (-64) );
if ( val > 0 )
val = 0;
}
return val;
}
/*******************************************************************
*
* Function : Round_Up_To_Grid
*
* Description : Rounds value up to grid after adding engine
* compensation.
*
* Input : distance : distance to round
* compensation : engine compensation
*
* Output : Rounded distance.
*
*****************************************************************/
static TT_F26Dot6 Round_Up_To_Grid( EXEC_OPS TT_F26Dot6 distance,
TT_F26Dot6 compensation )
{
TT_F26Dot6 val;
if ( distance >= 0 )
{
val = distance + compensation + 63;
if ( val > 0 )
val &= ~63;
else
val = 0;
}
else
{
val = -( (compensation - distance + 63) & (-64) );
if ( val > 0 )
val = 0;
}
return val;
}
/*******************************************************************
*
* Function : Round_To_Double_Grid
*
* Description : Rounds value to double grid after adding engine
* compensation.
*
* Input : distance : distance to round
* compensation : engine compensation
*
* Output : Rounded distance.
*
*****************************************************************/
static TT_F26Dot6 Round_To_Double_Grid( EXEC_OPS TT_F26Dot6 distance,
TT_F26Dot6 compensation )
{
TT_F26Dot6 val;
if ( distance >= 0 )
{
val = distance + compensation + 16;
if ( val > 0 )
val &= ~31;
else
val = 0;
}
else
{
val = -( (compensation - distance + 16) & (-32) );
if ( val > 0 )
val = 0;
}
return val;
}
/*******************************************************************
*
* Function : Round_Super
*
* Description : Super-rounds value to grid after adding engine
* compensation.
*
* Input : distance : distance to round
* compensation : engine compensation
*
* Output : Rounded distance.
*
* NOTE : The spec says very few about the relationship between
* rounding and engine compensation. However, it seems
* from the description of super round that we should
* should add the compensation before rounding.
*
*****************************************************************/
static TT_F26Dot6 Round_Super( EXEC_OPS TT_F26Dot6 distance,
TT_F26Dot6 compensation )
{
TT_F26Dot6 val;
if ( distance >= 0 )
{
val = (distance - CUR.phase + CUR.threshold + compensation) &
(-CUR.period);
if ( val < 0 )
val = 0;
val += CUR.phase;
}
else
{
val = -( (CUR.threshold - CUR.phase - distance + compensation) &
(-CUR.period) );
if ( val > 0 )
val = 0;
val -= CUR.phase;
}
return val;
}
/*******************************************************************
*
* Function : Round_Super_45
*
* Description : Super-rounds value to grid after adding engine
* compensation.
*
* Input : distance : distance to round
* compensation : engine compensation
*
* Output : Rounded distance.
*
* NOTE : There is a separate function for Round_Super_45 as we
* may need a greater precision.
*
*****************************************************************/
static TT_F26Dot6 Round_Super_45( EXEC_OPS TT_F26Dot6 distance,
TT_F26Dot6 compensation )
{
TT_F26Dot6 val;
if ( distance >= 0 )
{
val = ( (distance - CUR.phase + CUR.threshold + compensation) /
CUR.period ) * CUR.period;
if ( val < 0 )
val = 0;
val += CUR.phase;
}
else
{
val = -( ( (CUR.threshold - CUR.phase - distance + compensation) /
CUR.period ) * CUR.period );
if ( val > 0 )
val = 0;
val -= CUR.phase;
}
return val;
}
/*******************************************************************
* Compute_Round
*
*****************************************************************/
static void Compute_Round( EXEC_OPS Byte round_mode )
{
switch ( round_mode )
{
case TT_Round_Off:
CUR.func_round = (TRound_Function)Round_None;
break;
case TT_Round_To_Grid:
CUR.func_round = (TRound_Function)Round_To_Grid;
break;
case TT_Round_Up_To_Grid:
CUR.func_round = (TRound_Function)Round_Up_To_Grid;
break;
case TT_Round_Down_To_Grid:
CUR.func_round = (TRound_Function)Round_Down_To_Grid;
break;
case TT_Round_To_Half_Grid:
CUR.func_round = (TRound_Function)Round_To_Half_Grid;
break;
case TT_Round_To_Double_Grid:
CUR.func_round = (TRound_Function)Round_To_Double_Grid;
break;
case TT_Round_Super:
CUR.func_round = (TRound_Function)Round_Super;
break;
case TT_Round_Super_45:
CUR.func_round = (TRound_Function)Round_Super_45;
break;
}
}
/*******************************************************************
*
* Function : SetSuperRound
*
* Description : Sets Super Round parameters.
*
* Input : GridPeriod Grid period
* selector SROUND opcode
*
* Output : None.
*
*****************************************************************/
static void SetSuperRound( EXEC_OPS TT_F26Dot6 GridPeriod,
Long selector )
{
switch ( (Int)(selector & 0xC0) )
{
case 0:
CUR.period = GridPeriod / 2;
break;
case 0x40:
CUR.period = GridPeriod;
break;
case 0x80:
CUR.period = GridPeriod * 2;
break;
/* This opcode is reserved, but... */
case 0xC0:
CUR.period = GridPeriod;
break;
}
switch ( (Int)(selector & 0x30) )
{
case 0:
CUR.phase = 0;
break;
case 0x10:
CUR.phase = CUR.period / 4;
break;
case 0x20:
CUR.phase = CUR.period / 2;
break;
case 0x30:
CUR.phase = GridPeriod * 3 / 4;
break;
}
if ( (selector & 0x0F) == 0 )
CUR.threshold = CUR.period - 1;
else
CUR.threshold = ( (Int)(selector & 0x0F) - 4 ) * CUR.period / 8;
CUR.period /= 256;
CUR.phase /= 256;
CUR.threshold /= 256;
}
/*******************************************************************
*
* Function : Project
*
* Description : Computes the projection of vector given by (v2-v1)
* along the current projection vector.
*
* Input : v1, v2 input vector
*
* Output : Returns distance in F26dot6 format.
*
*****************************************************************/
static TT_F26Dot6 Project( EXEC_OPS TT_Vector* v1,
TT_Vector* v2 )
{
TT_Int64 T1, T2;
MUL_64( v1->x - v2->x, CUR.GS.projVector.x, T1 );
MUL_64( v1->y - v2->y, CUR.GS.projVector.y, T2 );
ADD_64( T1, T2, T1 );
return (TT_F26Dot6)DIV_64( T1, 0x4000L );
}
/*******************************************************************
*
* Function : Dual_Project
*
* Description : Computes the projection of the vector given by
* (v2-v1) along the current dual vector.
*
* Input : v1, v2 input vector
*
* Output : Returns distance in F26dot6 format.
*
*****************************************************************/
static TT_F26Dot6 Dual_Project( EXEC_OPS TT_Vector* v1,
TT_Vector* v2 )
{
TT_Int64 T1, T2;
MUL_64( v1->x - v2->x, CUR.GS.dualVector.x, T1 );
MUL_64( v1->y - v2->y, CUR.GS.dualVector.y, T2 );
ADD_64( T1, T2, T1 );
return (TT_F26Dot6)DIV_64( T1, 0x4000L );
}
/*******************************************************************
*
* Function : Free_Project
*
* Description : Computes the projection of the vector given by
* (v2-v1) along the current freedom vector.
*
* Input : v1, v2 input vector
*
* Output : Returns distance in F26dot6 format.
*
*****************************************************************/
static TT_F26Dot6 Free_Project( EXEC_OPS TT_Vector* v1,
TT_Vector* v2 )
{
TT_Int64 T1, T2;
MUL_64( v1->x - v2->x, CUR.GS.freeVector.x, T1 );
MUL_64( v1->y - v2->y, CUR.GS.freeVector.y, T2 );
ADD_64( T1, T2, T1 );
return (TT_F26Dot6)DIV_64( T1, 0x4000L );
}
/*******************************************************************
*
* Function : Project_x
*
* Input : Vx, Vy input vector
*
* Output : Returns Vx.
*
* Note : Used as a dummy function.
*
*****************************************************************/
static TT_F26Dot6 Project_x( EXEC_OPS TT_Vector* v1,
TT_Vector* v2 )
{
return (v1->x - v2->x);
}
/*******************************************************************
*
* Function : Project_y
*
* Input : Vx, Vy input vector
*
* Output : Returns Vy.
*
* Note : Used as a dummy function.
*
*****************************************************************/
static TT_F26Dot6 Project_y( EXEC_OPS TT_Vector* v1,
TT_Vector* v2 )
{
return (v1->y - v2->y);
}
/*******************************************************************
*
* Function : Compute_Funcs
*
* Description : Computes the projections and movement function
* pointers according to the current graphics state.
*
* Input : None
*
*****************************************************************/
static void Compute_Funcs( EXEC_OP )
{
if ( CUR.GS.freeVector.x == 0x4000 )
{
CUR.func_freeProj = (TProject_Function)Project_x;
CUR.F_dot_P = CUR.GS.projVector.x * 0x10000L;
}
else
{
if ( CUR.GS.freeVector.y == 0x4000 )
{
CUR.func_freeProj = (TProject_Function)Project_y;
CUR.F_dot_P = CUR.GS.projVector.y * 0x10000L;
}
else
{
CUR.func_freeProj = (TProject_Function)Free_Project;
CUR.F_dot_P = (Long)CUR.GS.projVector.x * CUR.GS.freeVector.x * 4 +
(Long)CUR.GS.projVector.y * CUR.GS.freeVector.y * 4;
}
}
CUR.cached_metrics = FALSE;
if ( CUR.GS.projVector.x == 0x4000 )
CUR.func_project = (TProject_Function)Project_x;
else
{
if ( CUR.GS.projVector.y == 0x4000 )
CUR.func_project = (TProject_Function)Project_y;
else
CUR.func_project = (TProject_Function)Project;
}
if ( CUR.GS.dualVector.x == 0x4000 )
CUR.func_dualproj = (TProject_Function)Project_x;
else
{
if ( CUR.GS.dualVector.y == 0x4000 )
CUR.func_dualproj = (TProject_Function)Project_y;
else
CUR.func_dualproj = (TProject_Function)Dual_Project;
}
CUR.func_move = (TMove_Function)Direct_Move;
if ( CUR.F_dot_P == 0x40000000L )
{
if ( CUR.GS.freeVector.x == 0x4000 )
CUR.func_move = (TMove_Function)Direct_Move_X;
else
{
if ( CUR.GS.freeVector.y == 0x4000 )
CUR.func_move = (TMove_Function)Direct_Move_Y;
}
}
/* at small sizes, F_dot_P can become too small, resulting */
/* in overflows and 'spikes' in a number of glyphs like 'w'. */
if ( ABS( CUR.F_dot_P ) < 0x4000000L )
CUR.F_dot_P = 0x40000000L;
/* Disable cached aspect ratio */
CUR.metrics.ratio = 0;
}
/*******************************************************************
*
* Function : Normalize
*
* Description : Norms a vector
*
* Input : Vx, Vy input vector
* R normed unit vector
*
* Output : Returns FAILURE if a vector parameter is zero.
*
*****************************************************************/
static Bool Normalize( EXEC_OPS TT_F26Dot6 Vx,
TT_F26Dot6 Vy,
TT_UnitVector* R )
{
TT_F26Dot6 W;
Bool S1, S2;
if ( ABS( Vx ) < 0x10000L && ABS( Vy ) < 0x10000L )
{
Vx *= 0x100;
Vy *= 0x100;
W = Norm( Vx, Vy );
if ( W == 0 )
{
/* XXX : UNDOCUMENTED! It seems that it's possible to try */
/* to normalize the vector (0,0). Return immediately */
return SUCCESS_OK;
}
R->x = (TT_F2Dot14)TT_MulDiv( Vx, 0x4000L, W );
R->y = (TT_F2Dot14)TT_MulDiv( Vy, 0x4000L, W );
return SUCCESS_OK;
}
W = Norm( Vx, Vy );
Vx = TT_MulDiv( Vx, 0x4000L, W );
Vy = TT_MulDiv( Vy, 0x4000L, W );
W = Vx * Vx + Vy * Vy;
/* Now, we want that Sqrt( W ) = 0x4000 */
/* Or 0x1000000 <= W < 0x1004000 */
if ( Vx < 0 )
{
Vx = -Vx;
S1 = TRUE;
}
else
S1 = FALSE;
if ( Vy < 0 )
{
Vy = -Vy;
S2 = TRUE;
}
else
S2 = FALSE;
while ( W < 0x1000000L )
{
/* We need to increase W, by a minimal amount */
if ( Vx < Vy )
Vx++;
else
Vy++;
W = Vx * Vx + Vy * Vy;
}
while ( W >= 0x1004000L )
{
/* We need to decrease W, by a minimal amount */
if ( Vx < Vy )
Vx--;
else
Vy--;
W = Vx * Vx + Vy * Vy;
}
/* Note that in various cases, we can only */
/* compute a Sqrt(W) of 0x3FFF, eg. Vx = Vy */
if ( S1 )
Vx = -Vx;
if ( S2 )
Vy = -Vy;
R->x = (TT_F2Dot14)Vx; /* Type conversion */
R->y = (TT_F2Dot14)Vy; /* Type conversion */
return SUCCESS_OK;
}
/****************************************************************
*
* Opcodes
*
****************************************************************/
static Bool Ins_SxVTL( EXEC_OPS UShort aIdx1,
UShort aIdx2,
Int aOpc,
TT_UnitVector* Vec )
{
Long A, B, C;
TT_Vector* p1;
TT_Vector* p2;
if ( BOUNDS( aIdx1, CUR.zp2.n_points ) ||
BOUNDS( aIdx2, CUR.zp1.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return FAILURE;
}
p1 = CUR.zp1.cur + aIdx2;
p2 = CUR.zp2.cur + aIdx1;
A = p1->x - p2->x;
B = p1->y - p2->y;
if ( (aOpc & 1) != 0 )
{
C = B; /* CounterClockwise rotation */
B = A;
A = -C;
}
NORMalize( A, B, Vec );
return SUCCESS_OK;
}
/* When not using the big switch statements, the interpreter uses a */
/* call table defined later below in this source. Each opcode must */
/* thus have a corresponding function, even trivial ones. */
/* */
/* They're all defined there. */
#define DO_SVTCA \
{ \
Short A, B; \
\
\
A = (Short)(CUR.opcode & 1) << 14; \
B = A ^ (Short)0x4000; \
\
CUR.GS.freeVector.x = A; \
CUR.GS.projVector.x = A; \
CUR.GS.dualVector.x = A; \
\
CUR.GS.freeVector.y = B; \
CUR.GS.projVector.y = B; \
CUR.GS.dualVector.y = B; \
\
COMPUTE_Funcs(); \
}
#define DO_SPVTCA \
{ \
Short A, B; \
\
\
A = (Short)(CUR.opcode & 1) << 14; \
B = A ^ (Short)0x4000; \
\
CUR.GS.projVector.x = A; \
CUR.GS.dualVector.x = A; \
\
CUR.GS.projVector.y = B; \
CUR.GS.dualVector.y = B; \
\
COMPUTE_Funcs(); \
}
#define DO_SFVTCA \
{ \
Short A, B; \
\
\
A = (Short)(CUR.opcode & 1) << 14; \
B = A ^ (Short)0x4000; \
\
CUR.GS.freeVector.x = A; \
CUR.GS.freeVector.y = B; \
\
COMPUTE_Funcs(); \
}
#define DO_SPVTL \
if ( INS_SxVTL( (UShort)args[1], \
(UShort)args[0], \
CUR.opcode, \
&CUR.GS.projVector) == SUCCESS_OK ) \
{ \
CUR.GS.dualVector = CUR.GS.projVector; \
COMPUTE_Funcs(); \
}
#define DO_SFVTL \
if ( INS_SxVTL( (UShort)args[1], \
(UShort)args[0], \
CUR.opcode, \
&CUR.GS.freeVector) == SUCCESS_OK ) \
COMPUTE_Funcs();
#define DO_SFVTPV \
CUR.GS.freeVector = CUR.GS.projVector; \
COMPUTE_Funcs();
#define DO_SPVFS \
{ \
Short S; \
Long X, Y; \
\
\
/* Only use low 16bits, then sign extend */ \
S = (Short)args[1]; \
Y = (Long)S; \
S = (Short)args[0]; \
X = (Long)S; \
\
NORMalize( X, Y, &CUR.GS.projVector ); \
\
CUR.GS.dualVector = CUR.GS.projVector; \
COMPUTE_Funcs(); \
}
#define DO_SFVFS \
{ \
Short S; \
Long X, Y; \
\
\
/* Only use low 16bits, then sign extend */ \
S = (Short)args[1]; \
Y = (Long)S; \
S = (Short)args[0]; \
X = S; \
\
NORMalize( X, Y, &CUR.GS.freeVector ); \
COMPUTE_Funcs(); \
}
#define DO_GPV \
args[0] = CUR.GS.projVector.x; \
args[1] = CUR.GS.projVector.y;
#define DO_GFV \
args[0] = CUR.GS.freeVector.x; \
args[1] = CUR.GS.freeVector.y;
#define DO_SRP0 \
CUR.GS.rp0 = (UShort)args[0];
#define DO_SRP1 \
CUR.GS.rp1 = (UShort)args[0];
#define DO_SRP2 \
CUR.GS.rp2 = (UShort)args[0];
#define DO_RTHG \
CUR.GS.round_state = TT_Round_To_Half_Grid; \
CUR.func_round = (TRound_Function)Round_To_Half_Grid;
#define DO_RTG \
CUR.GS.round_state = TT_Round_To_Grid; \
CUR.func_round = (TRound_Function)Round_To_Grid;
#define DO_RTDG \
CUR.GS.round_state = TT_Round_To_Double_Grid; \
CUR.func_round = (TRound_Function)Round_To_Double_Grid;
#define DO_RUTG \
CUR.GS.round_state = TT_Round_Up_To_Grid; \
CUR.func_round = (TRound_Function)Round_Up_To_Grid;
#define DO_RDTG \
CUR.GS.round_state = TT_Round_Down_To_Grid; \
CUR.func_round = (TRound_Function)Round_Down_To_Grid;
#define DO_ROFF \
CUR.GS.round_state = TT_Round_Off; \
CUR.func_round = (TRound_Function)Round_None;
#define DO_SROUND \
SET_SuperRound( 0x4000L, args[0] ); \
CUR.GS.round_state = TT_Round_Super; \
CUR.func_round = (TRound_Function)Round_Super;
#define DO_S45ROUND \
SET_SuperRound( 0x2D41L, args[0] ); \
CUR.GS.round_state = TT_Round_Super_45; \
CUR.func_round = (TRound_Function)Round_Super_45;
#define DO_SLOOP \
if ( args[0] < 0 ) \
CUR.error = TT_Err_Bad_Argument; \
else \
CUR.GS.loop = args[0];
#define DO_SMD \
CUR.GS.minimum_distance = (TT_F26Dot6)args[0];
#define DO_SCVTCI \
CUR.GS.control_value_cutin = (TT_F26Dot6)args[0];
#define DO_SSWCI \
CUR.GS.single_width_cutin = (TT_F26Dot6)args[0];
/* XXX : UNDOCUMENTED! or bug in the Windows engine? */
/* */
/* It seems that the value that is read here is */
/* expressed in 16.16 format, rather than in */
/* font units.. */
/* */
#define DO_SSW \
CUR.GS.single_width_value = (TT_F26Dot6)(args[0] >> 10);
#define DO_FLIPON \
CUR.GS.auto_flip = TRUE;
#define DO_FLIPOFF \
CUR.GS.auto_flip = FALSE;
#define DO_SDB \
CUR.GS.delta_base = (Short)args[0];
#define DO_SDS \
CUR.GS.delta_shift = (Short)args[0];
#define DO_MD /* nothing */
#define DO_MPPEM \
args[0] = CURRENT_Ppem();
#define DO_MPS \
args[0] = CUR.metrics.pointSize;
#define DO_DUP \
args[1] = args[0];
#define DO_CLEAR \
CUR.new_top = 0;
#define DO_SWAP \
{ \
Long L; \
\
L = args[0]; \
args[0] = args[1]; \
args[1] = L; \
}
#define DO_DEPTH \
args[0] = CUR.top;
#define DO_CINDEX \
{ \
Long L; \
\
\
L = args[0]; \
\
if ( L <= 0 || L > CUR.args ) \
CUR.error = TT_Err_Invalid_Reference; \
else \
args[0] = CUR.stack[CUR.args - L]; \
}
#define DO_JROT \
if ( args[1] != 0 ) \
{ \
CUR.IP += args[0]; \
CUR.step_ins = FALSE; \
}
#define DO_JMPR \
CUR.IP += args[0]; \
CUR.step_ins = FALSE;
#define DO_JROF \
if ( args[1] == 0 ) \
{ \
CUR.IP += args[0]; \
CUR.step_ins = FALSE; \
}
#define DO_LT \
args[0] = (args[0] < args[1]);
#define DO_LTEQ \
args[0] = (args[0] <= args[1]);
#define DO_GT \
args[0] = (args[0] > args[1]);
#define DO_GTEQ \
args[0] = (args[0] >= args[1]);
#define DO_EQ \
args[0] = (args[0] == args[1]);
#define DO_NEQ \
args[0] = (args[0] != args[1]);
#define DO_ODD \
args[0] = ( (CUR_Func_round( args[0], 0 ) & 127) == 64 );
#define DO_EVEN \
args[0] = ( (CUR_Func_round( args[0], 0 ) & 127) == 0 );
#define DO_AND \
args[0] = ( args[0] && args[1] );
#define DO_OR \
args[0] = ( args[0] || args[1] );
#define DO_NOT \
args[0] = !args[0];
#define DO_ADD \
args[0] += args[1];
#define DO_SUB \
args[0] -= args[1];
#define DO_DIV \
if ( args[1] == 0 ) \
CUR.error = TT_Err_Divide_By_Zero; \
else \
args[0] = TT_MulDiv( args[0], 64L, args[1] );
#define DO_MUL \
args[0] = TT_MulDiv( args[0], args[1], 64L );
#define DO_ABS \
args[0] = ABS( args[0] );
#define DO_NEG \
args[0] = -args[0];
#define DO_FLOOR \
args[0] &= -64;
#define DO_CEILING \
args[0] = (args[0] + 63) & (-64);
#define DO_RS \
{ \
ULong I = (ULong)args[0]; \
if ( BOUNDS( I, CUR.storeSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
else \
args[0] = 0; \
} \
else \
args[0] = CUR.storage[I]; \
}
#define DO_WS \
{ \
ULong I = (ULong)args[0]; \
if ( BOUNDS( I, CUR.storeSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
} \
else \
CUR.storage[I] = args[1]; \
}
#define DO_RCVT \
{ \
ULong I = (ULong)args[0]; \
if ( BOUNDS( I, CUR.cvtSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
else \
args[0] = 0; \
} \
else \
args[0] = CUR_Func_read_cvt(I); \
}
#define DO_WCVTP \
{ \
ULong I = (ULong)args[0]; \
if ( BOUNDS( I, CUR.cvtSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
} \
else \
CUR_Func_write_cvt( I, args[1] ); \
}
#define DO_WCVTF \
{ \
ULong I = (ULong)args[0]; \
if ( BOUNDS( I, CUR.cvtSize ) ) \
{ \
if ( CUR.pedantic_hinting ) \
{ \
ARRAY_BOUND_ERROR; \
} \
} \
else \
CUR.cvt[I] = FUnits_To_Pixels( EXEC_ARGS (Short)args[1] ); \
}
#define DO_DEBUG \
CUR.error = TT_Err_Debug_OpCode;
#define DO_ROUND \
args[0] = CUR_Func_round( args[0], \
CUR.metrics.compensations[CUR.opcode-0x68] );
#define DO_NROUND \
args[0] = Round_None( EXEC_ARGS \
args[0], \
CUR.metrics.compensations[CUR.opcode - 0x6C] );
#define DO_MAX \
if ( args[1] > args[0] ) \
args[0] = args[1];
#define DO_MIN \
if ( args[1] < args[0] ) \
args[0] = args[1];
#ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH
#undef ARRAY_BOUND_ERROR
#define ARRAY_BOUND_ERROR \
{ \
CUR.error = TT_Err_Invalid_Reference; \
return; \
}
/*******************************************/
/* SVTCA[a] : Set F and P vectors to axis */
/* CodeRange : $00-$01 */
/* Stack : --> */
static void Ins_SVTCA( INS_ARG )
{
DO_SVTCA
}
/*******************************************/
/* SPVTCA[a] : Set PVector to Axis */
/* CodeRange : $02-$03 */
/* Stack : --> */
static void Ins_SPVTCA( INS_ARG )
{
DO_SPVTCA
}
/*******************************************/
/* SFVTCA[a] : Set FVector to Axis */
/* CodeRange : $04-$05 */
/* Stack : --> */
static void Ins_SFVTCA( INS_ARG )
{
DO_SFVTCA
}
/*******************************************/
/* SPVTL[a] : Set PVector to Line */
/* CodeRange : $06-$07 */
/* Stack : uint32 uint32 --> */
static void Ins_SPVTL( INS_ARG )
{
DO_SPVTL
}
/*******************************************/
/* SFVTL[a] : Set FVector to Line */
/* CodeRange : $08-$09 */
/* Stack : uint32 uint32 --> */
static void Ins_SFVTL( INS_ARG )
{
DO_SFVTL
}
/*******************************************/
/* SFVTPV[] : Set FVector to PVector */
/* CodeRange : $0E */
/* Stack : --> */
static void Ins_SFVTPV( INS_ARG )
{
DO_SFVTPV
}
/*******************************************/
/* SPVFS[] : Set PVector From Stack */
/* CodeRange : $0A */
/* Stack : f2.14 f2.14 --> */
static void Ins_SPVFS( INS_ARG )
{
DO_SPVFS
}
/*******************************************/
/* SFVFS[] : Set FVector From Stack */
/* CodeRange : $0B */
/* Stack : f2.14 f2.14 --> */
static void Ins_SFVFS( INS_ARG )
{
DO_SFVFS
}
/*******************************************/
/* GPV[] : Get Projection Vector */
/* CodeRange : $0C */
/* Stack : ef2.14 --> ef2.14 */
static void Ins_GPV( INS_ARG )
{
DO_GPV
}
/*******************************************/
/* GFV[] : Get Freedom Vector */
/* CodeRange : $0D */
/* Stack : ef2.14 --> ef2.14 */
static void Ins_GFV( INS_ARG )
{
DO_GFV
}
/*******************************************/
/* SRP0[] : Set Reference Point 0 */
/* CodeRange : $10 */
/* Stack : uint32 --> */
static void Ins_SRP0( INS_ARG )
{
DO_SRP0
}
/*******************************************/
/* SRP1[] : Set Reference Point 1 */
/* CodeRange : $11 */
/* Stack : uint32 --> */
static void Ins_SRP1( INS_ARG )
{
DO_SRP1
}
/*******************************************/
/* SRP2[] : Set Reference Point 2 */
/* CodeRange : $12 */
/* Stack : uint32 --> */
static void Ins_SRP2( INS_ARG )
{
DO_SRP2
}
/*******************************************/
/* RTHG[] : Round To Half Grid */
/* CodeRange : $19 */
/* Stack : --> */
static void Ins_RTHG( INS_ARG )
{
DO_RTHG
}
/*******************************************/
/* RTG[] : Round To Grid */
/* CodeRange : $18 */
/* Stack : --> */
static void Ins_RTG( INS_ARG )
{
DO_RTG
}
/*******************************************/
/* RTDG[] : Round To Double Grid */
/* CodeRange : $3D */
/* Stack : --> */
static void Ins_RTDG( INS_ARG )
{
DO_RTDG
}
/*******************************************/
/* RUTG[] : Round Up To Grid */
/* CodeRange : $7C */
/* Stack : --> */
static void Ins_RUTG( INS_ARG )
{
DO_RUTG
}
/*******************************************/
/* RDTG[] : Round Down To Grid */
/* CodeRange : $7D */
/* Stack : --> */
static void Ins_RDTG( INS_ARG )
{
DO_RDTG
}
/*******************************************/
/* ROFF[] : Round OFF */
/* CodeRange : $7A */
/* Stack : --> */
static void Ins_ROFF( INS_ARG )
{
DO_ROFF
}
/*******************************************/
/* SROUND[] : Super ROUND */
/* CodeRange : $76 */
/* Stack : Eint8 --> */
static void Ins_SROUND( INS_ARG )
{
DO_SROUND
}
/*******************************************/
/* S45ROUND[]: Super ROUND 45 degrees */
/* CodeRange : $77 */
/* Stack : uint32 --> */
static void Ins_S45ROUND( INS_ARG )
{
DO_S45ROUND
}
/*******************************************/
/* SLOOP[] : Set LOOP variable */
/* CodeRange : $17 */
/* Stack : int32? --> */
static void Ins_SLOOP( INS_ARG )
{
DO_SLOOP
}
/*******************************************/
/* SMD[] : Set Minimum Distance */
/* CodeRange : $1A */
/* Stack : f26.6 --> */
static void Ins_SMD( INS_ARG )
{
DO_SMD
}
/**********************************************/
/* SCVTCI[] : Set Control Value Table Cut In */
/* CodeRange : $1D */
/* Stack : f26.6 --> */
static void Ins_SCVTCI( INS_ARG )
{
DO_SCVTCI
}
/**********************************************/
/* SSWCI[] : Set Single Width Cut In */
/* CodeRange : $1E */
/* Stack : f26.6 --> */
static void Ins_SSWCI( INS_ARG )
{
DO_SSWCI
}
/**********************************************/
/* SSW[] : Set Single Width */
/* CodeRange : $1F */
/* Stack : int32? --> */
static void Ins_SSW( INS_ARG )
{
DO_SSW
}
/**********************************************/
/* FLIPON[] : Set Auto_flip to On */
/* CodeRange : $4D */
/* Stack : --> */
static void Ins_FLIPON( INS_ARG )
{
DO_FLIPON
}
/**********************************************/
/* FLIPOFF[] : Set Auto_flip to Off */
/* CodeRange : $4E */
/* Stack : --> */
static void Ins_FLIPOFF( INS_ARG )
{
DO_FLIPOFF
}
/**********************************************/
/* SANGW[] : Set Angle Weight */
/* CodeRange : $7E */
/* Stack : uint32 --> */
static void Ins_SANGW( INS_ARG )
{
/* instruction not supported anymore */
}
/**********************************************/
/* SDB[] : Set Delta Base */
/* CodeRange : $5E */
/* Stack : uint32 --> */
static void Ins_SDB( INS_ARG )
{
DO_SDB
}
/**********************************************/
/* SDS[] : Set Delta Shift */
/* CodeRange : $5F */
/* Stack : uint32 --> */
static void Ins_SDS( INS_ARG )
{
DO_SDS
}
/**********************************************/
/* MPPEM[] : Measure Pixel Per EM */
/* CodeRange : $4B */
/* Stack : --> Euint16 */
static void Ins_MPPEM( INS_ARG )
{
DO_MPPEM
}
/**********************************************/
/* MPS[] : Measure PointSize */
/* CodeRange : $4C */
/* Stack : --> Euint16 */
static void Ins_MPS( INS_ARG )
{
DO_MPS
}
/*******************************************/
/* DUP[] : Duplicate top stack element */
/* CodeRange : $20 */
/* Stack : StkElt --> StkElt StkElt */
static void Ins_DUP( INS_ARG )
{
DO_DUP
}
/*******************************************/
/* POP[] : POPs the stack's top elt. */
/* CodeRange : $21 */
/* Stack : StkElt --> */
static void Ins_POP( INS_ARG )
{
/* nothing to do */
}
/*******************************************/
/* CLEAR[] : Clear the entire stack */
/* CodeRange : $22 */
/* Stack : StkElt... --> */
static void Ins_CLEAR( INS_ARG )
{
DO_CLEAR
}
/*******************************************/
/* SWAP[] : Swap the top two elements */
/* CodeRange : $23 */
/* Stack : 2 * StkElt --> 2 * StkElt */
static void Ins_SWAP( INS_ARG )
{
DO_SWAP
}
/*******************************************/
/* DEPTH[] : return the stack depth */
/* CodeRange : $24 */
/* Stack : --> uint32 */
static void Ins_DEPTH( INS_ARG )
{
DO_DEPTH
}
/*******************************************/
/* CINDEX[] : copy indexed element */
/* CodeRange : $25 */
/* Stack : int32 --> StkElt */
static void Ins_CINDEX( INS_ARG )
{
DO_CINDEX
}
/*******************************************/
/* EIF[] : End IF */
/* CodeRange : $59 */
/* Stack : --> */
static void Ins_EIF( INS_ARG )
{
/* nothing to do */
}
/*******************************************/
/* JROT[] : Jump Relative On True */
/* CodeRange : $78 */
/* Stack : StkElt int32 --> */
static void Ins_JROT( INS_ARG )
{
DO_JROT
}
/*******************************************/
/* JMPR[] : JuMP Relative */
/* CodeRange : $1C */
/* Stack : int32 --> */
static void Ins_JMPR( INS_ARG )
{
DO_JMPR
}
/*******************************************/
/* JROF[] : Jump Relative On False */
/* CodeRange : $79 */
/* Stack : StkElt int32 --> */
static void Ins_JROF( INS_ARG )
{
DO_JROF
}
/*******************************************/
/* LT[] : Less Than */
/* CodeRange : $50 */
/* Stack : int32? int32? --> bool */
static void Ins_LT( INS_ARG )
{
DO_LT
}
/*******************************************/
/* LTEQ[] : Less Than or EQual */
/* CodeRange : $51 */
/* Stack : int32? int32? --> bool */
static void Ins_LTEQ( INS_ARG )
{
DO_LTEQ
}
/*******************************************/
/* GT[] : Greater Than */
/* CodeRange : $52 */
/* Stack : int32? int32? --> bool */
static void Ins_GT( INS_ARG )
{
DO_GT
}
/*******************************************/
/* GTEQ[] : Greater Than or EQual */
/* CodeRange : $53 */
/* Stack : int32? int32? --> bool */
static void Ins_GTEQ( INS_ARG )
{
DO_GTEQ
}
/*******************************************/
/* EQ[] : EQual */
/* CodeRange : $54 */
/* Stack : StkElt StkElt --> bool */
static void Ins_EQ( INS_ARG )
{
DO_EQ
}
/*******************************************/
/* NEQ[] : Not EQual */
/* CodeRange : $55 */
/* Stack : StkElt StkElt --> bool */
static void Ins_NEQ( INS_ARG )
{
DO_NEQ
}
/*******************************************/
/* ODD[] : Odd */
/* CodeRange : $56 */
/* Stack : f26.6 --> bool */
static void Ins_ODD( INS_ARG )
{
DO_ODD
}
/*******************************************/
/* EVEN[] : Even */
/* CodeRange : $57 */
/* Stack : f26.6 --> bool */
static void Ins_EVEN( INS_ARG )
{
DO_EVEN
}
/*******************************************/
/* AND[] : logical AND */
/* CodeRange : $5A */
/* Stack : uint32 uint32 --> uint32 */
static void Ins_AND( INS_ARG )
{
DO_AND
}
/*******************************************/
/* OR[] : logical OR */
/* CodeRange : $5B */
/* Stack : uint32 uint32 --> uint32 */
static void Ins_OR( INS_ARG )
{
DO_OR
}
/*******************************************/
/* NOT[] : logical NOT */
/* CodeRange : $5C */
/* Stack : StkElt --> uint32 */
static void Ins_NOT( INS_ARG )
{
DO_NOT
}
/*******************************************/
/* ADD[] : ADD */
/* CodeRange : $60 */
/* Stack : f26.6 f26.6 --> f26.6 */
static void Ins_ADD( INS_ARG )
{
DO_ADD
}
/*******************************************/
/* SUB[] : SUBstract */
/* CodeRange : $61 */
/* Stack : f26.6 f26.6 --> f26.6 */
static void Ins_SUB( INS_ARG )
{
DO_SUB
}
/*******************************************/
/* DIV[] : DIVide */
/* CodeRange : $62 */
/* Stack : f26.6 f26.6 --> f26.6 */
static void Ins_DIV( INS_ARG )
{
DO_DIV
}
/*******************************************/
/* MUL[] : MULtiply */
/* CodeRange : $63 */
/* Stack : f26.6 f26.6 --> f26.6 */
static void Ins_MUL( INS_ARG )
{
DO_MUL
}
/*******************************************/
/* ABS[] : ABSolute value */
/* CodeRange : $64 */
/* Stack : f26.6 --> f26.6 */
static void Ins_ABS( INS_ARG )
{
DO_ABS
}
/*******************************************/
/* NEG[] : NEGate */
/* CodeRange : $65 */
/* Stack : f26.6 --> f26.6 */
static void Ins_NEG( INS_ARG )
{
DO_NEG
}
/*******************************************/
/* FLOOR[] : FLOOR */
/* CodeRange : $66 */
/* Stack : f26.6 --> f26.6 */
static void Ins_FLOOR( INS_ARG )
{
DO_FLOOR
}
/*******************************************/
/* CEILING[] : CEILING */
/* CodeRange : $67 */
/* f26.6 --> f26.6 */
static void Ins_CEILING( INS_ARG )
{
DO_CEILING
}
/*******************************************/
/* RS[] : Read Store */
/* CodeRange : $43 */
/* Stack : uint32 --> uint32 */
static void Ins_RS( INS_ARG )
{
DO_RS
}
/*******************************************/
/* WS[] : Write Store */
/* CodeRange : $42 */
/* Stack : uint32 uint32 --> */
static void Ins_WS( INS_ARG )
{
DO_WS
}
/*******************************************/
/* WCVTP[] : Write CVT in Pixel units */
/* CodeRange : $44 */
/* Stack : f26.6 uint32 --> */
static void Ins_WCVTP( INS_ARG )
{
DO_WCVTP
}
/*******************************************/
/* WCVTF[] : Write CVT in FUnits */
/* CodeRange : $70 */
/* Stack : uint32 uint32 --> */
static void Ins_WCVTF( INS_ARG )
{
DO_WCVTF
}
/*******************************************/
/* RCVT[] : Read CVT */
/* CodeRange : $45 */
/* Stack : uint32 --> f26.6 */
static void Ins_RCVT( INS_ARG )
{
DO_RCVT
}
/********************************************/
/* AA[] : Adjust Angle */
/* CodeRange : $7F */
/* Stack : uint32 --> */
static void Ins_AA( INS_ARG )
{
/* Intentional - no longer supported */
}
/********************************************/
/* DEBUG[] : DEBUG. Unsupported */
/* CodeRange : $4F */
/* Stack : uint32 --> */
/* NOTE : The original instruction pops a value from the stack */
static void Ins_DEBUG( INS_ARG )
{
DO_DEBUG
}
/*******************************************/
/* ROUND[ab] : ROUND value */
/* CodeRange : $68-$6B */
/* Stack : f26.6 --> f26.6 */
static void Ins_ROUND( INS_ARG )
{
DO_ROUND
}
/*******************************************/
/* NROUND[ab]: No ROUNDing of value */
/* CodeRange : $6C-$6F */
/* Stack : f26.6 --> f26.6 */
static void Ins_NROUND( INS_ARG )
{
DO_NROUND
}
/*******************************************/
/* MAX[] : MAXimum */
/* CodeRange : $68 */
/* Stack : int32? int32? --> int32 */
static void Ins_MAX( INS_ARG )
{
DO_MAX
}
/*******************************************/
/* MIN[] : MINimum */
/* CodeRange : $69 */
/* Stack : int32? int32? --> int32 */
static void Ins_MIN( INS_ARG )
{
DO_MIN
}
#endif /* !TT_CONFIG_OPTION_INTERPRETER_SWITCH */
/* The following functions are called as is within the switch statement */
/*******************************************/
/* MINDEX[] : move indexed element */
/* CodeRange : $26 */
/* Stack : int32? --> StkElt */
static void Ins_MINDEX( INS_ARG )
{
Long L, K;
L = args[0];
if ( L <= 0 || L > CUR.args )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
K = CUR.stack[CUR.args - L];
MEM_Move( (&CUR.stack[CUR.args - L ]),
(&CUR.stack[CUR.args - L + 1]),
(L - 1) * sizeof ( Long ) );
CUR.stack[CUR.args - 1] = K;
}
/*******************************************/
/* ROLL[] : roll top three elements */
/* CodeRange : $8A */
/* Stack : 3 * StkElt --> 3 * StkElt */
static void Ins_ROLL( INS_ARG )
{
Long A, B, C;
A = args[2];
B = args[1];
C = args[0];
args[2] = C;
args[1] = A;
args[0] = B;
}
/****************************************************************/
/* */
/* MANAGING THE FLOW OF CONTROL */
/* */
/* Instructions appear in the specs' order. */
/* */
/****************************************************************/
static Bool SkipCode( EXEC_OP )
{
CUR.IP += CUR.length;
if ( CUR.IP < CUR.codeSize )
if ( CALC_Length() == SUCCESS_OK )
return SUCCESS_OK;
CUR.error = TT_Err_Code_Overflow;
return FAILURE;
}
/*******************************************/
/* IF[] : IF test */
/* CodeRange : $58 */
/* Stack : StkElt --> */
static void Ins_IF( INS_ARG )
{
Int nIfs;
Bool Out;
if ( args[0] != 0 )
return;
nIfs = 1;
Out = 0;
do
{
if ( SKIP_Code() == FAILURE )
return;
switch ( CUR.opcode )
{
case 0x58: /* IF */
nIfs++;
break;
case 0x1b: /* ELSE */
Out = (nIfs == 1);
break;
case 0x59: /* EIF */
nIfs--;
Out = (nIfs == 0);
break;
}
} while ( Out == 0 );
}
/*******************************************/
/* ELSE[] : ELSE */
/* CodeRange : $1B */
/* Stack : --> */
static void Ins_ELSE( INS_ARG )
{
Int nIfs;
nIfs = 1;
do
{
if ( SKIP_Code() == FAILURE )
return;
switch ( CUR.opcode )
{
case 0x58: /* IF */
nIfs++;
break;
case 0x59: /* EIF */
nIfs--;
break;
}
} while ( nIfs != 0 );
}
/****************************************************************/
/* */
/* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS */
/* */
/* Instructions appear in the specs' order. */
/* */
/****************************************************************/
static PDefRecord Locate_FDef( EXEC_OPS Int n, Bool new_def )
{
PDefRecord def;
UShort hash;
UShort cnt;
/* The function table is interpreted as a simple hash table */
/* with indexes computed modulo maxFDefs and the linear search */
/* of free cells in the case of a collision. */
/* Except for some old Apple fonts, all functions in a TrueType */
/* font fit into 0..maxFDefs - 1 range and the lookup is */
/* reduced to a single step. */
/* Minor optimization. */
if ( !new_def && ( n < 0 || n > CUR.maxFunc ) )
return NULL;
for ( cnt = 0; cnt < CUR.maxFDefs; ++cnt )
{
hash = ( (UShort)n + cnt ) % CUR.maxFDefs;
def = &CUR.FDefs[ hash ];
if ( !def->Active )
return new_def ? def : NULL;
if ( def->Opc == n )
return def;
}
/* The table is full and the entry has not been found. */
return NULL;
}
/*******************************************/
/* FDEF[] : Function DEFinition */
/* CodeRange : $2C */
/* Stack : uint32 --> */
static void Ins_FDEF( INS_ARG )
{
Int n;
PDefRecord def;
/* check that there is enough room */
if ( CUR.numFDefs >= CUR.maxFDefs )
{
/* We could introduce a new error message, but we're too close */
/* from the release to change all the 'po' files again.. */
CUR.error = TT_Err_Too_Many_Ins;
return;
}
n = (Int)args[0];
if ( n < 0 || (ULong)n != args[0] )
{
/* Gotcha. Function index is uint32 according to the specs */
/* but TDefRecord.Opc is defined as Int. We cannot store */
/* the definition of this function. */
CUR.error = TT_Err_Bad_Argument;
return;
}
def = Locate_FDef( EXEC_ARGS n, TRUE );
if ( !def )
{
/* Oh, oh. Something is wrong. Locate_FDef should never fail here. */
CUR.error = TT_Err_Too_Many_Ins;
return;
}
/* Some font programs are broken enough to redefine functions! */
if ( !def->Active )
CUR.numFDefs++;
def->Range = CUR.curRange;
def->Opc = n;
def->Start = CUR.IP + 1;
def->Active = TRUE;
if ( n > CUR.maxFunc )
CUR.maxFunc = n;
/* Now skip the whole function definition. */
/* We don't allow nested IDEFS & FDEFs. */
while ( SKIP_Code() == SUCCESS_OK )
{
switch ( CUR.opcode )
{
case 0x89: /* IDEF */
case 0x2c: /* FDEF */
CUR.error = TT_Err_Nested_DEFS;
return;
case 0x2d: /* ENDF */
return;
}
}
}
/*******************************************/
/* ENDF[] : END Function definition */
/* CodeRange : $2D */
/* Stack : --> */
static void Ins_ENDF( INS_ARG )
{
PCallRecord pRec;
if ( CUR.callTop <= 0 ) /* We encountered an ENDF without a call */
{
CUR.error = TT_Err_ENDF_In_Exec_Stream;
return;
}
CUR.callTop--;
pRec = &CUR.callStack[CUR.callTop];
pRec->Cur_Count--;
CUR.step_ins = FALSE;
if ( pRec->Cur_Count > 0 )
{
CUR.callTop++;
CUR.IP = pRec->Cur_Restart;
}
else
/* Loop through the current function */
INS_Goto_CodeRange( pRec->Caller_Range,
pRec->Caller_IP );
/* Exit the current call frame. */
/* NOTE: When the last intruction 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's why we do not test */
/* the result of Ins_Goto_CodeRange() here! */
}
/*******************************************/
/* CALL[] : CALL function */
/* CodeRange : $2B */
/* Stack : uint32? --> */
static void Ins_CALL( INS_ARG )
{
Int n;
PDefRecord def;
PCallRecord pCrec;
n = (Int)args[0];
def = Locate_FDef( EXEC_ARGS n, FALSE );
if ( !def )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
/* check call stack */
if ( CUR.callTop >= CUR.callSize )
{
CUR.error = TT_Err_Stack_Overflow;
return;
}
pCrec = CUR.callStack + CUR.callTop;
pCrec->Caller_Range = CUR.curRange;
pCrec->Caller_IP = CUR.IP + 1;
pCrec->Cur_Count = 1;
pCrec->Cur_Restart = def->Start;
CUR.callTop++;
INS_Goto_CodeRange( def->Range,
def->Start );
CUR.step_ins = FALSE;
}
/*******************************************/
/* LOOPCALL[]: LOOP and CALL function */
/* CodeRange : $2A */
/* Stack : uint32? Eint16? --> */
static void Ins_LOOPCALL( INS_ARG )
{
Int n;
Long count;
PDefRecord def;
PCallRecord pTCR;
n = (Int)args[1];
def = Locate_FDef( EXEC_ARGS n, FALSE );
if ( !def )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
if ( CUR.callTop >= CUR.callSize )
{
CUR.error = TT_Err_Stack_Overflow;
return;
}
count = (Long)args[0];
if ( count <= 0 )
return;
pTCR = &CUR.callStack[CUR.callTop];
pTCR->Caller_Range = CUR.curRange;
pTCR->Caller_IP = CUR.IP + 1;
pTCR->Cur_Count = count;
pTCR->Cur_Restart = def->Start;
CUR.callTop++;
INS_Goto_CodeRange( def->Range,
def->Start );
CUR.step_ins = FALSE;
}
/*******************************************/
/* IDEF[] : Instruction DEFinition */
/* CodeRange : $89 */
/* Stack : Eint8 --> */
static void Ins_IDEF( INS_ARG )
{
Byte opcode;
PDefRecord def;
PDefRecord limit;
opcode = (Byte)args[0];
/* First of all, look for the same instruction in our table */
def = CUR.IDefs;
limit = def + CUR.numIDefs;
for ( ; def < limit; def++ )
if ( def->Opc == opcode )
break;
if ( def == limit )
{
/* check that there is enough room for a new instruction */
if ( CUR.numIDefs >= CUR.maxIDefs )
{
/* XXX Bad error code. See FDEF[]. */
CUR.error = TT_Err_Too_Many_Ins;
return;
}
CUR.numIDefs++;
}
def->Opc = opcode;
def->Start = CUR.IP + 1;
def->Range = CUR.curRange;
def->Active = TRUE;
if ( opcode > CUR.maxIns )
CUR.maxIns = opcode;
/* Now skip the whole function definition */
/* We don't allow nested IDEFs & FDEFs. */
while ( SKIP_Code() == SUCCESS_OK )
{
switch ( CUR.opcode )
{
case 0x89: /* IDEF */
case 0x2c: /* FDEF */
CUR.error = TT_Err_Nested_DEFS;
return;
case 0x2d: /* ENDF */
return;
}
}
}
/****************************************************************/
/* */
/* PUSHING DATA ONTO THE INTERPRETER STACK */
/* */
/* Instructions appear in the specs' order. */
/* */
/****************************************************************/
/*******************************************/
/* NPUSHB[] : PUSH N Bytes */
/* CodeRange : $40 */
/* Stack : --> uint32... */
static void Ins_NPUSHB( INS_ARG )
{
UShort L, K;
L = (UShort)CUR.code[CUR.IP + 1];
if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
{
CUR.error = TT_Err_Stack_Overflow;
return;
}
for ( K = 1; K <= L; K++ )
args[K - 1] = CUR.code[CUR.IP + K + 1];
CUR.new_top += L;
}
/*******************************************/
/* NPUSHW[] : PUSH N Words */
/* CodeRange : $41 */
/* Stack : --> int32... */
static void Ins_NPUSHW( INS_ARG )
{
UShort L, K;
L = (UShort)CUR.code[CUR.IP + 1];
if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
{
CUR.error = TT_Err_Stack_Overflow;
return;
}
CUR.IP += 2;
for ( K = 0; K < L; K++ )
args[K] = GET_ShortIns();
CUR.step_ins = FALSE;
CUR.new_top += L;
}
/*******************************************/
/* PUSHB[abc]: PUSH Bytes */
/* CodeRange : $B0-$B7 */
/* Stack : --> uint32... */
static void Ins_PUSHB( INS_ARG )
{
UShort L, K;
L = (UShort)CUR.opcode - 0xB0 + 1;
if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
{
CUR.error = TT_Err_Stack_Overflow;
return;
}
for ( K = 1; K <= L; K++ )
args[K - 1] = CUR.code[CUR.IP + K];
}
/*******************************************/
/* PUSHW[abc]: PUSH Words */
/* CodeRange : $B8-$BF */
/* Stack : --> int32... */
static void Ins_PUSHW( INS_ARG )
{
UShort L, K;
L = (UShort)CUR.opcode - 0xB8 + 1;
if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
{
CUR.error = TT_Err_Stack_Overflow;
return;
}
CUR.IP++;
for ( K = 0; K < L; K++ )
args[K] = GET_ShortIns();
CUR.step_ins = FALSE;
}
/****************************************************************/
/* */
/* MANAGING THE GRAPHICS STATE */
/* */
/* Instructions appear in the specs' order. */
/* */
/****************************************************************/
/**********************************************/
/* GC[a] : Get Coordinate projected onto */
/* CodeRange : $46-$47 */
/* Stack : uint32 --> f26.6 */
/* BULLSHIT: Measures from the original glyph must be taken */
/* along the dual projection vector! */
static void Ins_GC( INS_ARG )
{
ULong L;
TT_F26Dot6 R;
L = (ULong)args[0];
if ( BOUNDS( L, CUR.zp2.n_points ) )
{
if ( CUR.pedantic_hinting )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
else
R = 0;
}
else
{
if ( CUR.opcode & 1 )
R = CUR_Func_dualproj( CUR.zp2.org + L, NULL_Vector );
else
R = CUR_Func_project( CUR.zp2.cur + L, NULL_Vector );
}
args[0] = R;
}
/**********************************************/
/* SCFS[] : Set Coordinate From Stack */
/* CodeRange : $48 */
/* Stack : f26.6 uint32 --> */
/* */
/* Formula: */
/* */
/* OA := OA + ( value - OA.p )/( f.p ) * f */
/* */
static void Ins_SCFS( INS_ARG )
{
Long K;
UShort L;
L = (UShort)args[0];
if ( BOUNDS( L, CUR.zp2.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
K = CUR_Func_project( CUR.zp2.cur + L, NULL_Vector );
CUR_Func_move( &CUR.zp2, L, args[1] - K );
/* not part of the specs, but here for safety */
if ( CUR.GS.gep2 == 0 )
CUR.zp2.org[L] = CUR.zp2.cur[L];
}
/**********************************************/
/* MD[a] : Measure Distance */
/* CodeRange : $49-$4A */
/* Stack : uint32 uint32 --> f26.6 */
/* BULLSHIT: Measure taken in the original glyph must be along */
/* the dual projection vector. */
/* Second BULLSHIT: Flag attributes are inverted! */
/* 0 => measure distance in original outline */
/* 1 => measure distance in grid-fitted outline */
/* Third one !! : zp0 - zp1, and not "zp2 - zp1" !!! */
/* */
static void Ins_MD( INS_ARG )
{
UShort K, L;
TT_F26Dot6 D;
K = (UShort)args[1];
L = (UShort)args[0];
if( BOUNDS( L, CUR.zp0.n_points ) ||
BOUNDS( K, CUR.zp1.n_points ) )
{
if ( CUR.pedantic_hinting )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
else
D = 0;
}
else
{
if ( CUR.opcode & 1 )
D = CUR_Func_project( CUR.zp0.cur + L, CUR.zp1.cur + K );
else
D = CUR_Func_dualproj( CUR.zp0.org + L, CUR.zp1.org + K );
}
args[0] = D;
}
/*******************************************/
/* SDPVTL[a] : Set Dual PVector to Line */
/* CodeRange : $86-$87 */
/* Stack : uint32 uint32 --> */
static void Ins_SDPVTL( INS_ARG )
{
Long A, B, C;
UShort p1, p2; /* was Int in pas type ERROR */
p1 = (UShort)args[1];
p2 = (UShort)args[0];
if ( BOUNDS( p2, CUR.zp1.n_points ) ||
BOUNDS( p1, CUR.zp2.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
{
TT_Vector* v1 = CUR.zp1.org + p2;
TT_Vector* v2 = CUR.zp2.org + p1;
A = v1->x - v2->x;
B = v1->y - v2->y;
}
if ( (CUR.opcode & 1) != 0 )
{
C = B; /* CounterClockwise rotation */
B = A;
A = -C;
}
NORMalize( A, B, &CUR.GS.dualVector );
{
TT_Vector* v1 = CUR.zp1.cur + p2;
TT_Vector* v2 = CUR.zp2.cur + p1;
A = v1->x - v2->x;
B = v1->y - v2->y;
}
if ( (CUR.opcode & 1) != 0 )
{
C = B; /* CounterClockwise rotation */
B = A;
A = -C;
}
NORMalize( A, B, &CUR.GS.projVector );
COMPUTE_Funcs();
}
/*******************************************/
/* SZP0[] : Set Zone Pointer 0 */
/* CodeRange : $13 */
/* Stack : uint32 --> */
static void Ins_SZP0( INS_ARG )
{
switch ( (Int)args[0] )
{
case 0:
CUR.zp0 = CUR.twilight;
break;
case 1:
CUR.zp0 = CUR.pts;
break;
default:
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
CUR.GS.gep0 = (UShort)args[0];
}
/*******************************************/
/* SZP1[] : Set Zone Pointer 1 */
/* CodeRange : $14 */
/* Stack : uint32 --> */
static void Ins_SZP1( INS_ARG )
{
switch ( (Int)args[0] )
{
case 0:
CUR.zp1 = CUR.twilight;
break;
case 1:
CUR.zp1 = CUR.pts;
break;
default:
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
CUR.GS.gep1 = (UShort)args[0];
}
/*******************************************/
/* SZP2[] : Set Zone Pointer 2 */
/* CodeRange : $15 */
/* Stack : uint32 --> */
static void Ins_SZP2( INS_ARG )
{
switch ( (Int)args[0] )
{
case 0:
CUR.zp2 = CUR.twilight;
break;
case 1:
CUR.zp2 = CUR.pts;
break;
default:
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
CUR.GS.gep2 = (UShort)args[0];
}
/*******************************************/
/* SZPS[] : Set Zone Pointers */
/* CodeRange : $16 */
/* Stack : uint32 --> */
static void Ins_SZPS( INS_ARG )
{
switch ( (Int)args[0] )
{
case 0:
CUR.zp0 = CUR.twilight;
break;
case 1:
CUR.zp0 = CUR.pts;
break;
default:
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
CUR.zp1 = CUR.zp0;
CUR.zp2 = CUR.zp0;
CUR.GS.gep0 = (UShort)args[0];
CUR.GS.gep1 = (UShort)args[0];
CUR.GS.gep2 = (UShort)args[0];
}
/*******************************************/
/* INSTCTRL[]: INSTruction ConTRol */
/* CodeRange : $8e */
/* Stack : int32 int32 --> */
static void Ins_INSTCTRL( INS_ARG )
{
Long K, L;
K = args[1];
L = args[0];
if ( K < 1 || K > 2 )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
if ( L != 0 )
L = K;
CUR.GS.instruct_control =
(Byte)( CUR.GS.instruct_control & ~(Byte)K ) | (Byte)L;
}
/*******************************************/
/* SCANCTRL[]: SCAN ConTRol */
/* CodeRange : $85 */
/* Stack : uint32? --> */
static void Ins_SCANCTRL( INS_ARG )
{
Int A;
/* Get Threshold */
A = (Int)(args[0] & 0xFF);
if ( A == 0xFF )
{
CUR.GS.scan_control = TRUE;
return;
}
else if ( A == 0 )
{
CUR.GS.scan_control = FALSE;
return;
}
A *= 64;
if ( (args[0] & 0x100) != 0 && CUR.metrics.pointSize <= A )
CUR.GS.scan_control = TRUE;
if ( (args[0] & 0x200) != 0 && CUR.metrics.rotated )
CUR.GS.scan_control = TRUE;
if ( (args[0] & 0x400) != 0 && CUR.metrics.stretched )
CUR.GS.scan_control = TRUE;
if ( (args[0] & 0x800) != 0 && CUR.metrics.pointSize > A )
CUR.GS.scan_control = FALSE;
if ( (args[0] & 0x1000) != 0 && CUR.metrics.rotated )
CUR.GS.scan_control = FALSE;
if ( (args[0] & 0x2000) != 0 && CUR.metrics.stretched )
CUR.GS.scan_control = FALSE;
}
/*******************************************/
/* SCANTYPE[]: SCAN TYPE */
/* CodeRange : $8D */
/* Stack : uint32? --> */
static void Ins_SCANTYPE( INS_ARG )
{
/* For compatibility with future enhancements, */
/* we must ignore new modes */
if ( args[0] >= 0 && args[0] <= 5 )
{
if ( args[0] == 3 )
args[0] = 2;
CUR.GS.scan_type = (Int)args[0];
}
}
/****************************************************************/
/* */
/* MANAGING OUTLINES */
/* */
/* Instructions appear in the specs' order. */
/* */
/****************************************************************/
/**********************************************/
/* FLIPPT[] : FLIP PoinT */
/* CodeRange : $80 */
/* Stack : uint32... --> */
static void Ins_FLIPPT( INS_ARG )
{
UShort point;
if ( CUR.top < CUR.GS.loop )
{
CUR.error = TT_Err_Too_Few_Arguments;
return;
}
while ( CUR.GS.loop > 0 )
{
CUR.args--;
point = (UShort)CUR.stack[CUR.args];
if ( BOUNDS( point, CUR.pts.n_points ) )
{
if ( CUR.pedantic_hinting )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
}
else
CUR.pts.touch[point] ^= TT_Flag_On_Curve;
CUR.GS.loop--;
}
CUR.GS.loop = 1;
CUR.new_top = CUR.args;
}
/**********************************************/
/* FLIPRGON[]: FLIP RanGe ON */
/* CodeRange : $81 */
/* Stack : uint32 uint32 --> */
/* (but UShorts are sufficient) */
static void Ins_FLIPRGON( INS_ARG )
{
UShort I, K, L;
K = (UShort)args[1];
L = (UShort)args[0];
if ( BOUNDS( K, CUR.pts.n_points ) ||
BOUNDS( L, CUR.pts.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
for ( I = L; I <= K; I++ )
CUR.pts.touch[I] |= TT_Flag_On_Curve;
}
/**********************************************/
/* FLIPRGOFF : FLIP RanGe OFF */
/* CodeRange : $82 */
/* Stack : uint32 uint32 --> */
/* (but UShorts are sufficient) */
static void Ins_FLIPRGOFF( INS_ARG )
{
UShort I, K, L;
K = (UShort)args[1];
L = (UShort)args[0];
if ( BOUNDS( K, CUR.pts.n_points ) ||
BOUNDS( L, CUR.pts.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
for ( I = L; I <= K; I++ )
CUR.pts.touch[I] &= ~TT_Flag_On_Curve;
}
static Bool Compute_Point_Displacement( EXEC_OPS
PCoordinates x,
PCoordinates y,
PGlyph_Zone zone,
UShort* refp )
{
TGlyph_Zone zp;
UShort p;
TT_F26Dot6 d;
if ( CUR.opcode & 1 )
{
zp = CUR.zp0;
p = CUR.GS.rp1;
}
else
{
zp = CUR.zp1;
p = CUR.GS.rp2;
}
if ( BOUNDS( p, zp.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Displacement;
return FAILURE;
}
*zone = zp;
*refp = p;
d = CUR_Func_project( zp.cur + p, zp.org + p );
*x = TT_MulDiv(d, (Long)CUR.GS.freeVector.x * 0x10000L, CUR.F_dot_P );
*y = TT_MulDiv(d, (Long)CUR.GS.freeVector.y * 0x10000L, CUR.F_dot_P );
return SUCCESS_OK;
}
static void Move_Zp2_Point( EXEC_OPS
UShort point,
TT_F26Dot6 dx,
TT_F26Dot6 dy,
Bool touch )
{
if ( CUR.GS.freeVector.x != 0 )
{
CUR.zp2.cur[point].x += dx;
if ( touch )
CUR.zp2.touch[point] |= TT_Flag_Touched_X;
}
if ( CUR.GS.freeVector.y != 0 )
{
CUR.zp2.cur[point].y += dy;
if ( touch )
CUR.zp2.touch[point] |= TT_Flag_Touched_Y;
}
}
/**********************************************/
/* SHP[a] : SHift Point by the last point */
/* CodeRange : $32-33 */
/* Stack : uint32... --> */
static void Ins_SHP( INS_ARG )
{
TGlyph_Zone zp;
UShort refp;
TT_F26Dot6 dx,
dy;
UShort point;
if ( CUR.top < CUR.GS.loop )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
return;
while ( CUR.GS.loop > 0 )
{
CUR.args--;
point = (UShort)CUR.stack[CUR.args];
if ( BOUNDS( point, CUR.zp2.n_points ) )
{
if ( CUR.pedantic_hinting )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
}
else
/* UNDOCUMENTED! SHP touches the points */
MOVE_Zp2_Point( point, dx, dy, TRUE );
CUR.GS.loop--;
}
CUR.GS.loop = 1;
CUR.new_top = CUR.args;
}
/**********************************************/
/* SHC[a] : SHift Contour */
/* CodeRange : $34-35 */
/* Stack : uint32 --> */
static void Ins_SHC( INS_ARG )
{
TGlyph_Zone zp;
UShort refp;
TT_F26Dot6 dx,
dy;
Short contour;
UShort first_point, last_point, i;
contour = (UShort)args[0];
if ( BOUNDS( contour, CUR.pts.n_contours ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
return;
if ( contour == 0 )
first_point = 0;
else
first_point = CUR.pts.contours[contour - 1] + 1;
last_point = CUR.pts.contours[contour];
/* XXX: this is probably wrong... at least it prevents memory */
/* corruption when zp2 is the twilight zone */
if ( last_point > CUR.zp2.n_points )
{
if ( CUR.zp2.n_points > 0 )
last_point = CUR.zp2.n_points - 1;
else
last_point = 0;
}
/* UNDOCUMENTED! SHC doesn't touch the points */
for ( i = first_point; i <= last_point; i++ )
{
if ( zp.cur != CUR.zp2.cur || refp != i )
MOVE_Zp2_Point( i, dx, dy, FALSE );
}
}
/**********************************************/
/* SHZ[a] : SHift Zone */
/* CodeRange : $36-37 */
/* Stack : uint32 --> */
static void Ins_SHZ( INS_ARG )
{
TGlyph_Zone zp;
UShort refp;
TT_F26Dot6 dx,
dy;
UShort last_point, i;
if ( BOUNDS( args[0], 2 ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
return;
if ( CUR.zp2.n_points > 0 )
last_point = CUR.zp2.n_points - 1;
else
last_point = 0;
/* UNDOCUMENTED! SHZ doesn't touch the points */
for ( i = 0; i <= last_point; i++ )
{
if ( zp.cur != CUR.zp2.cur || refp != i )
MOVE_Zp2_Point( i, dx, dy, FALSE );
}
}
/**********************************************/
/* SHPIX[] : SHift points by a PIXel amount */
/* CodeRange : $38 */
/* Stack : f26.6 uint32... --> */
static void Ins_SHPIX( INS_ARG )
{
TT_F26Dot6 dx, dy;
UShort point;
if ( CUR.top < CUR.GS.loop + 1 )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
dx = TT_MulDiv( args[0],
(Long)CUR.GS.freeVector.x,
0x4000 );
dy = TT_MulDiv( args[0],
(Long)CUR.GS.freeVector.y,
0x4000 );
while ( CUR.GS.loop > 0 )
{
CUR.args--;
point = (UShort)CUR.stack[CUR.args];
if ( BOUNDS( point, CUR.zp2.n_points ) )
{
if ( CUR.pedantic_hinting )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
}
else
MOVE_Zp2_Point( point, dx, dy, TRUE );
CUR.GS.loop--;
}
CUR.GS.loop = 1;
CUR.new_top = CUR.args;
}
/**********************************************/
/* MSIRP[a] : Move Stack Indirect Relative */
/* CodeRange : $3A-$3B */
/* Stack : f26.6 uint32 --> */
static void Ins_MSIRP( INS_ARG )
{
UShort point;
TT_F26Dot6 distance;
point = (UShort)args[0];
if ( BOUNDS( point, CUR.zp1.n_points ) ||
BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
/* XXX: UNDOCUMENTED! behaviour */
if ( CUR.GS.gep0 == 0 ) /* if in twilight zone */
{
CUR.zp1.org[point] = CUR.zp0.org[CUR.GS.rp0];
CUR.zp1.cur[point] = CUR.zp1.org[point];
}
distance = CUR_Func_project( CUR.zp1.cur + point,
CUR.zp0.cur + CUR.GS.rp0 );
CUR_Func_move( &CUR.zp1, point, args[1] - distance );
CUR.GS.rp1 = CUR.GS.rp0;
CUR.GS.rp2 = point;
if ( (CUR.opcode & 1) != 0 )
CUR.GS.rp0 = point;
}
/**********************************************/
/* MDAP[a] : Move Direct Absolute Point */
/* CodeRange : $2E-$2F */
/* Stack : uint32 --> */
static void Ins_MDAP( INS_ARG )
{
UShort point;
TT_F26Dot6 cur_dist,
distance;
point = (UShort)args[0];
if ( BOUNDS( point, CUR.zp0.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
/* XXX: Is there some undocumented feature while in the */
/* twilight zone? ? */
if ( (CUR.opcode & 1) != 0 )
{
cur_dist = CUR_Func_project( CUR.zp0.cur + point, NULL_Vector );
distance = CUR_Func_round( cur_dist,
CUR.metrics.compensations[0] ) - cur_dist;
}
else
distance = 0;
CUR_Func_move( &CUR.zp0, point, distance );
CUR.GS.rp0 = point;
CUR.GS.rp1 = point;
}
/**********************************************/
/* MIAP[a] : Move Indirect Absolute Point */
/* CodeRange : $3E-$3F */
/* Stack : uint32 uint32 --> */
static void Ins_MIAP( INS_ARG )
{
ULong cvtEntry;
UShort point;
TT_F26Dot6 distance,
org_dist;
cvtEntry = (ULong)args[1];
point = (UShort)args[0];
if ( BOUNDS( point, CUR.zp0.n_points ) ||
BOUNDS( cvtEntry, CUR.cvtSize ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
/* UNDOCUMENTED! */
/* */
/* The behaviour of an MIAP instruction is quite */
/* different when used in the twilight zone. */
/* */
/* First, no control value cutin 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 "illegal" according to the */
/* specs :) */
/* */
/* We implement it with a special sequence for the */
/* twilight zone. This is a bad hack, but it seems */
/* to work. */
distance = CUR_Func_read_cvt( cvtEntry );
if ( CUR.GS.gep0 == 0 ) /* If in twilight zone */
{
CUR.zp0.org[point].x = TT_MulDiv( CUR.GS.freeVector.x,
distance, 0x4000L );
CUR.zp0.org[point].y = TT_MulDiv( CUR.GS.freeVector.y,
distance, 0x4000L );
CUR.zp0.cur[point] = CUR.zp0.org[point];
}
org_dist = CUR_Func_project( CUR.zp0.cur + point, NULL_Vector );
if ( (CUR.opcode & 1) != 0 ) /* rounding and control cutin flag */
{
if ( ABS( distance - org_dist ) > CUR.GS.control_value_cutin )
distance = org_dist;
distance = CUR_Func_round( distance, CUR.metrics.compensations[0] );
}
CUR_Func_move( &CUR.zp0, point, distance - org_dist );
CUR.GS.rp0 = point;
CUR.GS.rp1 = point;
}
/**********************************************/
/* MDRP[abcde] : Move Direct Relative Point */
/* CodeRange : $C0-$DF */
/* Stack : uint32 --> */
static void Ins_MDRP( INS_ARG )
{
UShort point;
TT_F26Dot6 org_dist, distance;
point = (UShort)args[0];
if ( BOUNDS( point, CUR.zp1.n_points ) ||
BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
/* XXX: Is there some undocumented feature while in the */
/* twilight zone? */
org_dist = CUR_Func_dualproj( CUR.zp1.org + point,
CUR.zp0.org + CUR.GS.rp0 );
/* single width cutin test */
if ( ABS( org_dist ) < CUR.GS.single_width_cutin )
{
if ( org_dist >= 0 )
org_dist = CUR.GS.single_width_value;
else
org_dist = -CUR.GS.single_width_value;
}
/* round flag */
if ( (CUR.opcode & 4) != 0 )
distance = CUR_Func_round( org_dist,
CUR.metrics.compensations[CUR.opcode & 3] );
else
distance = Round_None( EXEC_ARGS
org_dist,
CUR.metrics.compensations[CUR.opcode & 3] );
/* minimum distance flag */
if ( (CUR.opcode & 8) != 0 )
{
if ( org_dist >= 0 )
{
if ( distance < CUR.GS.minimum_distance )
distance = CUR.GS.minimum_distance;
}
else
{
if ( distance > -CUR.GS.minimum_distance )
distance = -CUR.GS.minimum_distance;
}
}
/* now move the point */
org_dist = CUR_Func_project( CUR.zp1.cur + point,
CUR.zp0.cur + CUR.GS.rp0 );
CUR_Func_move( &CUR.zp1, point, distance - org_dist );
CUR.GS.rp1 = CUR.GS.rp0;
CUR.GS.rp2 = point;
if ( (CUR.opcode & 16) != 0 )
CUR.GS.rp0 = point;
}
/**********************************************/
/* MIRP[abcde] : Move Indirect Relative Point */
/* CodeRange : $E0-$FF */
/* Stack : int32? uint32 --> */
static void Ins_MIRP( INS_ARG )
{
UShort point;
ULong cvtEntry;
TT_F26Dot6 cvt_dist,
distance,
cur_dist,
org_dist;
point = (UShort)args[0];
cvtEntry = (ULong)(args[1] + 1);
/* XXX: UNDOCUMENTED! cvt[-1] = 0 always */
if ( BOUNDS( point, CUR.zp1.n_points ) ||
BOUNDS( cvtEntry, CUR.cvtSize + 1 ) ||
BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
if ( !cvtEntry )
cvt_dist = 0;
else
cvt_dist = CUR_Func_read_cvt( cvtEntry - 1 );
/* single width test */
if ( ABS( cvt_dist ) < CUR.GS.single_width_cutin )
{
if ( cvt_dist >= 0 )
cvt_dist = CUR.GS.single_width_value;
else
cvt_dist = -CUR.GS.single_width_value;
}
/* XXX : UNDOCUMENTED! -- twilight zone */
if ( CUR.GS.gep1 == 0 )
{
CUR.zp1.org[point].x = CUR.zp0.org[CUR.GS.rp0].x +
TT_MulDiv( cvt_dist,
CUR.GS.freeVector.x,
0x4000 );
CUR.zp1.org[point].y = CUR.zp0.org[CUR.GS.rp0].y +
TT_MulDiv( cvt_dist,
CUR.GS.freeVector.y,
0x4000 );
CUR.zp1.cur[point] = CUR.zp1.org[point];
}
org_dist = CUR_Func_dualproj( CUR.zp1.org + point,
CUR.zp0.org + CUR.GS.rp0 );
cur_dist = CUR_Func_project( CUR.zp1.cur + point,
CUR.zp0.cur + CUR.GS.rp0 );
/* auto-flip test */
if ( CUR.GS.auto_flip )
{
if ( (org_dist ^ cvt_dist) < 0 )
cvt_dist = -cvt_dist;
}
/* control value cutin and round */
if ( (CUR.opcode & 4) != 0 )
{
/* XXX: UNDOCUMENTED! Only perform cut-in test when both points */
/* refer to the same zone. */
if ( CUR.GS.gep0 == CUR.GS.gep1 )
if ( ABS( cvt_dist - org_dist ) >= CUR.GS.control_value_cutin )
cvt_dist = org_dist;
distance = CUR_Func_round( cvt_dist,
CUR.metrics.compensations[CUR.opcode & 3] );
}
else
distance = Round_None( EXEC_ARGS
cvt_dist,
CUR.metrics.compensations[CUR.opcode & 3] );
/* minimum distance test */
if ( (CUR.opcode & 8) != 0 )
{
if ( org_dist >= 0 )
{
if ( distance < CUR.GS.minimum_distance )
distance = CUR.GS.minimum_distance;
}
else
{
if ( distance > -CUR.GS.minimum_distance )
distance = -CUR.GS.minimum_distance;
}
}
CUR_Func_move( &CUR.zp1, point, distance - cur_dist );
CUR.GS.rp1 = CUR.GS.rp0;
if ( (CUR.opcode & 16) != 0 )
CUR.GS.rp0 = point;
/* UNDOCUMENTED! */
CUR.GS.rp2 = point;
}
/**********************************************/
/* ALIGNRP[] : ALIGN Relative Point */
/* CodeRange : $3C */
/* Stack : uint32 uint32... --> */
static void Ins_ALIGNRP( INS_ARG )
{
UShort point;
TT_F26Dot6 distance;
if ( CUR.top < CUR.GS.loop ||
BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
while ( CUR.GS.loop > 0 )
{
CUR.args--;
point = (UShort)CUR.stack[CUR.args];
if ( BOUNDS( point, CUR.zp1.n_points ) )
{
if ( CUR.pedantic_hinting )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
}
else
{
distance = CUR_Func_project( CUR.zp1.cur + point,
CUR.zp0.cur + CUR.GS.rp0 );
CUR_Func_move( &CUR.zp1, point, -distance );
}
CUR.GS.loop--;
}
CUR.GS.loop = 1;
CUR.new_top = CUR.args;
}
/**********************************************/
/* ISECT[] : moves point to InterSECTion */
/* CodeRange : $0F */
/* Stack : 5 * uint32 --> */
static void Ins_ISECT( INS_ARG )
{
UShort point,
a0, a1,
b0, b1;
TT_F26Dot6 discriminant;
TT_F26Dot6 dx, dy,
dax, day,
dbx, dby;
TT_F26Dot6 val;
TT_Vector R;
point = (UShort)args[0];
a0 = (UShort)args[1];
a1 = (UShort)args[2];
b0 = (UShort)args[3];
b1 = (UShort)args[4];
if ( BOUNDS( b0, CUR.zp0.n_points ) ||
BOUNDS( b1, CUR.zp0.n_points ) ||
BOUNDS( a0, CUR.zp1.n_points ) ||
BOUNDS( a1, CUR.zp1.n_points ) ||
BOUNDS( point, CUR.zp2.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
dbx = CUR.zp0.cur[b1].x - CUR.zp0.cur[b0].x;
dby = CUR.zp0.cur[b1].y - CUR.zp0.cur[b0].y;
dax = CUR.zp1.cur[a1].x - CUR.zp1.cur[a0].x;
day = CUR.zp1.cur[a1].y - CUR.zp1.cur[a0].y;
dx = CUR.zp0.cur[b0].x - CUR.zp1.cur[a0].x;
dy = CUR.zp0.cur[b0].y - CUR.zp1.cur[a0].y;
CUR.zp2.touch[point] |= TT_Flag_Touched_Both;
discriminant = TT_MulDiv( dax, -dby, 0x40L ) +
TT_MulDiv( day, dbx, 0x40L );
if ( ABS( discriminant ) >= 0x40 )
{
val = TT_MulDiv( dx, -dby, 0x40L ) + TT_MulDiv( dy, dbx, 0x40L );
R.x = TT_MulDiv( val, dax, discriminant );
R.y = TT_MulDiv( val, day, discriminant );
CUR.zp2.cur[point].x = CUR.zp1.cur[a0].x + R.x;
CUR.zp2.cur[point].y = CUR.zp1.cur[a0].y + R.y;
}
else
{
/* else, take the middle of the middles of A and B */
CUR.zp2.cur[point].x = ( CUR.zp1.cur[a0].x +
CUR.zp1.cur[a1].x +
CUR.zp0.cur[b0].x +
CUR.zp0.cur[b1].x ) / 4;
CUR.zp2.cur[point].y = ( CUR.zp1.cur[a0].y +
CUR.zp1.cur[a1].y +
CUR.zp0.cur[b0].y +
CUR.zp0.cur[b1].y ) / 4;
}
}
/**********************************************/
/* ALIGNPTS[] : ALIGN PoinTS */
/* CodeRange : $27 */
/* Stack : uint32 uint32 --> */
static void Ins_ALIGNPTS( INS_ARG )
{
UShort p1, p2;
TT_F26Dot6 distance;
p1 = (UShort)args[0];
p2 = (UShort)args[1];
if ( BOUNDS( args[0], CUR.zp1.n_points ) ||
BOUNDS( args[1], CUR.zp0.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
distance = CUR_Func_project( CUR.zp0.cur + p2,
CUR.zp1.cur + p1 ) / 2;
CUR_Func_move( &CUR.zp1, p1, distance );
CUR_Func_move( &CUR.zp0, p2, -distance );
}
/**********************************************/
/* IP[] : Interpolate Point */
/* CodeRange : $39 */
/* Stack : uint32... --> */
static void Ins_IP( INS_ARG )
{
TT_F26Dot6 org_a, org_b, org_x,
cur_a, cur_b, cur_x,
distance;
UShort point;
if ( CUR.top < CUR.GS.loop )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
/* 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( CUR.GS.rp1, CUR.zp0.n_points ) ||
BOUNDS( CUR.GS.rp2, CUR.zp1.n_points ) )
{
org_a = cur_a = 0;
org_b = cur_b = 0;
}
else
{
org_a = CUR_Func_dualproj( CUR.zp0.org + CUR.GS.rp1, NULL_Vector );
org_b = CUR_Func_dualproj( CUR.zp1.org + CUR.GS.rp2, NULL_Vector );
cur_a = CUR_Func_project( CUR.zp0.cur + CUR.GS.rp1, NULL_Vector );
cur_b = CUR_Func_project( CUR.zp1.cur + CUR.GS.rp2, NULL_Vector );
}
while ( CUR.GS.loop > 0 )
{
CUR.args--;
point = (UShort)CUR.stack[CUR.args];
if ( BOUNDS( point, CUR.zp2.n_points ) )
{
if ( CUR.pedantic_hinting )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
}
else
{
org_x = CUR_Func_dualproj( CUR.zp2.org + point, NULL_Vector );
cur_x = CUR_Func_project ( CUR.zp2.cur + point, NULL_Vector );
if ( ( org_a <= org_b && org_x <= org_a ) ||
( org_a > org_b && org_x >= org_a ) )
distance = ( cur_a - org_a ) + ( org_x - cur_x );
else if ( ( org_a <= org_b && org_x >= org_b ) ||
( org_a > org_b && org_x < org_b ) )
distance = ( cur_b - org_b ) + ( org_x - cur_x );
else
/* note: it seems that rounding this value isn't a good */
/* idea (cf. width of capital 'S' in Times) */
distance = TT_MulDiv( cur_b - cur_a,
org_x - org_a,
org_b - org_a ) + ( cur_a - cur_x );
CUR_Func_move( &CUR.zp2, point, distance );
}
CUR.GS.loop--;
}
CUR.GS.loop = 1;
CUR.new_top = CUR.args;
}
/**********************************************/
/* UTP[a] : UnTouch Point */
/* CodeRange : $29 */
/* Stack : uint32 --> */
static void Ins_UTP( INS_ARG )
{
UShort point;
Byte mask;
point = (UShort)args[0];
if ( BOUNDS( point, CUR.zp0.n_points ) )
{
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
return;
}
mask = 0xFF;
if ( CUR.GS.freeVector.x != 0 )
mask &= ~TT_Flag_Touched_X;
if ( CUR.GS.freeVector.y != 0 )
mask &= ~TT_Flag_Touched_Y;
CUR.zp0.touch[point] &= mask;
}
/* Local variables for Ins_IUP: */
struct LOC_Ins_IUP
{
TT_Vector* orgs; /* original and current coordinate */
TT_Vector* curs; /* arrays */
};
static void Shift( UShort p1,
UShort p2,
UShort p,
struct LOC_Ins_IUP* LINK )
{
UShort i;
TT_F26Dot6 x;
x = LINK->curs[p].x - LINK->orgs[p].x;
for ( i = p1; i < p; i++ )
LINK->curs[i].x += x;
for ( i = p + 1; i <= p2; i++ )
LINK->curs[i].x += x;
}
static void Interp( UShort p1,
UShort p2,
UShort ref1,
UShort ref2,
struct LOC_Ins_IUP* LINK )
{
UShort i;
TT_F26Dot6 x, x1, x2, d1, d2;
if ( p1 > p2 )
return;
x1 = LINK->orgs[ref1].x;
d1 = LINK->curs[ref1].x - LINK->orgs[ref1].x;
x2 = LINK->orgs[ref2].x;
d2 = LINK->curs[ref2].x - LINK->orgs[ref2].x;
if ( x1 == x2 )
{
for ( i = p1; i <= p2; i++ )
{
x = LINK->orgs[i].x;
if ( x <= x1 )
x += d1;
else
x += d2;
LINK->curs[i].x = x;
}
return;
}
if ( x1 < x2 )
{
for ( i = p1; i <= p2; i++ )
{
x = LINK->orgs[i].x;
if ( x <= x1 )
x += d1;
else
{
if ( x >= x2 )
x += d2;
else
x = LINK->curs[ref1].x +
TT_MulDiv( x - x1,
LINK->curs[ref2].x - LINK->curs[ref1].x,
x2 - x1 );
}
LINK->curs[i].x = x;
}
return;
}
/* x2 < x1 */
for ( i = p1; i <= p2; i++ )
{
x = LINK->orgs[i].x;
if ( x <= x2 )
x += d2;
else
{
if ( x >= x1 )
x += d1;
else
x = LINK->curs[ref1].x +
TT_MulDiv( x - x1,
LINK->curs[ref2].x - LINK->curs[ref1].x,
x2 - x1 );
}
LINK->curs[i].x = x;
}
}
/**********************************************/
/* IUP[a] : Interpolate Untouched Points */
/* CodeRange : $30-$31 */
/* Stack : --> */
static void Ins_IUP( INS_ARG )
{
struct LOC_Ins_IUP V;
Byte mask;
UShort first_point; /* first point of contour */
UShort end_point; /* end point (last+1) of contour */
UShort first_touched; /* first touched point in contour */
UShort cur_touched; /* current touched point in contour */
UShort point; /* current point */
Short contour; /* current contour */
if ( CUR.opcode & 1 )
{
mask = TT_Flag_Touched_X;
V.orgs = CUR.pts.org;
V.curs = CUR.pts.cur;
}
else
{
mask = TT_Flag_Touched_Y;
V.orgs = (TT_Vector*)( ((TT_F26Dot6*)CUR.pts.org) + 1 );
V.curs = (TT_Vector*)( ((TT_F26Dot6*)CUR.pts.cur) + 1 );
}
contour = 0;
point = 0;
do
{
end_point = CUR.pts.contours[contour];
first_point = point;
while ( point <= end_point && (CUR.pts.touch[point] & mask) == 0 )
point++;
if ( point <= end_point )
{
first_touched = point;
cur_touched = point;
point++;
while ( point <= end_point )
{
if ( (CUR.pts.touch[point] & mask) != 0 )
{
if ( point > 0 )
Interp( cur_touched + 1,
point - 1,
cur_touched,
point,
&V );
cur_touched = point;
}
point++;
}
if ( cur_touched == first_touched )
Shift( first_point, end_point, cur_touched, &V );
else
{
Interp( cur_touched + 1,
end_point,
cur_touched,
first_touched,
&V );
if ( first_touched > 0 )
Interp( first_point,
first_touched - 1,
cur_touched,
first_touched,
&V );
}
}
contour++;
} while ( contour < CUR.pts.n_contours );
}
/**********************************************/
/* DELTAPn[] : DELTA Exceptions P1, P2, P3 */
/* CodeRange : $5D,$71,$72 */
/* Stack : uint32 (2 * uint32)... --> */
static void Ins_DELTAP( INS_ARG )
{
ULong nump, k;
UShort A;
ULong C;
Long B;
nump = (ULong)args[0]; /* some points theoretically may occur more
than once, thus UShort isn't enough */
for ( k = 1; k <= nump; k++ )
{
if ( CUR.args < 2 )
{
CUR.error = TT_Err_Too_Few_Arguments;
return;
}
CUR.args -= 2;
A = (UShort)CUR.stack[CUR.args + 1];
B = CUR.stack[CUR.args];
/* XXX : because some popular fonts contain some invalid DeltaP */
/* instructions, we simply ignore them when the stacked */
/* point reference is off limit, rather than returning an */
/* error. As a delta instruction doesn't change a glyph */
/* in great ways, this shouldn't be a problem.. */
if ( !BOUNDS( A, CUR.zp0.n_points ) )
{
C = ((ULong)B & 0xF0) >> 4;
switch ( CUR.opcode )
{
case 0x5d:
break;
case 0x71:
C += 16;
break;
case 0x72:
C += 32;
break;
}
C += CUR.GS.delta_base;
if ( CURRENT_Ppem() == (Long)C )
{
B = ((ULong)B & 0xF) - 8;
if ( B >= 0 )
B++;
B = B * 64L / (1L << CUR.GS.delta_shift);
CUR_Func_move( &CUR.zp0, A, B );
}
}
else
if ( CUR.pedantic_hinting )
CUR.error = TT_Err_Invalid_Reference;
}
CUR.new_top = CUR.args;
}
/**********************************************/
/* DELTACn[] : DELTA Exceptions C1, C2, C3 */
/* CodeRange : $73,$74,$75 */
/* Stack : uint32 (2 * uint32)... --> */
static void Ins_DELTAC( INS_ARG )
{
ULong nump, k;
ULong A, C;
Long B;
nump = (ULong)args[0];
for ( k = 1; k <= nump; k++ )
{
if ( CUR.args < 2 )
{
CUR.error = TT_Err_Too_Few_Arguments;
return;
}
CUR.args -= 2;
A = (ULong)CUR.stack[CUR.args + 1];
B = CUR.stack[CUR.args];
if ( BOUNDS( A, CUR.cvtSize ) )
{
if ( CUR.pedantic_hinting )
{
CUR.error = TT_Err_Invalid_Reference;
return;
}
}
else
{
C = ((ULong)B & 0xF0) >> 4;
switch ( CUR.opcode )
{
case 0x73:
break;
case 0x74:
C += 16;
break;
case 0x75:
C += 32;
break;
}
C += CUR.GS.delta_base;
if ( CURRENT_Ppem() == (Long)C )
{
B = ((ULong)B & 0xF) - 8;
if ( B >= 0 )
B++;
B = B * 64L / (1L << CUR.GS.delta_shift);
CUR_Func_move_cvt( A, B );
}
}
}
CUR.new_top = CUR.args;
}
/****************************************************************/
/* */
/* MISC. INSTRUCTIONS */
/* */
/****************************************************************/
/**********************************************/
/* GETINFO[] : GET INFOrmation */
/* CodeRange : $88 */
/* Stack : uint32 --> uint32 */
/* XXX According to Apple specs, bits 1 & 2 of the argument ought to be */
/* consulted before rotated / stretched info is returned */
static void Ins_GETINFO( INS_ARG )
{
Long K;
K = 0;
/* We return then Windows 3.1 version number */
/* for the font scaler */
if ( (args[0] & 1) != 0 )
K = 3;
/* Has the glyph been rotated ? */
if ( CUR.metrics.rotated )
K |= 0x80;
/* Has the glyph been stretched ? */
if ( CUR.metrics.stretched )
K |= 0x100;
args[0] = K;
}
static void Ins_UNKNOWN( INS_ARG )
{
/* look up the current instruction in our table */
PDefRecord def, limit;
def = CUR.IDefs;
limit = def + CUR.numIDefs;
for ( ; def < limit; def++ )
{
if ( def->Opc == CUR.opcode && def->Active )
{
PCallRecord pCrec;
/* implement instruction as a function call */
/* check call stack */
if ( CUR.callTop >= CUR.callSize )
{
CUR.error = TT_Err_Stack_Overflow;
return;
}
pCrec = CUR.callStack + CUR.callTop;
pCrec->Caller_Range = CUR.curRange;
pCrec->Caller_IP = CUR.IP + 1;
pCrec->Cur_Count = 1;
pCrec->Cur_Restart = def->Start;
CUR.callTop++;
INS_Goto_CodeRange( def->Range,
def->Start );
CUR.step_ins = FALSE;
return;
}
}
CUR.error = TT_Err_Invalid_Opcode;
}
#ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH
static TInstruction_Function Instruct_Dispatch[256] =
{
/* Opcodes are gathered in groups of 16. */
/* Please keep the spaces as they are. */
/* SVTCA y */ Ins_SVTCA,
/* SVTCA x */ Ins_SVTCA,
/* SPvTCA y */ Ins_SPVTCA,
/* SPvTCA x */ Ins_SPVTCA,
/* SFvTCA y */ Ins_SFVTCA,
/* SFvTCA x */ Ins_SFVTCA,
/* SPvTL // */ Ins_SPVTL,
/* SPvTL + */ Ins_SPVTL,
/* SFvTL // */ Ins_SFVTL,
/* SFvTL + */ Ins_SFVTL,
/* SPvFS */ Ins_SPVFS,
/* SFvFS */ Ins_SFVFS,
/* GPV */ Ins_GPV,
/* GFV */ Ins_GFV,
/* SFvTPv */ Ins_SFVTPV,
/* ISECT */ Ins_ISECT,
/* SRP0 */ Ins_SRP0,
/* SRP1 */ Ins_SRP1,
/* SRP2 */ Ins_SRP2,
/* SZP0 */ Ins_SZP0,
/* SZP1 */ Ins_SZP1,
/* SZP2 */ Ins_SZP2,
/* SZPS */ Ins_SZPS,
/* SLOOP */ Ins_SLOOP,
/* RTG */ Ins_RTG,
/* RTHG */ Ins_RTHG,
/* SMD */ Ins_SMD,
/* ELSE */ Ins_ELSE,
/* JMPR */ Ins_JMPR,
/* SCvTCi */ Ins_SCVTCI,
/* SSwCi */ Ins_SSWCI,
/* SSW */ Ins_SSW,
/* DUP */ Ins_DUP,
/* POP */ Ins_POP,
/* CLEAR */ Ins_CLEAR,
/* SWAP */ Ins_SWAP,
/* DEPTH */ Ins_DEPTH,
/* CINDEX */ Ins_CINDEX,
/* MINDEX */ Ins_MINDEX,
/* AlignPTS */ Ins_ALIGNPTS,
/* INS_$28 */ Ins_UNKNOWN,
/* UTP */ Ins_UTP,
/* LOOPCALL */ Ins_LOOPCALL,
/* CALL */ Ins_CALL,
/* FDEF */ Ins_FDEF,
/* ENDF */ Ins_ENDF,
/* MDAP[0] */ Ins_MDAP,
/* MDAP[1] */ Ins_MDAP,
/* IUP[0] */ Ins_IUP,
/* IUP[1] */ Ins_IUP,
/* SHP[0] */ Ins_SHP,
/* SHP[1] */ Ins_SHP,
/* SHC[0] */ Ins_SHC,
/* SHC[1] */ Ins_SHC,
/* SHZ[0] */ Ins_SHZ,
/* SHZ[1] */ Ins_SHZ,
/* SHPIX */ Ins_SHPIX,
/* IP */ Ins_IP,
/* MSIRP[0] */ Ins_MSIRP,
/* MSIRP[1] */ Ins_MSIRP,
/* AlignRP */ Ins_ALIGNRP,
/* RTDG */ Ins_RTDG,
/* MIAP[0] */ Ins_MIAP,
/* MIAP[1] */ Ins_MIAP,
/* NPushB */ Ins_NPUSHB,
/* NPushW */ Ins_NPUSHW,
/* WS */ Ins_WS,
/* RS */ Ins_RS,
/* WCvtP */ Ins_WCVTP,
/* RCvt */ Ins_RCVT,
/* GC[0] */ Ins_GC,
/* GC[1] */ Ins_GC,
/* SCFS */ Ins_SCFS,
/* MD[0] */ Ins_MD,
/* MD[1] */ Ins_MD,
/* MPPEM */ Ins_MPPEM,
/* MPS */ Ins_MPS,
/* FlipON */ Ins_FLIPON,
/* FlipOFF */ Ins_FLIPOFF,
/* DEBUG */ Ins_DEBUG,
/* LT */ Ins_LT,
/* LTEQ */ Ins_LTEQ,
/* GT */ Ins_GT,
/* GTEQ */ Ins_GTEQ,
/* EQ */ Ins_EQ,
/* NEQ */ Ins_NEQ,
/* ODD */ Ins_ODD,
/* EVEN */ Ins_EVEN,
/* IF */ Ins_IF,
/* EIF */ Ins_EIF,
/* AND */ Ins_AND,
/* OR */ Ins_OR,
/* NOT */ Ins_NOT,
/* DeltaP1 */ Ins_DELTAP,
/* SDB */ Ins_SDB,
/* SDS */ Ins_SDS,
/* ADD */ Ins_ADD,
/* SUB */ Ins_SUB,
/* DIV */ Ins_DIV,
/* MUL */ Ins_MUL,
/* ABS */ Ins_ABS,
/* NEG */ Ins_NEG,
/* FLOOR */ Ins_FLOOR,
/* CEILING */ Ins_CEILING,
/* ROUND[0] */ Ins_ROUND,
/* ROUND[1] */ Ins_ROUND,
/* ROUND[2] */ Ins_ROUND,
/* ROUND[3] */ Ins_ROUND,
/* NROUND[0] */ Ins_NROUND,
/* NROUND[1] */ Ins_NROUND,
/* NROUND[2] */ Ins_NROUND,
/* NROUND[3] */ Ins_NROUND,
/* WCvtF */ Ins_WCVTF,
/* DeltaP2 */ Ins_DELTAP,
/* DeltaP3 */ Ins_DELTAP,
/* DeltaCn[0] */ Ins_DELTAC,
/* DeltaCn[1] */ Ins_DELTAC,
/* DeltaCn[2] */ Ins_DELTAC,
/* SROUND */ Ins_SROUND,
/* S45Round */ Ins_S45ROUND,
/* JROT */ Ins_JROT,
/* JROF */ Ins_JROF,
/* ROFF */ Ins_ROFF,
/* INS_$7B */ Ins_UNKNOWN,
/* RUTG */ Ins_RUTG,
/* RDTG */ Ins_RDTG,
/* SANGW */ Ins_SANGW,
/* AA */ Ins_AA,
/* FlipPT */ Ins_FLIPPT,
/* FlipRgON */ Ins_FLIPRGON,
/* FlipRgOFF */ Ins_FLIPRGOFF,
/* INS_$83 */ Ins_UNKNOWN,
/* INS_$84 */ Ins_UNKNOWN,
/* ScanCTRL */ Ins_SCANCTRL,
/* SDPVTL[0] */ Ins_SDPVTL,
/* SDPVTL[1] */ Ins_SDPVTL,
/* GetINFO */ Ins_GETINFO,
/* IDEF */ Ins_IDEF,
/* ROLL */ Ins_ROLL,
/* MAX */ Ins_MAX,
/* MIN */ Ins_MIN,
/* ScanTYPE */ Ins_SCANTYPE,
/* InstCTRL */ Ins_INSTCTRL,
/* INS_$8F */ Ins_UNKNOWN,
/* INS_$90 */ Ins_UNKNOWN,
/* INS_$91 */ Ins_UNKNOWN,
/* INS_$92 */ Ins_UNKNOWN,
/* INS_$93 */ Ins_UNKNOWN,
/* INS_$94 */ Ins_UNKNOWN,
/* INS_$95 */ Ins_UNKNOWN,
/* INS_$96 */ Ins_UNKNOWN,
/* INS_$97 */ Ins_UNKNOWN,
/* INS_$98 */ Ins_UNKNOWN,
/* INS_$99 */ Ins_UNKNOWN,
/* INS_$9A */ Ins_UNKNOWN,
/* INS_$9B */ Ins_UNKNOWN,
/* INS_$9C */ Ins_UNKNOWN,
/* INS_$9D */ Ins_UNKNOWN,
/* INS_$9E */ Ins_UNKNOWN,
/* INS_$9F */ Ins_UNKNOWN,
/* INS_$A0 */ Ins_UNKNOWN,
/* INS_$A1 */ Ins_UNKNOWN,
/* INS_$A2 */ Ins_UNKNOWN,
/* INS_$A3 */ Ins_UNKNOWN,
/* INS_$A4 */ Ins_UNKNOWN,
/* INS_$A5 */ Ins_UNKNOWN,
/* INS_$A6 */ Ins_UNKNOWN,
/* INS_$A7 */ Ins_UNKNOWN,
/* INS_$A8 */ Ins_UNKNOWN,
/* INS_$A9 */ Ins_UNKNOWN,
/* INS_$AA */ Ins_UNKNOWN,
/* INS_$AB */ Ins_UNKNOWN,
/* INS_$AC */ Ins_UNKNOWN,
/* INS_$AD */ Ins_UNKNOWN,
/* INS_$AE */ Ins_UNKNOWN,
/* INS_$AF */ Ins_UNKNOWN,
/* PushB[0] */ Ins_PUSHB,
/* PushB[1] */ Ins_PUSHB,
/* PushB[2] */ Ins_PUSHB,
/* PushB[3] */ Ins_PUSHB,
/* PushB[4] */ Ins_PUSHB,
/* PushB[5] */ Ins_PUSHB,
/* PushB[6] */ Ins_PUSHB,
/* PushB[7] */ Ins_PUSHB,
/* PushW[0] */ Ins_PUSHW,
/* PushW[1] */ Ins_PUSHW,
/* PushW[2] */ Ins_PUSHW,
/* PushW[3] */ Ins_PUSHW,
/* PushW[4] */ Ins_PUSHW,
/* PushW[5] */ Ins_PUSHW,
/* PushW[6] */ Ins_PUSHW,
/* PushW[7] */ Ins_PUSHW,
/* MDRP[00] */ Ins_MDRP,
/* MDRP[01] */ Ins_MDRP,
/* MDRP[02] */ Ins_MDRP,
/* MDRP[03] */ Ins_MDRP,
/* MDRP[04] */ Ins_MDRP,
/* MDRP[05] */ Ins_MDRP,
/* MDRP[06] */ Ins_MDRP,
/* MDRP[07] */ Ins_MDRP,
/* MDRP[08] */ Ins_MDRP,
/* MDRP[09] */ Ins_MDRP,
/* MDRP[10] */ Ins_MDRP,
/* MDRP[11] */ Ins_MDRP,
/* MDRP[12] */ Ins_MDRP,
/* MDRP[13] */ Ins_MDRP,
/* MDRP[14] */ Ins_MDRP,
/* MDRP[15] */ Ins_MDRP,
/* MDRP[16] */ Ins_MDRP,
/* MDRP[17] */ Ins_MDRP,
/* MDRP[18] */ Ins_MDRP,
/* MDRP[19] */ Ins_MDRP,
/* MDRP[20] */ Ins_MDRP,
/* MDRP[21] */ Ins_MDRP,
/* MDRP[22] */ Ins_MDRP,
/* MDRP[23] */ Ins_MDRP,
/* MDRP[24] */ Ins_MDRP,
/* MDRP[25] */ Ins_MDRP,
/* MDRP[26] */ Ins_MDRP,
/* MDRP[27] */ Ins_MDRP,
/* MDRP[28] */ Ins_MDRP,
/* MDRP[29] */ Ins_MDRP,
/* MDRP[30] */ Ins_MDRP,
/* MDRP[31] */ Ins_MDRP,
/* MIRP[00] */ Ins_MIRP,
/* MIRP[01] */ Ins_MIRP,
/* MIRP[02] */ Ins_MIRP,
/* MIRP[03] */ Ins_MIRP,
/* MIRP[04] */ Ins_MIRP,
/* MIRP[05] */ Ins_MIRP,
/* MIRP[06] */ Ins_MIRP,
/* MIRP[07] */ Ins_MIRP,
/* MIRP[08] */ Ins_MIRP,
/* MIRP[09] */ Ins_MIRP,
/* MIRP[10] */ Ins_MIRP,
/* MIRP[11] */ Ins_MIRP,
/* MIRP[12] */ Ins_MIRP,
/* MIRP[13] */ Ins_MIRP,
/* MIRP[14] */ Ins_MIRP,
/* MIRP[15] */ Ins_MIRP,
/* MIRP[16] */ Ins_MIRP,
/* MIRP[17] */ Ins_MIRP,
/* MIRP[18] */ Ins_MIRP,
/* MIRP[19] */ Ins_MIRP,
/* MIRP[20] */ Ins_MIRP,
/* MIRP[21] */ Ins_MIRP,
/* MIRP[22] */ Ins_MIRP,
/* MIRP[23] */ Ins_MIRP,
/* MIRP[24] */ Ins_MIRP,
/* MIRP[25] */ Ins_MIRP,
/* MIRP[26] */ Ins_MIRP,
/* MIRP[27] */ Ins_MIRP,
/* MIRP[28] */ Ins_MIRP,
/* MIRP[29] */ Ins_MIRP,
/* MIRP[30] */ Ins_MIRP,
/* MIRP[31] */ Ins_MIRP
};
#endif
/****************************************************************/
/* */
/* 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 whith TRUE, test IP < CodeSize to know wether it */
/* comes from a 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 */
/* */
/* Instructions appear in the specs' order. */
/* */
/****************************************************************/
LOCAL_FUNC
#ifndef DEBUG_INTERPRETER
TT_Error RunIns( PExecution_Context exc )
#else
TT_Error RunIns2( PExecution_Context exc )
#endif
{
UShort A;
PDefRecord WITH;
PCallRecord WITH1;
Long ins_counter = 0; /* executed instructions counter */
#ifdef TT_CONFIG_OPTION_STATIC_INTERPRETER
cur = *exc;
#endif
/* set CVT functions */
CUR.metrics.ratio = 0;
if ( CUR.metrics.x_ppem != CUR.metrics.y_ppem )
{
/* non-square pixels, use the stretched routines */
CUR.func_read_cvt = Read_CVT_Stretched;
CUR.func_write_cvt = Write_CVT_Stretched;
CUR.func_move_cvt = Move_CVT_Stretched;
}
else
{
/* square pixels, use normal routines */
CUR.func_read_cvt = Read_CVT;
CUR.func_write_cvt = Write_CVT;
CUR.func_move_cvt = Move_CVT;
}
COMPUTE_Funcs();
Compute_Round( EXEC_ARGS (Byte)exc->GS.round_state );
do
{
if ( CALC_Length() != SUCCESS_OK )
{
CUR.error = TT_Err_Code_Overflow;
goto LErrorLabel_;
}
/* First, let's check for empty stack and overflow */
CUR.args = CUR.top - (Pop_Push_Count[CUR.opcode] >> 4);
/* `args' is the top of the stack once arguments have been popped. */
/* One can also interpret it as the index of the last argument. */
if ( CUR.args < 0 )
{
CUR.error = TT_Err_Too_Few_Arguments;
goto LErrorLabel_;
}
CUR.new_top = CUR.args + (Pop_Push_Count[CUR.opcode] & 15);
/* `new_top' is the new top of the stack, after the instruction's */
/* execution. `top' will be set to `new_top' after the 'switch' */
/* statement. */
if ( CUR.new_top > CUR.stackSize )
{
CUR.error = TT_Err_Stack_Overflow;
goto LErrorLabel_;
}
CUR.step_ins = TRUE;
CUR.error = TT_Err_Ok;
#ifdef TT_CONFIG_OPTION_INTERPRETER_SWITCH
{
PStorage args = CUR.stack + CUR.args;
Byte opcode = CUR.opcode;
#undef ARRAY_BOUND_ERROR
#define ARRAY_BOUND_ERROR goto Set_Invalid_Ref
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 */
{
Short AA, BB;
AA = (Short)(opcode & 1) << 14;
BB = AA ^ (Short)0x4000;
if ( opcode < 4 )
{
CUR.GS.projVector.x = AA;
CUR.GS.projVector.y = BB;
CUR.GS.dualVector.x = AA;
CUR.GS.dualVector.y = BB;
}
if ( (opcode & 2) == 0 )
{
CUR.GS.freeVector.x = AA;
CUR.GS.freeVector.y = BB;
}
COMPUTE_Funcs();
}
break;
case 0x06: /* SPvTL // */
case 0x07: /* SPvTL + */
DO_SPVTL
break;
case 0x08: /* SFvTL // */
case 0x09: /* SFvTL + */
DO_SFVTL
break;
case 0x0A: /* SPvFS */
DO_SPVFS
break;
case 0x0B: /* SFvFS */
DO_SFVFS
break;
case 0x0C: /* GPV */
DO_GPV
break;
case 0x0D: /* GFV */
DO_GFV
break;
case 0x0E: /* SFvTPv */
DO_SFVTPV
break;
case 0x0F: /* ISECT */
Ins_ISECT( EXEC_ARGS args );
break;
case 0x10: /* SRP0 */
DO_SRP0
break;
case 0x11: /* SRP1 */
DO_SRP1
break;
case 0x12: /* SRP2 */
DO_SRP2
break;
case 0x13: /* SZP0 */
Ins_SZP0( EXEC_ARGS args );
break;
case 0x14: /* SZP1 */
Ins_SZP1( EXEC_ARGS args );
break;
case 0x15: /* SZP2 */
Ins_SZP2( EXEC_ARGS args );
break;
case 0x16: /* SZPS */
Ins_SZPS( EXEC_ARGS args );
break;
case 0x17: /* SLOOP */
DO_SLOOP
break;
case 0x18: /* RTG */
DO_RTG
break;
case 0x19: /* RTHG */
DO_RTHG
break;
case 0x1A: /* SMD */
DO_SMD
break;
case 0x1B: /* ELSE */
Ins_ELSE( EXEC_ARGS args );
break;
case 0x1C: /* JMPR */
DO_JMPR
break;
case 0x1D: /* SCVTCI */
DO_SCVTCI
break;
case 0x1E: /* SSWCI */
DO_SSWCI
break;
case 0x1F: /* SSW */
DO_SSW
break;
case 0x20: /* DUP */
DO_DUP
break;
case 0x21: /* POP */
/* nothing :-) !! */
break;
case 0x22: /* CLEAR */
DO_CLEAR
break;
case 0x23: /* SWAP */
DO_SWAP
break;
case 0x24: /* DEPTH */
DO_DEPTH
break;
case 0x25: /* CINDEX */
DO_CINDEX
break;
case 0x26: /* MINDEX */
Ins_MINDEX( EXEC_ARGS args );
break;
case 0x27: /* ALIGNPTS */
Ins_ALIGNPTS( EXEC_ARGS args );
break;
case 0x28: /* ???? */
Ins_UNKNOWN( EXEC_ARGS args );
break;
case 0x29: /* UTP */
Ins_UTP( EXEC_ARGS args );
break;
case 0x2A: /* LOOPCALL */
Ins_LOOPCALL( EXEC_ARGS args );
break;
case 0x2B: /* CALL */
Ins_CALL( EXEC_ARGS args );
break;
case 0x2C: /* FDEF */
Ins_FDEF( EXEC_ARGS args );
break;
case 0x2D: /* ENDF */
Ins_ENDF( EXEC_ARGS args );
break;
case 0x2E: /* MDAP */
case 0x2F: /* MDAP */
Ins_MDAP( EXEC_ARGS args );
break;
case 0x30: /* IUP */
case 0x31: /* IUP */
Ins_IUP( EXEC_ARGS args );
break;
case 0x32: /* SHP */
case 0x33: /* SHP */
Ins_SHP( EXEC_ARGS args );
break;
case 0x34: /* SHC */
case 0x35: /* SHC */
Ins_SHC( EXEC_ARGS args );
break;
case 0x36: /* SHZ */
case 0x37: /* SHZ */
Ins_SHZ( EXEC_ARGS args );
break;
case 0x38: /* SHPIX */
Ins_SHPIX( EXEC_ARGS args );
break;
case 0x39: /* IP */
Ins_IP( EXEC_ARGS args );
break;
case 0x3A: /* MSIRP */
case 0x3B: /* MSIRP */
Ins_MSIRP( EXEC_ARGS args );
break;
case 0x3C: /* AlignRP */
Ins_ALIGNRP( EXEC_ARGS args );
break;
case 0x3D: /* RTDG */
DO_RTDG
break;
case 0x3E: /* MIAP */
case 0x3F: /* MIAP */
Ins_MIAP( EXEC_ARGS args );
break;
case 0x40: /* NPUSHB */
Ins_NPUSHB( EXEC_ARGS args );
break;
case 0x41: /* NPUSHW */
Ins_NPUSHW( EXEC_ARGS args );
break;
case 0x42: /* WS */
DO_WS
break;
Set_Invalid_Ref:
CUR.error = TT_Err_Invalid_Reference;
break;
case 0x43: /* RS */
DO_RS
break;
case 0x44: /* WCVTP */
DO_WCVTP
break;
case 0x45: /* RCVT */
DO_RCVT
break;
case 0x46: /* GC */
case 0x47: /* GC */
Ins_GC( EXEC_ARGS args );
break;
case 0x48: /* SCFS */
Ins_SCFS( EXEC_ARGS args );
break;
case 0x49: /* MD */
case 0x4A: /* MD */
Ins_MD( EXEC_ARGS args );
break;
case 0x4B: /* MPPEM */
DO_MPPEM
break;
case 0x4C: /* MPS */
DO_MPS
break;
case 0x4D: /* FLIPON */
DO_FLIPON
break;
case 0x4E: /* FLIPOFF */
DO_FLIPOFF
break;
case 0x4F: /* DEBUG */
DO_DEBUG
break;
case 0x50: /* LT */
DO_LT
break;
case 0x51: /* LTEQ */
DO_LTEQ
break;
case 0x52: /* GT */
DO_GT
break;
case 0x53: /* GTEQ */
DO_GTEQ
break;
case 0x54: /* EQ */
DO_EQ
break;
case 0x55: /* NEQ */
DO_NEQ
break;
case 0x56: /* ODD */
DO_ODD
break;
case 0x57: /* EVEN */
DO_EVEN
break;
case 0x58: /* IF */
Ins_IF( EXEC_ARGS args );
break;
case 0x59: /* EIF */
/* do nothing */
break;
case 0x5A: /* AND */
DO_AND
break;
case 0x5B: /* OR */
DO_OR
break;
case 0x5C: /* NOT */
DO_NOT
break;
case 0x5D: /* DELTAP1 */
Ins_DELTAP( EXEC_ARGS args );
break;
case 0x5E: /* SDB */
DO_SDB
break;
case 0x5F: /* SDS */
DO_SDS
break;
case 0x60: /* ADD */
DO_ADD
break;
case 0x61: /* SUB */
DO_SUB
break;
case 0x62: /* DIV */
DO_DIV
break;
case 0x63: /* MUL */
DO_MUL
break;
case 0x64: /* ABS */
DO_ABS
break;
case 0x65: /* NEG */
DO_NEG
break;
case 0x66: /* FLOOR */
DO_FLOOR
break;
case 0x67: /* CEILING */
DO_CEILING
break;
case 0x68: /* ROUND */
case 0x69: /* ROUND */
case 0x6A: /* ROUND */
case 0x6B: /* ROUND */
DO_ROUND
break;
case 0x6C: /* NROUND */
case 0x6D: /* NROUND */
case 0x6E: /* NRRUND */
case 0x6F: /* NROUND */
DO_NROUND
break;
case 0x70: /* WCVTF */
DO_WCVTF
break;
case 0x71: /* DELTAP2 */
case 0x72: /* DELTAP3 */
Ins_DELTAP( EXEC_ARGS args );
break;
case 0x73: /* DELTAC0 */
case 0x74: /* DELTAC1 */
case 0x75: /* DELTAC2 */
Ins_DELTAC( EXEC_ARGS args );
break;
case 0x76: /* SROUND */
DO_SROUND
break;
case 0x77: /* S45Round */
DO_S45ROUND
break;
case 0x78: /* JROT */
DO_JROT
break;
case 0x79: /* JROF */
DO_JROF
break;
case 0x7A: /* ROFF */
DO_ROFF
break;
case 0x7B: /* ???? */
Ins_UNKNOWN( EXEC_ARGS args );
break;
case 0x7C: /* RUTG */
DO_RUTG
break;
case 0x7D: /* RDTG */
DO_RDTG
break;
case 0x7E: /* SANGW */
case 0x7F: /* AA */
/* nothing - obsolete */
break;
case 0x80: /* FLIPPT */
Ins_FLIPPT( EXEC_ARGS args );
break;
case 0x81: /* FLIPRGON */
Ins_FLIPRGON( EXEC_ARGS args );
break;
case 0x82: /* FLIPRGOFF */
Ins_FLIPRGOFF( EXEC_ARGS args );
break;
case 0x83: /* UNKNOWN */
case 0x84: /* UNKNOWN */
Ins_UNKNOWN( EXEC_ARGS args );
break;
case 0x85: /* SCANCTRL */
Ins_SCANCTRL( EXEC_ARGS args );
break;
case 0x86: /* SDPVTL */
case 0x87: /* SDPVTL */
Ins_SDPVTL( EXEC_ARGS args );
break;
case 0x88: /* GETINFO */
Ins_GETINFO( EXEC_ARGS args );
break;
case 0x89: /* IDEF */
Ins_IDEF( EXEC_ARGS args );
break;
case 0x8A: /* ROLL */
Ins_ROLL( EXEC_ARGS args );
break;
case 0x8B: /* MAX */
DO_MAX
break;
case 0x8C: /* MIN */
DO_MIN
break;
case 0x8D: /* SCANTYPE */
Ins_SCANTYPE( EXEC_ARGS args );
break;
case 0x8E: /* INSTCTRL */
Ins_INSTCTRL( EXEC_ARGS args );
break;
case 0x8F:
Ins_UNKNOWN( EXEC_ARGS args );
break;
default:
if ( opcode >= 0xE0 )
Ins_MIRP( EXEC_ARGS args );
else if ( opcode >= 0xC0 )
Ins_MDRP( EXEC_ARGS args );
else if ( opcode >= 0xB8 )
Ins_PUSHW( EXEC_ARGS args );
else if ( opcode >= 0xB0 )
Ins_PUSHB( EXEC_ARGS args );
else
Ins_UNKNOWN( EXEC_ARGS args );
}
}
#else
Instruct_Dispatch[CUR.opcode]( EXEC_ARGS &CUR.stack[CUR.args] );
#endif
if ( CUR.error != TT_Err_Ok )
{
switch ( (Int)(CUR.error) )
{
case TT_Err_Invalid_Opcode: /* looking for redefined instructions */
A = 0;
while ( A < CUR.numIDefs )
{
WITH = &CUR.IDefs[A];
if ( WITH->Active && CUR.opcode == WITH->Opc )
{
if ( CUR.callTop >= CUR.callSize )
{
CUR.error = TT_Err_Invalid_Reference;
goto LErrorLabel_;
}
WITH1 = &CUR.callStack[CUR.callTop];
WITH1->Caller_Range = CUR.curRange;
WITH1->Caller_IP = CUR.IP + 1;
WITH1->Cur_Count = 1;
WITH1->Cur_Restart = WITH->Start;
if ( INS_Goto_CodeRange( WITH->Range, WITH->Start ) == FAILURE )
goto LErrorLabel_;
goto LSuiteLabel_;
}
else
{
A++;
continue;
}
}
CUR.error = TT_Err_Invalid_Opcode;
goto LErrorLabel_;
/* break; Unreachable code warning suppress. Leave in case a later
change to remind the editor to consider break; */
default:
goto LErrorLabel_;
/* break; */
}
}
CUR.top = CUR.new_top;
if ( CUR.step_ins )
CUR.IP += CUR.length;
/* increment instruction counter and check if we didn't */
/* run this program for too long ?? (e.g. infinite loops) */
if ( ++ins_counter > MAX_RUNNABLE_OPCODES )
{
CUR.error = TT_Err_Execution_Too_Long;
goto LErrorLabel_;
}
LSuiteLabel_:
if ( CUR.IP >= CUR.codeSize )
{
if ( CUR.callTop > 0 )
{
CUR.error = TT_Err_Code_Overflow;
goto LErrorLabel_;
}
else
goto LNo_Error_;
}
} while ( !CUR.instruction_trap );
LNo_Error_:
CUR.error = TT_Err_Ok;
LErrorLabel_:
#ifdef TT_CONFIG_OPTION_STATIC_INTERPRETER
*exc = cur;
#endif
return CUR.error;
}
#ifdef DEBUG_INTERPRETER
/* This function must be declared by the debugger front end */
/* in order to specify which code range to debug. */
int debug_coderange = TT_CodeRange_Glyph;
LOCAL_FUNC
TT_Error RunIns( PExecution_Context exc )
{
Int A, diff;
ULong next_IP;
Char ch, oldch;
char *temp;
int key;
TT_Error error = 0;
TGlyph_Zone save;
TGlyph_Zone pts;
#define TT_Round_Off 5
#define TT_Round_To_Half_Grid 0
#define TT_Round_To_Grid 1
#define TT_Round_To_Double_Grid 2
#define TT_Round_Up_To_Grid 4
#define TT_Round_Down_To_Grid 3
#define TT_Round_Super 6
#define TT_Round_Super_45 7
const String* round_str[8] =
{
"to half-grid",
"to grid",
"to double grid",
"down to grid",
"up to grid",
"off",
"super",
"super 45"
};
/* Check that we're running the code range that is effectively */
/* asked by the debugger front end. */
if ( exc->curRange != debug_coderange )
return RunIns2( exc );
pts = exc->pts;
save.n_points = pts.n_points;
save.n_contours = pts.n_contours;
MEM_Alloc( save.org, sizeof ( TT_Vector ) * save.n_points );
MEM_Alloc( save.cur, sizeof ( TT_Vector ) * save.n_points );
MEM_Alloc( save.touch, sizeof ( Byte ) * save.n_points );
exc->instruction_trap = 1;
oldch = '\0';
do
{
if ( exc->IP < exc->codeSize )
{
#ifdef TT_CONFIG_OPTION_STATIC_INTERPRETER
cur = *exc;
#endif
CALC_Length();
exc->args = exc->top - (Pop_Push_Count[exc->opcode] >> 4);
/* `args' is the top of the stack once arguments have been popped. */
/* One can also interpret it as the index of the last argument. */
/* Print the current line. We use a 80-columns console with the */
/* following formatting: */
/* */
/* [loc]:[addr] [opcode] [disassemby] [a][b]|[c][d] */
/* */
{
char temp[80];
int n, col, pop;
int args = CUR.args;
sprintf( temp, "%78c\n", ' ' );
/* first letter of location */
switch ( CUR.curRange )
{
case TT_CodeRange_Glyph:
temp[0] = 'g';
break;
case TT_CodeRange_Cvt:
temp[0] = 'c';
break;
default:
temp[0] = 'f';
}
/* current IP */
sprintf( temp+1, "%04lx: %02x %-36.36s",
CUR.IP,
CUR.opcode,
Cur_U_Line(&CUR) );
strncpy( temp+46, " (", 2 );
args = CUR.top - 1;
pop = Pop_Push_Count[CUR.opcode] >> 4;
col = 48;
for ( n = 6; n > 0; n-- )
{
if ( pop == 0 )
temp[col-1] = (temp[col-1] == '(' ? ' ' : ')' );
if ( args < CUR.top && args >= 0 )
sprintf( temp+col, "%04lx", CUR.stack[args] );
else
sprintf( temp+col, " " );
temp[col+4] = ' ';
col += 5;
pop--;
args--;
}
temp[78] = '\n';
temp[79] = '\0';
PTRACE0(( temp ));
}
/* First, check for empty stack and overflow */
if ( CUR.args < 0 )
{
PTRACE0(( "ERROR : Too few arguments\n" ));
exc->error = TT_Err_Too_Few_Arguments;
goto LErrorLabel_;
}
CUR.new_top = CUR.args + (Pop_Push_Count[CUR.opcode] & 15);
/* new_top is the new top of the stack, after the instruction's */
/* execution. top will be set to new_top after the 'case' */
if ( CUR.new_top > CUR.stackSize )
{
PTRACE0(( "ERROR : Stack overflow\n" ));
exc->error = TT_Err_Stack_Overflow;
goto LErrorLabel_;
}
}
else
PTRACE0(( "End of program reached.\n" ));
key = 0;
do
{
/* read keyboard */
ch = getch();
switch ( ch )
{
/* Help - show keybindings */
case '?':
PTRACE0(( "FDebug Help\n\n" ));
PTRACE0(( "? Show this page\n" ));
PTRACE0(( "q Quit debugger\n" ));
PTRACE0(( "n Skip to next instruction\n" ));
PTRACE0(( "s Step into\n" ));
PTRACE0(( "v Show vector info\n" ));
PTRACE0(( "g Show graphics state\n" ));
PTRACE0(( "p Show points zone\n\n" ));
break;
/* Show vectors */
case 'v':
PTRACE0(( "freedom (%04hx,%04hx)\n", exc->GS.freeVector.x,
exc->GS.freeVector.y ));
PTRACE0(( "projection (%04hx,%04hx)\n", exc->GS.projVector.x,
exc->GS.projVector.y ));
PTRACE0(( "dual (%04hx,%04hx)\n\n", exc->GS.dualVector.x,
exc->GS.dualVector.y ));
break;
/* Show graphics state */
case 'g':
PTRACE0(( "rounding %s\n", round_str[exc->GS.round_state] ));
PTRACE0(( "min dist %04lx\n", exc->GS.minimum_distance ));
PTRACE0(( "cvt_cutin %04lx\n", exc->GS.control_value_cutin ));
break;
/* Show points table */
case 'p':
for ( A = 0; A < exc->pts.n_points; A++ )
{
PTRACE0(( "%02hx ", A ));
PTRACE0(( "%08lx,%08lx - ", pts.org[A].x, pts.org[A].y ));
PTRACE0(( "%08lx,%08lx\n", pts.cur[A].x, pts.cur[A].y ));
}
PTRACE0(( "\n" ));
break;
default:
key = 1;
}
} while ( !key );
MEM_Copy( save.org, pts.org, pts.n_points * sizeof ( TT_Vector ) );
MEM_Copy( save.cur, pts.cur, pts.n_points * sizeof ( TT_Vector ) );
MEM_Copy( save.touch, pts.touch, pts.n_points );
/* a return indicate the last command */
if (ch == '\r')
ch = oldch;
switch ( ch )
{
/* Quit debugger */
case 'q':
goto LErrorLabel_;
/* Step over */
case 'n':
if ( exc->IP < exc->codeSize )
{
/* `step over' is equivalent to `step into' except if */
/* the current opcode is a CALL or LOOPCALL */
if ( CUR.opcode != 0x2a && CUR.opcode != 0x2b )
goto Step_into;
/* otherwise, loop execution until we reach the next opcode */
next_IP = CUR.IP + CUR.length;
while ( exc->IP != next_IP )
{
if ( ( error = RunIns2( exc ) ) )
goto LErrorLabel_;
}
}
oldch = ch;
break;
/* Step into */
case 's':
if ( exc->IP < exc->codeSize )
Step_into:
if ( ( error = RunIns2( exc ) ) )
goto LErrorLabel_;
oldch = ch;
break;
default:
PTRACE0(( "unknown command. Press ? for help\n" ));
oldch = '\0';
}
for ( A = 0; A < pts.n_points; A++ )
{
diff = 0;
if ( save.org[A].x != pts.org[A].x ) diff |= 1;
if ( save.org[A].y != pts.org[A].y ) diff |= 2;
if ( save.cur[A].x != pts.cur[A].x ) diff |= 4;
if ( save.cur[A].y != pts.cur[A].y ) diff |= 8;
if ( save.touch[A] != pts.touch[A] ) diff |= 16;
if ( diff )
{
PTRACE0(( "%02hx ", A ));
if ( diff & 16 ) temp = "(%01hx)"; else temp = " %01hx ";
PTRACE0(( temp, save.touch[A] & 7 ));
if ( diff & 1 ) temp = "(%08lx)"; else temp = " %08lx ";
PTRACE0(( temp, save.org[A].x ));
if ( diff & 2 ) temp = "(%08lx)"; else temp = " %08lx ";
PTRACE0(( temp, save.org[A].y ));
if ( diff & 4 ) temp = "(%08lx)"; else temp = " %08lx ";
PTRACE0(( temp, save.cur[A].x ));
if ( diff & 8 ) temp = "(%08lx)"; else temp = " %08lx ";
PTRACE0(( temp, save.cur[A].y ));
PTRACE0(( "\n" ));
PTRACE0(( "%02hx ", A ));
if ( diff & 16 ) temp = "[%01hx]"; else temp = " %01hx ";
PTRACE0(( temp, pts.touch[A] & 7 ));
if ( diff & 1 ) temp = "[%08lx]"; else temp = " %08lx ";
PTRACE0(( temp, pts.org[A].x ));
if ( diff & 2 ) temp = "[%08lx]"; else temp = " %08lx ";
PTRACE0(( temp, pts.org[A].y ));
if ( diff & 4 ) temp = "[%08lx]"; else temp = " %08lx ";
PTRACE0(( temp, pts.cur[A].x ));
if ( diff & 8 ) temp = "[%08lx]"; else temp = " %08lx ";
PTRACE0(( temp, pts.cur[A].y ));
PTRACE0(( "\n\n" ));
}
}
} while ( TRUE );
LErrorLabel_:
return error;
}
#endif /* DEBUG_INTERPRETER */
#endif /* TT_CONFIG_OPTION_NO_INTERPRETER */
/* END */
TTINTERP.H
/*******************************************************************
*
* ttinterp.h 2.2
*
* TrueType bytecode intepreter.
*
* Copyright 1996-1999 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.
*
*
* Changes between 2.2 and 2.1:
*
* - a small bugfix in the Push opcodes
*
* Changes between 2.1 and 2.0:
*
* - created the TTExec component to take care of all execution
* context management. The interpreter has now one single
* function.
*
* - made some changes to support re-entrancy. The re-entrant
* interpreter is smaller!
*
******************************************************************/
#ifndef TTINTERP_H
#define TTINTERP_H
#include "ttconfig.h"
#include "ttobjs.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Run instructions in current execution context */
LOCAL_DEF TT_Error RunIns( PExecution_Context exc );
#ifdef __cplusplus
}
#endif
#endif /* TTINTERP_H */
/* END */
T168_111\appl\Text\Freetype文件:第24~28个文件
最新推荐文章于 2024-09-15 22:31:42 发布