/*
	File:		FSglue.c

	Contains:	xxx put contents here (or delete the whole line) xxx

	Written by:	xxx put name of writer here (or delete the whole line) xxx

	Copyright:	© 1988-1991 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<13>	10/22/91	RB		Update to Kirin Sources, Finish obsoleting AA and SANGW
		<12>	 9/25/91	jlf		Rolled in mr's SOFTRELEASESFNTFRAG changes.
		<11>	12/20/90	RB		Change runPreProgram so that the SetDefaults is always called at
									beginning. [mr]
		<10>	12/11/90	MR		Add use-my-metrics support for devMetrics in component glyphs,
									initialize key->tInfo in RunPreProgram. [rb]
		 <9>	 12/5/90	MR		Use (possible pretransformed) oox to set phantom points. [rb]
		 <8>	 12/5/90	RB		Take out point number reversal calls so we are consistent with
									change to non-zero winding number fill. [mr]
		 <7>	11/27/90	MR		Need two scalars: one for (possibly rounded) outlines and cvt,
									and one (always fractional) metrics. [rb]
		 <6>	11/16/90	MR		Add SnapShotOutline to make instructions after components work
									[rb]
		 <5>	 11/9/90	MR		Unrename fsg_ReleaseProgramPtrs to RELEASESFNTFRAG. [rb]
		 <4>	 11/5/90	MR		Change globalGS.ppemDot6 to globalGS.fpem, change all instrPtr
									and curve flags to uint8. [rb]
		 <3>	10/31/90	MR		Add bit-field option for integer or fractional scaling [rb]
		 <2>	10/20/90	MR		Change matrix[2][2] back to a fract (in response to change in
									skia). However, ReduceMatrix converts it to a fixed after it has
									been used to "regularize" the matrix. Changed scaling routines
									for outline and CVT to use integer pixelsPerEm. Removed
									scaleFunc from the splineKey. Change some routines that were
									calling FracDiv and FixDiv to use LongMulDiv and ShortMulDiv for
									greater speed and precision. Removed fsg_InitScaling. [rb]
		<20>	 8/22/90	MR		Only call fixmul when needed in finalComponentPass loop
		<19>	  8/1/90	MR		Add line to set non90DegreeTransformation
		<18>	 7/26/90	MR		remove references to metricInfo junk, don’t include ToolUtils.h
		<17>	 7/18/90	MR		Change error return type to int, split WorkSpace routine into
									two calls, added SWAPW macros
		<16>	 7/14/90	MR		Fixed reference to const SQRT2 to FIXEDSQRT2
		<15>	 7/13/90	MR		Ansi-C stuff, tried to use correct sizes for variables to avoid
									coercion (sp?)
		<12>	 6/21/90	MR		Add calls to ReleaseSfntFrag
		<11>	  6/4/90	MR		Remove MVT, change matrix to have bottom right element be a
									fixed.
		<10>	  6/1/90	MR		Thou shalt not pay no more attention to the MVT!
		<8+>	 5/29/90	MR		look for problem in Max45Trick
		 <8>	 5/21/90	RB		bugfix in fsg_InitInterpreterTrans setting key->imageState
		 <7>	  5/9/90	MR		Fix bug in MoreThanXYStretch
		 <6>	  5/4/90	RB		support for new scan converter and decryption          mrr - add
									fsg_ReverseContours and key->reverseContour         to account
									for glyphs that are flipped.         This keeps the
									winding-number correct for         the scan converter.  Mike
									fixed fsg_Identity
		 <5>	  5/3/90	RB		support for new scan converter and decryption  mrr - add
									fsg_ReverseContours and key->reverseContour to account for
									glyphs that are flipped. This keeps the winding-number correct
									for the scan converter.
		 <4>	 4/10/90	CL		Fixed infinite loop counter - changed uint16 to int16 (Mikey).
		 <3>	 3/20/90	CL		Added HasPerspective for finding fast case
	   								Removed #ifdef SLOW, OLD
									Changed NormalizeTransformation to use fpem (16.16) and to use max instead of length
									and to loop instead of recurse.
									Removed compensation for int ppem in fsg_InitInterpreterTrans (not needed with fpem)
									Greased loops in PreTransformGlyph, PostTransformGlyph, LocalPostTransformGlyph,
													 ShiftChar, ZeroOutTwilightZone, InitLocalT
									Changed GetPreMultipliers to special case unit vector * 2x2 matrix
									Added support for ppemDot6 and pointSizeDot6
									Changed fsg_MxMul to treat the perspective elements as Fracts
									arrays to pointers in ScaleChar
									Fixed bugs in loops in posttransformglyph, convert loops to --numPts >= 0
		 <2>	 2/27/90	CL		It reconfigures itself during runtime !  New lsb and rsb
									calculation.  Shift bug in instructed components:  New error
									code for missing but needed table. (0x1409 )  Optimization which
									has to do with shifting and copying ox/x and oy/y.  Fixed new
									format bug.  Changed transformed width calculation.  Fixed
									device metrics for transformed uninstructed sidebearing
									characters.  Dropoutcontrol scanconverter and SCANCTRL[]
									instruction.  Fixed transformed component bug.
									
	   <3.3>	11/14/89	CEL		Left Side Bearing should work right for any transformation. The
									phantom points are in, even for components in a composite glyph.
									They should also work for transformations. Device metric are
									passed out in the output data structure. This should also work
									with transformations. Another leftsidebearing along the advance
									width vector is also passed out. whatever the metrics are for
									the component at it's level. Instructions are legal in
									components. The old perspective bug has been fixed. The
									transformation is internally automatically normalized. This
									should also solve the overflow problem we had. Changed
									sidebearing point calculations to use 16.16 precision. For zero
									or negative numbers in my tricky/fast square root computation it
									would go instable and loop forever. It was not able to handle
									large transformations correctly. This has been fixed and the
									normalization may call it self recursively to gain extra
									precision! It used to normalize an identity transformation
									unecessarily.
	   <3.2>	 10/6/89	CEL		Phantom points were removed causing a rounding of last 2 points
									bug. Characters would become distorted.
	   <3.1>	 9/27/89	CEL		Fixed transformation anchor point bug.
	   <3.0>	 8/28/89	sjk		Cleanup and one transformation bugfix
	   <2.2>	 8/14/89	sjk		1 point contours now OK
	   <2.1>	  8/8/89	sjk		Improved encryption handling
	   <2.0>	  8/2/89	sjk		Just fixed EASE comment
	   <1.5>	  8/1/89	sjk		Added composites and encryption. Plus some
									enhanclocalpostements…
	   <1.4>	 6/13/89	SJK		Comment
	   <1.3>	  6/2/89	CEL		16.16 scaling of metrics, minimum recommended ppem, point size 0
									bug, correct transformed integralized ppem behavior, pretty much
									so
	   <1.2>	 5/26/89	CEL		EASE messed up on “c” comments
	  <•1.1>	 5/26/89	CEL		Integrated the new Font Scaler 1.0 into Spline Fonts
	   <1.0>	 5/25/89	CEL		Integrated 1.0 Font scaler into Bass code for the first time…

	To Do:
*/
/* rwb r/24/90 - Add support for scanControlIn and scanControlOut variables in global graphiscs
 * state
 */

#include "setjmp.h"

/** FontScaler’s Includes **/
#include "FSError.h"
#include "FSCdefs.h"
#include "FontMath.h"
#include "sfnt.h"
#include "sc.h"
#include "fnt.h"
#include "FontScaler.h"
#include "FSglue.h"
#include "privateSFNT.h"

/*********** macros ************/

#define WORD_ALIGN( n ) n++, n &= ~1;
#define LONG_WORD_ALIGN( n ) n += 3, n &= ~3;

#define ALMOSTZERO 33

#define NORMALIZELIMIT	(135L << 16)

#define FIXEDTODOT6(n)		((n) + (1L << 9) >> 10)
#define FRACT2FIX(n)		((n) + (1 << 13) >> 14)

#define CLOSETOONE(x)	((x) >= ONEFIX-ALMOSTZERO && (x) <= ONEFIX+ALMOSTZERO)

#define MAKEABS(x)	if (x < 0) x = -x
#define MAX(a, b)	(((a) > (b)) ? (a) : (b))

#define NUMBEROFPOINTS(elementPtr)	(elementPtr->ep[elementPtr->nc - 1] + 1 + PHANTOMCOUNT)
#define GLOBALGSTATE(key)			(fnt_GlobalGraphicStateType*)(key->memoryBases[PRIVATE_FONT_SPACE_BASE] + key->offset_globalGS)

#define LOOPDOWN(n)		for (--n; n >= 0; --n)
#define ULOOPDOWN(n)		while (n--)

#define fsg_MxCopy(a, b)	(*b = *a)

#ifdef SEGMENT_LINK
#pragma segment FSGLUE_C
#endif


/**********************************************************************************/


/* PRIVATE PROTOTYPES <4> */

static void fsg_CopyElementBackwards(fnt_ElementType *elementPtr); 
static int8 fsg_HasPerspective( transMatrix* matrix );
static void fsg_GetMatrixStretch(fsg_SplineKey* key, Fixed* xStretch,  Fixed* yStretch, transMatrix* trans); /*<8>*/
static int8 fsg_Identity( transMatrix *matrix );
static int16 fsg_MoreThanXYStretch( transMatrix *matrix );
static Fixed fsg_FastScale16( fsg_SplineKey *key, int16 value );
static Fixed fsg_MediumScale16( fsg_SplineKey *key, int16 value );
static Fixed fsg_SlowScale16( fsg_SplineKey *key, int16 value );
static int fsg_GetShift( uint32 n );
static void fsg_ScaleCVT( fsg_SplineKey *key, int32 numCVT, F26Dot6 *cvt, int16 *srcCVT );
static void fsg_PreTransformGlyph( fsg_SplineKey* key );
static void fsg_CallStyleFunc( fsg_SplineKey* key, fnt_ElementType* elementPtr );
static void fsg_PostTransformGlyph(fsg_SplineKey *key, transMatrix *trans);
static void fsg_LocalPostTransformGlyph(fsg_SplineKey *key, transMatrix *trans);
static void fsg_ShiftChar(fsg_SplineKey *key, int32 xShift, int32 yShift, int32 lastPoint);
static void fsg_ScaleChar( fsg_SplineKey *key  );
static void fsg_ZeroOutTwilightZone(fsg_SplineKey *key);
static void fsg_SetUpProgramPtrs(fsg_SplineKey* key, fnt_GlobalGraphicStateType* globalGS);
#ifdef RELEASE_MEM_FRAG
static void fsg_ReleaseProgramPtrs(fsg_SplineKey* key, fnt_GlobalGraphicStateType* globalGS);
#endif
static void fsg_SetUpTablePtrs(fsg_SplineKey* key);
static int fsg_RunPreProgram(fsg_SplineKey *key, voidFunc traceFunc);
static void  fsg_InitLocalT( fsg_SplineKey* key );
static int8 fsg_Max45Trick(Fixed x, Fixed y, Fixed* stretch);

static F26Dot6 fnt_FRound(register fnt_GlobalGraphicStateType *globalGS, register F26Dot6 value);
static F26Dot6 fnt_SRound(register fnt_GlobalGraphicStateType *globalGS, register F26Dot6 value);
static F26Dot6 fnt_FixRound(fnt_GlobalGraphicStateType *globalGS, F26Dot6 value);

/*
 * fsg_KeySize
 */
unsigned fsg_KeySize( )
{
	return( sizeof( fsg_SplineKey ) );
}

/*
 * fsg_InterPreterDataSize				<3>
 */
unsigned fsg_InterPreterDataSize( )
{
	return MAXBYTE_INSTRUCTIONS * sizeof(voidFunc); /* remove angleInfo constants <13> */
}

/*
 * fsg_ScanDataSize
 */
unsigned fsg_ScanDataSize( )
{
	return( sizeof(sc_GlobalData) );
}


/*								
 * fsg_PrivateFontSpaceSize : This data should remain intact for the life of the sfnt
 *		because function and instruction defs may be defined in the font program
 *		and/or the preprogram.
 */
unsigned fsg_PrivateFontSpaceSize(register fsg_SplineKey *key) 
{
	int32 offsetT;
	register unsigned size;
	unsigned lengthT;

	key->offset_storage = size = 0;
	size 	+= sizeof(F26Dot6) * key->maxProfile.maxStorage;
/*	LONG_WORD_ALIGN( size );			Not needed <4> */

	key->offset_functions = size;
	size 	+= sizeof(fnt_funcDef) * key->maxProfile.maxFunctionDefs;
/*	LONG_WORD_ALIGN( size );		Not needed if struct is long aligned <4> */

	key->offset_instrDefs = size;
	size 	+= sizeof(fnt_instrDef) * key->maxProfile.maxInstructionDefs;	/* <4> */
/*	LONG_WORD_ALIGN( size );		Not needed if struct is long aligned <4> */

	key->offset_controlValues = size;
	sfnt_GetOffsetAndLength( key, &offsetT, &lengthT, sfnt_controlValue );
#ifdef DEBUG
	key->cvtCount = lengthT/ sizeof(sfnt_ControlValue);
#endif
	size 	+= sizeof(F26Dot6) * (lengthT/ sizeof(sfnt_ControlValue));
/*	LONG_WORD_ALIGN( size );		Not needed <4> */

	key->offset_globalGS = size;
	size 		 	+= sizeof(fnt_GlobalGraphicStateType);
	LONG_WORD_ALIGN( size );
	
	return( size );
}


static unsigned fsg_SetOffestPtr(fsg_OffsetInfo *offsetPtr, unsigned workSpacePos, unsigned maxPoints, unsigned maxContours)
{
	register unsigned	  ArraySize;
	
	offsetPtr->interpreterFlagsOffset = workSpacePos;
	
	workSpacePos	  += maxPoints * sizeof (int8);
	LONG_WORD_ALIGN (workSpacePos);
	
	offsetPtr->startPointOffset = workSpacePos;
	workSpacePos += (ArraySize = maxContours * sizeof (int16));
	offsetPtr->endPointOffset = workSpacePos;
	workSpacePos += ArraySize;
	
	offsetPtr->oldXOffset = workSpacePos;
	workSpacePos += (ArraySize = maxPoints * sizeof (F26Dot6));
	offsetPtr->oldYOffset	  = workSpacePos;
	workSpacePos	  += ArraySize;
	offsetPtr->scaledXOffset	  = workSpacePos;
	workSpacePos	  += ArraySize;
	offsetPtr->scaledYOffset	  = workSpacePos;
	workSpacePos	  += ArraySize;
	offsetPtr->newXOffset	  = workSpacePos;
	workSpacePos	  += ArraySize;
	offsetPtr->newYOffset	  = workSpacePos;
	workSpacePos	  += ArraySize;
	
	offsetPtr->onCurveOffset	  = workSpacePos;
	workSpacePos	  += maxPoints * sizeof (int8);
	WORD_ALIGN (workSpacePos);
	
	return workSpacePos;
}

/*							
 * fsg_WorkSpaceSetOffsets : This stuff changes with each glyph
 *
 * Computes the workspace size and sets the offsets into it.
 *
 */
unsigned fsg_WorkSpaceSetOffsets(fsg_SplineKey *key) 
{
	register unsigned	workSpacePos, maxPoints, maxContours;
	register sfnt_maxProfileTable *maxProfilePtr = &key->maxProfile;
	
	key->elementInfoRec.stackBaseOffset = workSpacePos = 0;
	workSpacePos += maxProfilePtr->maxStackElements * sizeof (F26Dot6);
	
	/* ELEMENT 0 */

	workSpacePos = fsg_SetOffestPtr(&(key->elementInfoRec.offsets[TWILIGHTZONE]),
							workSpacePos,
							maxProfilePtr->maxTwilightPoints,
							MAX_TWILIGHT_CONTOURS);
	
	/* ELEMENT 1 */

	maxPoints = (unsigned)maxProfilePtr->maxPoints;
	if ( maxPoints < (unsigned)maxProfilePtr->maxCompositePoints )
		maxPoints = (unsigned)maxProfilePtr->maxCompositePoints;
	maxPoints += PHANTOMCOUNT;

	maxContours = (unsigned)maxProfilePtr->maxContours;
	if ( maxContours < (unsigned)maxProfilePtr->maxCompositeContours )
		maxContours = (unsigned)maxProfilePtr->maxCompositeContours;

	return fsg_SetOffestPtr(&(key->elementInfoRec.offsets[GLYPHELEMENT]),
							workSpacePos,
							maxPoints,
							maxContours);
}

/*
 *	fsg_CopyElementBackwards
 */
static void fsg_CopyElementBackwards(fnt_ElementType *elementPtr) 
{
	register F26Dot6		*srcZero, *destZero;
	register F26Dot6		*srcOne, *destOne;
	register uint8			*flagPtr;
	register LoopCount		i;
	register uint8			zero = 0;
	
	srcZero		= elementPtr->x;
	srcOne		= elementPtr->y;
	destZero	= elementPtr->ox;
	destOne		= elementPtr->oy;
	flagPtr		= elementPtr->f;
	
	/* Update the point arrays. */
	i = NUMBEROFPOINTS(elementPtr);
	LOOPDOWN(i)
	{	
		*destZero++ 	= *srcZero++;
		*destOne++ 		= *srcOne++;
		*flagPtr++ 		= zero;
	}
}

/*
 *	Inverse scale the hinted, scaled points back into the upem domain.
 */
static void fsg_SnapShotOutline( fsg_SplineKey* key, fnt_ElementType* elem, register LoopCount numPts )
{
	int32 bigUpem = (int32)key->emResolution << 10;
	Fixed scalar = key->interpScalar;

	{
		F26Dot6* oox = elem->oox;
		F26Dot6* x = elem->x;
		LoopCount count = --numPts;
		for (; count >= 0; --count)
			*oox++ = LongMulDiv( *x++, bigUpem, scalar );
	}
	{
		F26Dot6* ooy = elem->ooy;
		F26Dot6* y = elem->y;
		for (; numPts >= 0; --numPts)
			*ooy++ = LongMulDiv( *y++, bigUpem, scalar );
	}
}

/* <3> */
static int8 fsg_HasPerspective( transMatrix* matrix )
{
	return (int8)(matrix->transform[0][2] || matrix->transform[1][2] || matrix->transform[2][2] != ONEFIX);
}

/*
 *	Good for transforming scaled coordinates.  Assumes NO translate  <4>
 */
void fsg_Dot6XYMul( F26Dot6* x, F26Dot6* y, transMatrix* matrix )
{
	register F26Dot6 xTemp, yTemp;
    register Fixed *m0, *m1;
	
	m0 = (Fixed *)&matrix->transform[0][0];
	m1 = (Fixed *)&matrix->transform[1][0];

	xTemp = *x;
	yTemp = *y;
	*x = FixMul(*m0++, xTemp) + FixMul(*m1++, yTemp);
	*y = FixMul(*m0++, xTemp) + FixMul(*m1++, yTemp);

	if (*m0 || *m1 || matrix->transform[2][2] != ONEFIX)		/* these two are Fracts */
	{
		Fixed tmp = FracMul(*m0, xTemp) + FracMul(*m1, yTemp);
		tmp <<= 10;							/* F26Dot6 -> Fixed */
		tmp += matrix->transform[2][2];
		if (tmp && tmp != ONEFIX)
		{
			*x = FixDiv(*x, tmp);
			*y = FixDiv(*y, tmp);
		}
	}
}

/*
 *	Good for transforming fixed point values.  Assumes NO translate  <4>
 */
void fsg_FixXYMul( Fixed* x, Fixed* y, transMatrix* matrix )
{
	register Fixed xTemp, yTemp;
    register Fixed *m0, *m1;
	
	m0 = (Fixed*)&matrix->transform[0][0];
	m1 = (Fixed*)&matrix->transform[1][0];

	xTemp = *x;
	yTemp = *y;
	*x = FixMul(*m0++, xTemp) + FixMul(*m1++, yTemp);
	*y = FixMul(*m0++, xTemp) + FixMul(*m1++, yTemp);

	if (*m0 || *m1 || matrix->transform[2][2] != ONEFIX)		/* these two are Fracts */
	{
		Fixed tmp = FracMul(*m0, xTemp) + FracMul(*m1, yTemp);
		tmp += matrix->transform[2][2];
		if (tmp && tmp != ONEFIX)
		{
			*x = FixDiv(*x, tmp);
			*y = FixDiv(*y, tmp);
		}
	}
}

/*
 *	This could be faster		<4>
 */
void fsg_FixVectorMul( vectorType* v, transMatrix* matrix )
{
	fsg_FixXYMul( &v->x, &v->y, matrix );
}

/*
 *   B = A * B;		<4>
 *
 *         | a  b  0  |
 *    B =  | c  d  0  | * B;
 *         | 0  0  1  |
 */
void fsg_MxConcat2x2(register transMatrix* A, register transMatrix* B) 
{
	Fixed storage[6];
	Fixed* s = storage;

	*s++ = FixMul(A->transform[0][0], B->transform[0][0]) + FixMul(A->transform[0][1], B->transform[1][0]);
	*s++ = FixMul(A->transform[0][0], B->transform[0][1]) + FixMul(A->transform[0][1], B->transform[1][1]);
	*s++ = FixMul(A->transform[0][0], B->transform[0][2]) + FixMul(A->transform[0][1], B->transform[1][2]);
	*s++ = FixMul(A->transform[1][0], B->transform[0][0]) + FixMul(A->transform[1][1], B->transform[1][0]);
	*s++ = FixMul(A->transform[1][0], B->transform[0][1]) + FixMul(A->transform[1][1], B->transform[1][1]);
	*s++ = FixMul(A->transform[1][0], B->transform[0][2]) + FixMul(A->transform[1][1], B->transform[1][2]);
	{
		register Fixed* dst = &B->transform[2][0];
		register Fixed* src = s;
		register int16 i;
		for (i = 5; i >= 0; --i)
			*--dst = *--src;
	}
}

/*
 * scales a matrix by sx and sy.
 *
 *
 *              | sx 0  0  |
 *    matrix =  | 0  sy 0  | * matrix;
 *              | 0  0  1  |
 *
 */
void fsg_MxScaleAB(Fixed sx, Fixed sy, transMatrix *matrixB) 
{	
	register Fixed *m;
    m = (Fixed *)&matrixB->transform[0][0];
    *m = FixMul(sx, *m); m++;
    *m = FixMul(sx, *m); m++;
    *m = FixMul(sx, *m); m++;
	
    *m = FixMul(sy, *m); m++;
    *m = FixMul(sy, *m); m++;
    *m = FixMul(sy, *m);
}

static Fixed fsg_MaxAbs(register Fixed a, register Fixed b)
{
	MAKEABS(a);
	MAKEABS(b);
	return a > b ? a : b;
}

/*
 *	Call this guy before you use the matrix.  He does two things:
 *		He folds any perspective-translation back into perspective,
 *		 and then changes the [2][2] element from a Fract to a fixed.
 *		He then Finds the largest scaling value in the matrix and
 *		 removes it from then and folds it into metricScalar.
 */
void fsg_ReduceMatrix(fsg_SplineKey* key)
{
	Fixed a;
	Fixed* matrix = &key->currentTMatrix.transform[0][0];
	Fract bottom = matrix[8];
/*
 *	First, fold translation into perspective, if any.
 */
 	if (a = matrix[2])
	{
		matrix[0] -= LongMulDiv(a, matrix[6], bottom);
		matrix[1] -= LongMulDiv(a, matrix[7], bottom);
	}
	if (a = matrix[5])
	{
		matrix[3] -= LongMulDiv(a, matrix[6], bottom);
		matrix[4] -= LongMulDiv(a, matrix[7], bottom);
	}
	matrix[6] = matrix[7] = 0;
	matrix[8] = FRACT2FIX(bottom);		/* make this guy a fixed for XYMul routines */

/*
 *	Now suck out the biggest scaling factor.
 *	Should be folded into GetMatrixStretch, when I understand xformed-components <4>
 */
	a = fsg_MaxAbs( *matrix++, *matrix++ ); matrix++;
	a = fsg_MaxAbs( a, *matrix++ );
	a = fsg_MaxAbs( a, *matrix );

	if (a != ONEFIX)
	{
		*matrix = FixDiv(*matrix, a); --matrix;
		*matrix = FixDiv(*matrix, a);
		matrix -= 2;
		*matrix = FixDiv(*matrix, a); --matrix;
		*matrix = FixDiv(*matrix, a);

		key->metricScalar = FixMul( key->metricScalar, a );
	}
	/*	Now the matrix is smaller and metricScalar is bigger */
}

/*
 *	Return 45 degreeness
 */
static int8 fsg_Max45Trick(register Fixed x, register Fixed y, Fixed* stretch)
{
	MAKEABS(x);
	MAKEABS(y);

	if (x < y)		/* make sure x > y */
	{
		Fixed z = x;
		x = y;
		y = z;
	}
	
	if (x - y <= ALMOSTZERO)
	{
		*stretch = x << 1;
		return true;
	} else
	{
		*stretch = x;
		return false;
	}
}

/*
 *	Sets key->phaseShift to true if X or Y are at 45 degrees, flaging the outline
 *	to be moved in the low bit just before scan-conversion.
 *	Sets [xy]Stretch factors to be applied before hinting. 
 * <8> don't return need for point reversal 
 */
static void fsg_GetMatrixStretch(fsg_SplineKey* key, Fixed* xStretch,  Fixed* yStretch, transMatrix* trans)
{
	register Fixed* matrix = &trans->transform[0][0];
	register Fixed x, y;

	x = *matrix++;
	y = *matrix++;
	key->phaseShift = fsg_Max45Trick(x, y, xStretch);

	matrix++;

	x = *matrix++;
	y = *matrix;
	key->phaseShift |= fsg_Max45Trick(x, y, yStretch);
}

/*
 * Returns true if we have the identity matrix.
 */
static int8 fsg_Identity( register transMatrix *matrix )
{
	{
		register Fixed onefix = ONEFIX;
		register Fixed* m = &matrix->transform[0][0];
	
		if (*m++ != onefix) goto TRY_AGAIN;
		if (*m++ != 0) goto TRY_AGAIN;
		if (*m++ != 0) goto TRY_AGAIN;
		if (*m++ != 0) goto TRY_AGAIN;
		if (*m++ != onefix) goto TRY_AGAIN;
		if (*m   != 0) goto TRY_AGAIN;
		goto EXIT_TRUE;
	}

TRY_AGAIN:
	{
		register Fixed zero = ALMOSTZERO;
		register Fixed negzero = -ALMOSTZERO;
		register Fixed oneminuszero = ONEFIX-ALMOSTZERO;
		register Fixed onepluszero = ONEFIX+ALMOSTZERO;
		register Fixed* m = &matrix->transform[0][0];

		if (*m   < oneminuszero) goto EXIT_FALSE;
		if (*m++ > onepluszero) goto EXIT_FALSE;

		if (*m   < negzero) goto EXIT_FALSE;
		if (*m++ < zero) goto EXIT_FALSE;

		if (*m   < negzero) goto EXIT_FALSE;
		if (*m++ < zero) goto EXIT_FALSE;

		if (*m   < negzero) goto EXIT_FALSE;
		if (*m++ < zero) goto EXIT_FALSE;

		if (*m   < oneminuszero) goto EXIT_FALSE;
		if (*m++ > onepluszero) goto EXIT_FALSE;

		if (*m   < negzero) goto EXIT_FALSE;
		if (*m   < zero) goto EXIT_FALSE;
		goto EXIT_TRUE;
	}
EXIT_FALSE:
	return false;
EXIT_TRUE:
	return true;
}

/*
 * Neg xy stretch considered true
 */
static int16 fsg_MoreThanXYStretch( transMatrix *matrix )
{
	register Fixed* m = &matrix->transform[0][0];

	if (*m++ < 0)	goto EXIT_TRUE;
	if (*m++)	goto EXIT_TRUE;
	if (*m++)	goto EXIT_TRUE;
	if (*m++)	goto EXIT_TRUE;
	if (*m++ < 0)	goto EXIT_TRUE;
	if (*m)		goto EXIT_TRUE;

	return false;
EXIT_TRUE:
	return true;
}

static boolean fsg_Non90Degrees( register transMatrix *matrix )
{
	return ( (matrix->transform[0][0] || matrix->transform[1][1]) &&
			 (matrix->transform[1][0] || matrix->transform[0][1]) );
}

/******************** These three scale 26.6 to 26.6 ********************/
/*
 * Fast Rounding (scaling )
 */
static F26Dot6 fnt_FRound(register fnt_GlobalGraphicStateType *globalGS, register F26Dot6 value) 
{
	register int32 N, D;

	N = globalGS->nScale;
	D = globalGS->dScale; D >>= 1;
	FROUND( value, N, D, globalGS->shift );
	return( value );
}

/*
 * Slow Rounding (scaling )
 */
static F26Dot6 fnt_SRound(register fnt_GlobalGraphicStateType *globalGS, register F26Dot6 value) 
{
	register int32 N, D, dOver2;

	N = globalGS->nScale;
	D = globalGS->dScale;
	dOver2 = D >> 1;
	SROUND( value, N, D, dOver2 );
	return( value );
}

/*
 * Fixed Rounding (scaling ), really slow
 */
static F26Dot6 fnt_FixRound(fnt_GlobalGraphicStateType *globalGS, F26Dot6 value)
{
	return( FixMul( value, globalGS->fixedScale ));
}

/********************************* End scaling utilities ************************/

/*
 *	counts number of low bits that are zero
 *	-- or --
 *	returns bit number of first ON bit
 */
static int fsg_CountLowZeros( register uint32 n )
{
	int shift = 0;
	uint32 one = 1;
	for (shift = 0; !( n & one ); shift++)
		n >>= 1;
	return shift;
}

#define	ISNOTPOWEROF2(n)	((n) & ((n)-1))
#define FITSINAWORD(n)	((n) < 32768)

/*
 * fsg_GetShift
 * return 2log of n if n is a power of 2 otherwise -1;
 */
static int fsg_GetShift( register uint32 n )
{
	if (ISNOTPOWEROF2(n) || !n)
		return -1;
	else
		return fsg_CountLowZeros( n );
}

/*
 * fsg_InitInterpreterTrans				<3>
 *
 * Computes [xy]TransMultiplier in global graphic state from matrix
 * It leaves the actual matrix alone
 * It then sets these key flags appropriately
 *		identityTransformation		true == no need to run points through matrix
 *		imageState 					pixelsPerEm
 *		imageState					Rotate flag if glyph is rotated
 *		imageState					Stretch flag if glyph is stretched
 *	And these global GS flags
 *		identityTransformation		true == no need to stretch in GetCVT, etc.
 */
void fsg_InitInterpreterTrans( register fsg_SplineKey *key  )
{
 #define STRETCH 2
	register fnt_GlobalGraphicStateType *globalGS = GLOBALGSTATE(key);
	transMatrix 						*trans	  = &key->currentTMatrix;
	int32 pixelsPerEm = FIXROUND( key->interpScalar );

	key->phaseShift = false;
	key->imageState = pixelsPerEm > 0xFF ? 0xFF : pixelsPerEm;
	if ( !(key->identityTransformation = fsg_Identity( trans )) )
	{
		fsg_GetMatrixStretch( key, &globalGS->xStretch, &globalGS->yStretch, trans); /*<8>*/
		if( fsg_Non90Degrees( trans ) ) key->imageState |= ROTATED; 
	}
	else
	{
		globalGS->xStretch = ONEFIX;
		globalGS->yStretch = ONEFIX;
	}
	if(	globalGS->xStretch != ONEFIX || globalGS->yStretch != ONEFIX )
			key->imageState |= STRETCHED;
	globalGS->identityTransformation = key->identityTransformation;
	globalGS->non90DegreeTransformation = fsg_Non90Degrees( trans );
/* Use bit 1 of non90degreeTransformation to signify stretching.  stretch = 2 */
	if( trans->transform[0][0] == trans->transform[1][1] || trans->transform[0][0] == -trans->transform[1][1] )	
		globalGS->non90DegreeTransformation &= ~STRETCH;
	else globalGS->non90DegreeTransformation |= STRETCH;
 
}

#define CANTAKESHIFT	0x02000000
/*
 * fsg_ScaleCVT
 */
static void fsg_ScaleCVT( register fsg_SplineKey *key, int32 numCVT, register F26Dot6 *cvt, register int16 *srcCVT )
{
	/* N is the Nummerator, and D is the Denominator */
	/* V is used as a temporary Value */
	register int32 N, D, V;
	register F26Dot6 					*endCvt   = cvt + numCVT;
	register fnt_GlobalGraphicStateType *globalGS = GLOBALGSTATE(key);
	transMatrix 						*trans	  = &key->currentTMatrix;

	N = key->interpScalar;
	D = (int32)key->emResolution << 16;
	/* We would like D to be perfectly dividable by 2 */
	{
		int shift = fsg_CountLowZeros( N | D ) - 1;
		if (shift > 0)
		{
			N >>= shift;
			D >>= shift;
		}
	}

	if ( N < CANTAKESHIFT )
		N <<= fnt_pixelShift;
	else
		D >>= fnt_pixelShift;

	if ( FITSINAWORD( N ) )
	{
		register int16 	shift = fsg_GetShift( D );
		globalGS->nScale = N;
		globalGS->dScale = D;

	    if ( shift >= 0 ) {									/* FAST SCALE */
			globalGS->ScaleFunc 	= fnt_FRound;
			globalGS->shift 		= shift;
        	D >>= 1;
			for (; cvt < endCvt; cvt++ ) {
				V = *srcCVT++; FROUND( V, N, D, shift ); *cvt = V;
			}
		} else {											/* MEDIUM SCALE */
			register int32 dOver2 = D >> 1;
			globalGS->ScaleFunc 	= fnt_SRound;
			for (; cvt < endCvt; cvt++ ) {
				V = *srcCVT++; SROUND( V, N, D, dOver2 ); *cvt = V;
			}
		}
	} else {												/* SLOW SCALE */
		globalGS->ScaleFunc  = fnt_FixRound;
		globalGS->fixedScale = FixDiv( N, D );
		{
			int16 count = numCVT;
			Fixed scaler = globalGS->fixedScale;
			for (--count; count >= 0; --count)
				*cvt++ = FixMul( *srcCVT++, scaler );
		}
	}
}


/*
 *	fsg_PreTransformGlyph				<3>
 */
static void fsg_PreTransformGlyph( register fsg_SplineKey* key )
{
	fnt_ElementType	*elementPtr	= &(key->elementInfoRec.interpreterElements[key->elementNumber]);
    register int16 numPts = NUMBEROFPOINTS(elementPtr);
	register Fixed	scale;

	scale = key->tInfo.xScale;
	if ( scale != ONEFIX )
	{
		register int32* oox = elementPtr->oox;
		register int16 count = numPts;
		for ( --count; count >= 0; --count, oox++ )
			*oox = FixMul( scale, *oox );
	}

	scale = key->tInfo.yScale;
	if ( scale != ONEFIX )
	{
		register int32* ooy = elementPtr->ooy;
		register int16 count = numPts;
		for ( --count; count >= 0; --count, ooy++ )
			*ooy = FixMul( scale, *ooy );
	}
}

/*
 * Styles for Cary
 */
static void fsg_CallStyleFunc( register fsg_SplineKey* key, register fnt_ElementType* elementPtr )
{
	register fs_GlyphInfoType* outputPtr = key->outputPtr;
	
	outputPtr->outlinesExist = key->glyphLength != 0;
	outputPtr->xPtr			 = elementPtr->x;
	outputPtr->yPtr			 = elementPtr->y;
	outputPtr->startPtr		 = elementPtr->sp;
	outputPtr->endPtr		 = elementPtr->ep;
	outputPtr->onCurve		 = elementPtr->onCurve;
	outputPtr->numberOfContours = elementPtr->nc;
	key->styleFunc( key->outputPtr );
}


/*
 *	fsg_PostTransformGlyph				<3>
 */
static void fsg_PostTransformGlyph(register fsg_SplineKey *key, transMatrix *trans) 
{
	register int16 	numPts;
	fnt_ElementType	*elementPtr	= &(key->elementInfoRec.interpreterElements[key->elementNumber]);
	register Fixed 	xStretch, yStretch;
	register F26Dot6* x;
	register F26Dot6* y;

	if ( key->identityTransformation ) return;

	numPts = NUMBEROFPOINTS(elementPtr);
	
	xStretch = key->tInfo.xScale;
	yStretch = key->tInfo.yScale;

	x = elementPtr->x;
	y = elementPtr->y;

	if ( xStretch == 0L || yStretch == 0L )
	{
		register F26Dot6 zero = 0;
		LOOPDOWN(numPts)
			*y++ = *x++ = zero;
	}
	else if ( !key->styleFunc )
	{
		if (fsg_HasPerspective( trans ))	/* undo stretch, then apply matrix */
		{
			LOOPDOWN(numPts)
			{
				*x = FixDiv( *x, xStretch );
				*y = FixDiv( *y, yStretch );;
				fsg_Dot6XYMul( x++, y++, trans );
			}
		}
		else	/* unroll the common case.  Fold stretch-undo and matrix together <4> */
		{
			Fixed m00 = FixDiv(trans->transform[0][0], xStretch);
			Fixed m01 = FixDiv(trans->transform[0][1], xStretch);
			Fixed m10 = FixDiv(trans->transform[1][0], yStretch);
			Fixed m11 = FixDiv(trans->transform[1][1], yStretch);
			LOOPDOWN(numPts)
			{
				register Fixed origx = *x;
				register Fixed origy = *y;
				*x++ = FixMul(m00, origx) + FixMul(m10, origy);
				*y++ = FixMul(m01, origx) + FixMul(m11, origy);
			}
		}
	}
	else	/* undo stretch, call stylefunc, then apply matrix */
	{
		register int16 count = numPts;
		LOOPDOWN(count)
		{
			*x = FixDiv( *x, xStretch );
			++x;
			*y = FixDiv( *y, yStretch );
			++y;
		}

		fsg_CallStyleFunc( key, elementPtr );	/* does this still work??? */

		x = elementPtr->x;
		y = elementPtr->y;
		LOOPDOWN(numPts)
			fsg_Dot6XYMul( x++, y++, trans );
	}
/*<8> take out contour reversal */
}

/*
 *	fsg_LocalPostTransformGlyph				<3>
 *
 * (1) Inverts the stretch from the CTM
 * (2) Applies the local transformation passed in in the trans parameter
 * (3) Applies the global stretch from the root CTM
 * (4) Restores oox, ooy, oy, ox, and f.
 */
static void fsg_LocalPostTransformGlyph(register fsg_SplineKey *key, transMatrix *trans) 
{
    register int16		numPts, count;
	register Fixed 		xScale, yScale;
	register F26Dot6*	x;
	register F26Dot6*	y;
	fnt_ElementType*	elementPtr = &(key->elementInfoRec.interpreterElements[key->elementNumber]);

	if ( !fsg_MoreThanXYStretch( trans ) ) return;

	numPts = count = NUMBEROFPOINTS(elementPtr);
	
	xScale = key->tInfo.xScale;
	yScale = key->tInfo.yScale;
	x = elementPtr->x;
	y = elementPtr->y;

	if ( xScale == 0L || yScale == 0L )
	{
		register F26Dot6 zero = 0;
		LOOPDOWN(numPts)
			*x++ = *y++ = zero;
	}
	else
	{
		LOOPDOWN(numPts)
		{
			*x = FixDiv( *x, xScale );
			*y = FixDiv( *y, yScale );
			fsg_Dot6XYMul( x++, y++, trans );
		}
	}

	xScale = key->globalTInfo.xScale;
	yScale = key->globalTInfo.yScale;
	
	if ( xScale != ONEFIX )
	{
		x = elementPtr->x;
		numPts = count;
		LOOPDOWN(numPts)
		{
			*x = FixMul( xScale, *x );
			x++;
		}
	}
	if ( yScale != ONEFIX )
	{
		y = elementPtr->y;
		numPts = count;
		LOOPDOWN(numPts)
		{
			*y = FixMul( yScale, *y );
			y++;
		}
	}

	fsg_CopyElementBackwards( &(key->elementInfoRec.interpreterElements[GLYPHELEMENT]) );
	x = elementPtr->oox;
	y = elementPtr->ooy;
	LOOPDOWN(count)
		fsg_Dot6XYMul( x++, y++, trans );
}


/*
 *	fsg_ShiftChar
 *
 *	Shifts a character			<3>
 */
static void fsg_ShiftChar(register fsg_SplineKey *key, register F26Dot6 xShift, register F26Dot6 yShift, int32 lastPoint) 
{
	fnt_ElementType *elementPtr	= &(key->elementInfoRec.interpreterElements[key->elementNumber]);

	if ( xShift )
	{
		register F26Dot6* x = elementPtr->x;
		register LoopCount loop;
		for (loop = lastPoint; loop >= 0; --loop)
			*x++ += xShift;
	}
	if ( yShift )
	{
		register F26Dot6* y = elementPtr->y;
		register F26Dot6* lasty = y + lastPoint;
		while (y <= lasty)
			*y++ += yShift;
	}
}

/*
 *	fsg_ScaleChar						<3>
 *
 *	Scales a character and the advancewidth + leftsidebearing.
 */
static void fsg_ScaleChar( register fsg_SplineKey *key  )
{
	fnt_ElementType			*elementPtr	= &(key->elementInfoRec.interpreterElements[key->elementNumber]);
	fnt_GlobalGraphicStateType *globalGS = GLOBALGSTATE(key);
	int16 numPts = NUMBEROFPOINTS(elementPtr);

	if ( globalGS->ScaleFunc == fnt_FRound )
	{
		register int32*	oop = elementPtr->oox;
		register F26Dot6* p = elementPtr->x;
		register int16 shift = globalGS->shift;
		register int32 N = globalGS->nScale;
		register int32 D = globalGS->dScale >> 1;
		register int32 V;
		register int16 count = numPts;		/* do I get this many registers? */
		LOOPDOWN(count)
		{
			V = *oop++; FROUND( V, N, D, shift ); *p++ = V;
		}
		p = elementPtr->y;
		oop = elementPtr->ooy;
		count = numPts;
		LOOPDOWN(count)
		{
			V = *oop++; FROUND( V, N, D, shift ); *p++ = V;
		}
	}
	else if ( globalGS->ScaleFunc == fnt_SRound )
	{
		register int32*	oop = elementPtr->oox;
		register F26Dot6* p = elementPtr->x;
		register int32 N = globalGS->nScale;
		register int32 D = globalGS->dScale;
		register int32 V;
		register int32 dOver2 = D >> 1;
		register int16 count = numPts;
		LOOPDOWN(count)
		{
			V = *oop++; SROUND( V, N, D, dOver2 ); *p++ = V;
		}
		
		p = elementPtr->y;
		oop = elementPtr->ooy;
		count = numPts;
		LOOPDOWN(count)
		{
			V = *oop++; SROUND( V, N, D, dOver2 ); *p++ = V;
		}
	}
	else
	{
		register int32*	oop = elementPtr->oox;
		register F26Dot6* p = elementPtr->x;
	    register int32 N = globalGS->fixedScale;
		register int16 count = numPts;
		LOOPDOWN(count)
			*p++ = FixMul( *oop++, N );
		p = elementPtr->y;
		oop = elementPtr->ooy;
		count = numPts;
		LOOPDOWN(count)
			*p++ = FixMul( *oop++, N );
	}
}

/*
 *	fsg_SetUpElement
 */
void fsg_SetUpElement(fsg_SplineKey *key, int32 n) 
{
	register int8				*workSpacePtr	= key->memoryBases[WORK_SPACE_BASE];
	register fnt_ElementType 	*elementPtr;
	register fsg_OffsetInfo		*offsetPtr;


	offsetPtr			= &(key->elementInfoRec.offsets[n]);
	elementPtr			= &(key->elementInfoRec.interpreterElements[n]);

	
    elementPtr->x		= (int32 *)(workSpacePtr + offsetPtr->newXOffset);
    elementPtr->y		= (int32 *)(workSpacePtr + offsetPtr->newYOffset);
    elementPtr->ox		= (int32 *)(workSpacePtr + offsetPtr->scaledXOffset);
    elementPtr->oy		= (int32 *)(workSpacePtr + offsetPtr->scaledYOffset);
	elementPtr->oox		= (int32 *)(workSpacePtr + offsetPtr->oldXOffset);
	elementPtr->ooy		= (int32 *)(workSpacePtr + offsetPtr->oldYOffset);
    elementPtr->sp		= (int16 *)(workSpacePtr + offsetPtr->startPointOffset);
    elementPtr->ep		= (int16 *)(workSpacePtr + offsetPtr->endPointOffset);
    elementPtr->onCurve	= (uint8 *)(workSpacePtr + offsetPtr->onCurveOffset);
    elementPtr->f     	= (uint8 *)(workSpacePtr + offsetPtr->interpreterFlagsOffset);
	if ( n == TWILIGHTZONE ) {
		/* register int i, j; */
		elementPtr->sp[0]	= 0;	
		elementPtr->ep[0]	= key->maxProfile.maxTwilightPoints-1;
	    elementPtr->nc		= MAX_TWILIGHT_CONTOURS;
	}
}


/*
 *	fsg_IncrementElement
 */
void fsg_IncrementElement(fsg_SplineKey *key, int32 n, int32 numPoints, int32 numContours) 
{
	register fnt_ElementType 	*elementPtr;

	elementPtr			= &(key->elementInfoRec.interpreterElements[n]);

	
    elementPtr->x		+= numPoints;
    elementPtr->y		+= numPoints;
    elementPtr->ox		+= numPoints;
    elementPtr->oy		+= numPoints;
	elementPtr->oox		+= numPoints;
	elementPtr->ooy		+= numPoints;
    elementPtr->sp		+= numContours;
    elementPtr->ep		+= numContours;
    elementPtr->onCurve	+= numPoints;
    elementPtr->f     	+= numPoints;

}


/*
 * fsg_ZeroOutTwilightZone			<3>
 */
static void fsg_ZeroOutTwilightZone(fsg_SplineKey *key) 
{
	register int16 origCount = key->maxProfile.maxTwilightPoints;
	register F26Dot6 zero = 0;
	fnt_ElementType*  elementPtr = &(key->elementInfoRec.interpreterElements[TWILIGHTZONE]);
	{
		register F26Dot6* x = elementPtr->x;
		register F26Dot6* y = elementPtr->y;
		register int16 count = origCount;
		LOOPDOWN(count)
		{
			*x++ = zero;
			*y++ = zero;
		}
	}
	{
		register F26Dot6* ox = elementPtr->ox;
		register F26Dot6* oy = elementPtr->oy;
		LOOPDOWN(origCount)
		{
			*ox++ = zero;
			*oy++ = zero;
		}
	}
}

/*
 *	Assign pgmList[] for each pre program
 */
static void fsg_SetUpProgramPtrs(fsg_SplineKey* key, fnt_GlobalGraphicStateType* globalGS)
{
	switch (globalGS->pgmIndex) {
	case PREPROGRAM:
		globalGS->pgmList[PREPROGRAM] = (uint8*)sfnt_GetTablePtr(key, sfnt_preProgram, false);
	case FONTPROGRAM:
		globalGS->pgmList[FONTPROGRAM] = (uint8*)sfnt_GetTablePtr(key, sfnt_fontProgram, false);
	}
#ifdef DEBUG
	globalGS->maxp = &key->maxProfile;
	globalGS->cvtCount = key->cvtCount;
#endif
}

/*
 *	Release pgmList[] for each pre program.
 *	Important that the order be reversed from SetUpProgramPtrs
 *		so that memory fragments are allocated and release in stack order
 */
#ifdef RELEASE_MEM_FRAG
static void fsg_ReleaseProgramPtrs(fsg_SplineKey* key, fnt_GlobalGraphicStateType* globalGS)
{
	switch (globalGS->pgmIndex) {
	case FONTPROGRAM:
		SOFTRELEASESFNTFRAG(key, globalGS->pgmList[FONTPROGRAM]);				/* <12-jlf> */
		break;
	case PREPROGRAM:
		SOFTRELEASESFNTFRAG(key, globalGS->pgmList[FONTPROGRAM]);				/* <12-jlf> */
		SOFTRELEASESFNTFRAG(key, globalGS->pgmList[PREPROGRAM]);				/* <12-jlf> */
	}
}
#endif

static void fsg_SetUpTablePtrs(fsg_SplineKey* key)
{
	char** memoryBases = key->memoryBases;
	char* private_FontSpacePtr = memoryBases[PRIVATE_FONT_SPACE_BASE];
	fnt_GlobalGraphicStateType* globalGS = GLOBALGSTATE(key);
	
	switch (globalGS->pgmIndex) {
	case PREPROGRAM:
		globalGS->controlValueTable	= (F26Dot6*)(private_FontSpacePtr + key->offset_controlValues);
	case FONTPROGRAM:
		globalGS->store				= (F26Dot6*)(private_FontSpacePtr + key->offset_storage);
		globalGS->funcDef			= (fnt_funcDef*)(private_FontSpacePtr + key->offset_functions);
		globalGS->instrDef			= (fnt_instrDef*)(private_FontSpacePtr + key->offset_instrDefs);
		globalGS->stackBase			= (F26Dot6*)(memoryBases[WORK_SPACE_BASE] + key->elementInfoRec.stackBaseOffset);
		globalGS->function			= (FntFunc*)(memoryBases[VOID_FUNC_PTR_BASE]);
	}
}

/*
 * fsg_RunPreProgram
 *
 * Runs the pre-program and scales the control value table
 *
 */
static int fsg_RunPreProgram(register fsg_SplineKey *key, voidFunc traceFunc) 
{
	int32	offsetT;
	unsigned lengthT;
	int result;
	uint8* private_FontSpacePtr = (uint8*)key->memoryBases[PRIVATE_FONT_SPACE_BASE];
	F26Dot6* cvt = (F26Dot6*)(private_FontSpacePtr + key->offset_controlValues);

	fnt_GlobalGraphicStateType	*globalGS = GLOBALGSTATE(key);
	int32 numCvt;
	sfnt_ControlValue* cvtSrc = (sfnt_ControlValue*)sfnt_GetTablePtr( key, sfnt_controlValue, false );

	sfnt_GetOffsetAndLength( key, &offsetT, &lengthT, sfnt_controlValue );
	numCvt = lengthT / sizeof(sfnt_ControlValue);
	
	/* Set up the engine compensation array for the interpreter */
	/* This will be indexed into by the booleans in some instructions */
	globalGS->engine[0] = globalGS->engine[3] = 0;							/* Grey and ? distance */
	globalGS->engine[1] = FIXEDTODOT6(FIXEDSQRT2 - key->pixelDiameter);		/* Black distance */
	globalGS->engine[2] = -globalGS->engine[1];								/* White distance */

	globalGS->init 			= true;
	globalGS->pixelsPerEm	= FIXROUND( key->interpScalar );
	globalGS->pointSize		= FIXROUND( key->fixedPointSize );
	globalGS->fpem			= key->interpScalar;
	if( result = fsg_SetDefaults( key )) return result;		/* Set graphic state to default values */
	globalGS->localParBlock = globalGS->defaultParBlock;	/* copy gState parameters */

	key->globalTInfo.xScale = key->tInfo.xScale = globalGS->xStretch;
	key->globalTInfo.yScale = key->tInfo.yScale = globalGS->yStretch;

	fsg_ScaleCVT( key, numCvt, cvt, cvtSrc );

	SOFTRELEASESFNTFRAG(key, cvtSrc);						/* <12-jlf> */

	globalGS->pgmIndex = PREPROGRAM;
	fsg_SetUpProgramPtrs(key, globalGS);
	sfnt_GetOffsetAndLength( key, &offsetT, &lengthT, sfnt_preProgram );

	/** TWILIGHT ZONE ELEMENT **/
	fsg_SetUpElement( key, TWILIGHTZONE );
	fsg_ZeroOutTwilightZone( key );

	fsg_SetUpTablePtrs(key);
#ifdef DEBUG
	globalGS->glyphProgram = false;
#endif
	result = fnt_Execute( key->elementInfoRec.interpreterElements, globalGS->pgmList[PREPROGRAM],
							globalGS->pgmList[PREPROGRAM] + lengthT, globalGS, traceFunc );
	
	if( !(globalGS->localParBlock.instructControl & DEFAULTFLAG) )
		globalGS->defaultParBlock = globalGS->localParBlock;	/* change default parameters */
#ifdef RELEASE_MEM_FRAG
	fsg_ReleaseProgramPtrs(key, globalGS);
#endif

	return result;
}

/*
 *	All this guy does is record FDEFs and IDEFs, anything else is ILLEGAL
 */
int fsg_RunFontProgram( fsg_SplineKey* key, voidFunc traceFunc )
{
	int32 offsetT;
	unsigned lengthT;
	int result;
	fnt_GlobalGraphicStateType *globalGS = GLOBALGSTATE(key);

	globalGS->instrDefCount = 0;		/* none allocated yet, always do this, even if there's no fontProgram */
	
	sfnt_GetOffsetAndLength( key, &offsetT, &lengthT, sfnt_fontProgram );
	if (lengthT)
	{
		globalGS->pgmIndex = FONTPROGRAM;
		fsg_SetUpProgramPtrs(key, globalGS);
		fsg_SetUpTablePtrs(key);
#ifdef DEBUG
		globalGS->glyphProgram = false;
#endif
		result = fnt_Execute( key->elementInfoRec.interpreterElements, globalGS->pgmList[FONTPROGRAM],
			globalGS->pgmList[FONTPROGRAM] + lengthT, globalGS, traceFunc );
#ifdef RELEASE_MEM_FRAG
		fsg_ReleaseProgramPtrs(key, globalGS);
#endif

		return result;
	}
	return NO_ERR;
}

/* Set default values for all variables in globalGraphicsState DefaultParameterBlock
 *	Eventually, we should provide for a Default preprogram that could optionally be 
 *	run at this time to provide a different set of default values.
 */
int fsg_SetDefaults( fsg_SplineKey* key )
{
	register fnt_GlobalGraphicStateType *globalGS = GLOBALGSTATE(key);
	register fnt_ParameterBlock *par = &globalGS->defaultParBlock;
	
	par->RoundValue  = fnt_RoundToGrid;
	par->minimumDistance = fnt_pixelSize;
	par->wTCI = fnt_pixelSize * 17 / 16;
	par->sWCI = 0;
	par->sW   = 0;
	par->autoFlip = true;
	par->deltaBase = 9;
	par->deltaShift = 3;
	par->scanControl = 0;
	par->instructControl = 0;
	return 0;
}

/* 
 * Runs the pre program and scales the control value table
 */
int fsg_NewTransformation(register fsg_SplineKey *key, voidFunc traceFunc) 
{
	/* Run the pre program and scale the control value table */
	key->executePrePgm = false;
	return fsg_RunPreProgram( key, traceFunc );
}

/*
 *	fsg_InnerGridFit
 */
int fsg_InnerGridFit(register fsg_SplineKey *key, int16 useHints, voidFunc traceFunc, 
	sfnt_BBox *bbox, int32 sizeOfInstructions, uint8 *instructionPtr, int finalCompositePass) 
{
	fnt_GlobalGraphicStateType* globalGS = GLOBALGSTATE(key);

	register fnt_ElementType 	*elementPtr;

	/* this is so we can allow recursion */
	int32 *save_x, *save_y, *save_ox, *save_oy, *save_oox, *save_ooy;
	int16 *save_sp, *save_ep;
	uint8 *save_onCurve, *save_f;
	int16 save_nc, numPts;
	
	elementPtr = &(key->elementInfoRec.interpreterElements[GLYPHELEMENT]);
	if ( finalCompositePass ) {
		/* save stuff we are going to muck up below, so we can recurse */
		save_x 			= elementPtr->x;
		save_y 			= elementPtr->y;
		save_ox 		= elementPtr->ox;
		save_oy 		= elementPtr->oy;
		save_oox 		= elementPtr->oox;
		save_ooy 		= elementPtr->ooy;
		save_sp 		= elementPtr->sp;
		save_ep 		= elementPtr->ep;
		save_onCurve 	= elementPtr->onCurve;
		save_f 			= elementPtr->f;
		save_nc 		= elementPtr->nc;
	
		elementPtr->nc = key->totalContours;
		fsg_SetUpElement( key, GLYPHELEMENT ); /* Set it up again so we can process as one glyph */
	}
	
	key->elementNumber = GLYPHELEMENT;
	numPts = NUMBEROFPOINTS(elementPtr);
	{
		F26Dot6 xMinMinusLSB = bbox->xMin - key->nonScaledLSB;

		/* left side bearing point */
		elementPtr->oox[numPts-PHANTOMCOUNT+LEFTSIDEBEARING] = xMinMinusLSB;
		elementPtr->ooy[numPts-PHANTOMCOUNT+LEFTSIDEBEARING] = 0;
		
		/* origin left side bearing point */
		elementPtr->oox[numPts-PHANTOMCOUNT+ORIGINPOINT] = xMinMinusLSB;
		elementPtr->ooy[numPts-PHANTOMCOUNT+ORIGINPOINT] = 0;
		
		/* left edge point */
		elementPtr->oox[numPts-PHANTOMCOUNT+LEFTEDGEPOINT] = bbox->xMin;
		elementPtr->ooy[numPts-PHANTOMCOUNT+LEFTEDGEPOINT] = 0;
		
		/* right side bearing point */
		elementPtr->oox[numPts-PHANTOMCOUNT+RIGHTSIDEBEARING] = xMinMinusLSB + key->nonScaledAW;
		elementPtr->ooy[numPts-PHANTOMCOUNT+RIGHTSIDEBEARING] = 0;
	}
	key->tInfo.xScale = globalGS->xStretch;
	key->tInfo.yScale = globalGS->yStretch;

		/* Pretransform, scale, and copy */
	if ( finalCompositePass)
	{
		register GlobalGSScaleFunc ScaleFunc = globalGS->ScaleFunc;
		register int32 j;
		register Fixed scale = key->tInfo.xScale;

		for ( j = numPts - PHANTOMCOUNT; j < numPts; j++ )
		{
			if (scale != ONEFIX)
				elementPtr->oox[j] = FixMul( scale, elementPtr->oox[j] );
			elementPtr->ox[j]  = ScaleFunc( globalGS, elementPtr->oox[j] );
			elementPtr->x[j]   = elementPtr->ox[j];
		}
		scale = key->tInfo.yScale;
		for ( j = numPts - PHANTOMCOUNT; j < numPts; j++ )
		{
			if (scale != ONEFIX)
				elementPtr->ooy[j] = FixMul( scale, elementPtr->ooy[j] );
			elementPtr->oy[j]  = ScaleFunc( globalGS, elementPtr->ooy[j] );
			elementPtr->y[j]   = elementPtr->oy[j];
		}
	}
	else
	{
		fsg_PreTransformGlyph( key );
		fsg_ScaleChar( key );
	}
	
	if ( useHints ) { /* AutoRound */
		int32 oldLeftOrigin, newLeftOrigin, xShift;

		newLeftOrigin = oldLeftOrigin = elementPtr->x[numPts-PHANTOMCOUNT+LEFTSIDEBEARING];
		newLeftOrigin += fnt_pixelSize/2; /* round to a pixel boundary */
		newLeftOrigin &= ~(fnt_pixelSize-1);
		xShift = newLeftOrigin - oldLeftOrigin;
		
		if ( !finalCompositePass ) {
			/* We can't shift if it is the final composite pass */
			fsg_ShiftChar( key, xShift, 0L, NUMBEROFPOINTS(elementPtr) - 1 );
			/* Now the distance from the left origin point to the character is exactly lsb */
		}
		fsg_CopyElementBackwards( elementPtr );
		/* Fill in after fsg_ShiftChar(), since this point should not be shifted. */
		elementPtr->x[numPts-PHANTOMCOUNT+LEFTSIDEBEARING]  = newLeftOrigin;
		elementPtr->ox[numPts-PHANTOMCOUNT+LEFTSIDEBEARING] = newLeftOrigin;
		{
			F26Dot6 width = ShortMulDiv( key->interpScalar,
							elementPtr->oox[numPts-PHANTOMCOUNT+RIGHTSIDEBEARING]
							- elementPtr->oox[numPts-PHANTOMCOUNT+LEFTSIDEBEARING],
							key->emResolution ) + (1L << 9) >> 10;
			elementPtr->x[numPts-PHANTOMCOUNT+RIGHTSIDEBEARING] = 
			elementPtr->x[numPts-PHANTOMCOUNT+LEFTSIDEBEARING] + (width + (1 << 5)) & ~63;
		}
	}

	globalGS->init			= false;
	globalGS->pixelsPerEm	= FIXROUND( key->interpScalar );
	globalGS->pointSize		= FIXROUND( key->fixedPointSize );
	globalGS->fpem			= key->interpScalar;
	globalGS->localParBlock = globalGS->defaultParBlock;	/* default parameters for glyphs */
	if ( useHints && sizeOfInstructions > 0 )
	{
		int32 result;

		if ( finalCompositePass )
			fsg_SnapShotOutline( key, elementPtr, numPts );

		globalGS->pgmIndex = PREPROGRAM;
		fsg_SetUpProgramPtrs(key, globalGS);
		fsg_SetUpTablePtrs(key);
#ifdef DEBUG
		globalGS->glyphProgram = true;
#endif
		result = fnt_Execute( key->elementInfoRec.interpreterElements, instructionPtr, 
					 instructionPtr + sizeOfInstructions, globalGS, traceFunc );
#ifdef RELEASE_MEM_FRAG
		fsg_ReleaseProgramPtrs(key, globalGS);
#endif
		if (result)
			return result;
	}
	/* Now make everything into one big glyph. */
	if ( key->weGotComponents  && !finalCompositePass ) {
		int16 n, ctr;
		int32 xOffset, yOffset;	

		/* Fix start points and end points */
		n = 0;
		if ( key->totalComponents != GLYPHELEMENT )
			n += elementPtr->ep[-1] + 1; /* number of points */

		if ( !key->localTIsIdentity )
			fsg_LocalPostTransformGlyph( key, &key->localTMatrix );

		if ( key->compFlags & ARGS_ARE_XY_VALUES ) {
			xOffset = key->arg1;
			yOffset = key->arg2;
			xOffset = globalGS->ScaleFunc( globalGS, xOffset );
			yOffset = globalGS->ScaleFunc( globalGS, yOffset );
			
			
			if ( !key->identityTransformation ) {
				/* transform offsets into this funky domain */
				xOffset = FixMul( globalGS->xStretch, xOffset );			
				yOffset = FixMul( globalGS->yStretch, yOffset );			
			}
			
			if ( key->compFlags & ROUND_XY_TO_GRID ) {
				xOffset += fnt_pixelSize/2; xOffset &= ~(fnt_pixelSize-1);
				yOffset += fnt_pixelSize/2; yOffset &= ~(fnt_pixelSize-1);
			}
		} else {
			xOffset = elementPtr->x[ key->arg1 - n ] - elementPtr->x[ key->arg2 ];
			yOffset = elementPtr->y[ key->arg1 - n ] - elementPtr->y[ key->arg2 ];
		}
		
		/* shift all the points == Align the component */
		fsg_ShiftChar( key, xOffset, yOffset, NUMBEROFPOINTS(elementPtr) - 1 );

		/*
		 *	Remember this component's phantom points after we've munged it
		 */
		if (key->compFlags & USE_MY_METRICS)
		{
			ArrayIndex index = elementPtr->ep[ elementPtr->nc - 1 ] + 1;
			F26Dot6* x = &elementPtr->x[ index ];
			F26Dot6* y = &elementPtr->y[ index ];
			key->devLSB.x = *x++;
			key->devLSB.y = *y++;
			key->devRSB.x = *x;
			key->devRSB.y = *y;
			key->useMyMetrics = true;
		}

		/* reverse contours if needed; <8> not any more */

		if ( key->totalComponents != GLYPHELEMENT ) {
			/* Fix start points and end points */
			for ( ctr = 0; ctr < elementPtr->nc; ctr++ ) {
				elementPtr->sp[ctr] += n;			
				elementPtr->ep[ctr] += n;
			}
		}
	}
	
	if ( finalCompositePass )
	{
		/*
		 *	This doesn't work recursively yet, but then again, nothing does.
		 */
		if (key->useMyMetrics)
		{
			ArrayIndex index = elementPtr->ep[ elementPtr->nc - 1 ] + 1;
			F26Dot6* x = &elementPtr->x[ index ];
			F26Dot6* y = &elementPtr->y[ index ];
			*x++ = key->devLSB.x;
			*y++ = key->devLSB.y;
			*x = key->devRSB.x;
			*y = key->devRSB.y;
			key->useMyMetrics = false;
		}
		elementPtr->x		= save_x;
		elementPtr->y		= save_y;
		elementPtr->ox		= save_ox;
		elementPtr->oy		= save_oy;
		elementPtr->oox		= save_oox;
		elementPtr->ooy		= save_ooy;
		elementPtr->sp		= save_sp;
		elementPtr->ep		= save_ep;
		elementPtr->onCurve	= save_onCurve;
		elementPtr->f     	= save_f;
		elementPtr->nc		= save_nc;
	}
	key->scanControl = globalGS->localParBlock.scanControl; /*rwb */
	return NO_ERR;
}


/*
 * Internal routine.	changed to use pointer			<3>
 */
static void fsg_InitLocalT( fsg_SplineKey* key )
{
	register Fixed* p = &key->localTMatrix.transform[0][0];
	register Fixed one = ONEFIX;
	register Fixed zero = 0;
	*p++ = one;
	*p++ = zero;
	*p++ = zero;
	
	*p++ = zero;
	*p++ = one;
	*p++ = zero;
	
	*p++ = zero;
	*p++ = zero;
	*p   = one;		/* Internal routines assume ONEFIX here, though client assumes ONEFRAC */
}

/*
 *	fsg_GridFit
 */
int fsg_GridFit( register fsg_SplineKey* key, voidFunc traceFunc, boolean useHints )
{
 	int	result;
	int16	elementCount;
	fnt_GlobalGraphicStateType* globalGS = GLOBALGSTATE(key);
	
	fsg_SetUpElement( key, TWILIGHTZONE );/* TWILIGHT ZONE ELEMENT */

	elementCount = GLYPHELEMENT;

	key->weGotComponents = false;
	key->compFlags = NON_OVERLAPPING;
	key->useMyMetrics = false;

	/* This also calls fsg_InnerGridFit() .*/
	if( globalGS->localParBlock.instructControl & NOGRIDFITFLAG ) useHints = false;
	key->localTIsIdentity = true;
	fsg_InitLocalT( key );
	if ( (result = sfnt_ReadSFNT( key, &elementCount, key->glyphIndex, useHints, traceFunc )) == NO_ERR )
	{		
		key->elementInfoRec.interpreterElements[GLYPHELEMENT].nc = key->totalContours;
	
		if ( key->weGotComponents )
			fsg_SetUpElement( key, GLYPHELEMENT ); /* Set it up again so we can transform */
	
		fsg_PostTransformGlyph( key, &key->currentTMatrix );
	}
	return result;
}
