/*	
    access_library.c	1.2 
  
    Copyright 1997 Willows Software, Inc. 

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
Cambridge, MA 02139, USA.

The maintainer of the Willows TWIN Libraries may be reached (Email) 
at the address twin@willows.com	

*/


#include <Memory.h>
#include <Fonts.h>
#include <Resources.h>
#include <QuickDraw.h>
#include <FixMath.h>
#include <ToolUtils.h>

#include "access library.h"

void AppendHandle(Handle dst, Handle extra);
Handle GetSfntFromFOND(Handle fond, short styleWord);


Handle GetNamedSfntHandle(unsigned char* name, short styleWord)
{
	Handle fond = GetNamedResource('FOND', name);
	if (fond && !ResError()) return(GetSfntFromFOND(fond, styleWord));
#ifdef	DEBUG
	DebugStr("\pGetNamedSfntHandle(): Unable to get named font");
#endif	//	DEBUG
	return 0;
}

Handle GetSfntHandle(short fondID, register short styleWord)
{
	Handle fond = GetResource('FOND', fondID);
	if (fond && !ResError()) return(GetSfntFromFOND(fond, styleWord));
#ifdef	DEBUG
	DebugStr("\pGetSfntHandle(): Unable to get font");
#endif	//	DEBUG
	return 0;
}

Handle GetSfntFromFOND(Handle fond, short styleWord)
{
	Handle workingFOND = fond;

	do{
		FamRec* frec = (FamRec*)*workingFOND;
		register short* assoc = (short*)(frec + 1);
		register short sfntID = 0;
		register count = *assoc++;

		while (count-- >= 0 && !sfntID) {
		if (*assoc++ == 0)		/* size == 0 means sfnt */
			if (*assoc++ == styleWord)
				sfntID = *assoc;
			else
				assoc++;
		else
			assoc += 2;
		}
		if (sfntID) {
			Handle sfnt = GetResource('sfnt', sfntID);
			if (sfnt && !ResError())
				return sfnt;
		}
	} while ((workingFOND = GetNextFOND(workingFOND)) != NULL);
#ifdef	DEBUG
	DebugStr("\pGetSfntFromFOND(): Unable to get font");
#endif	//	DEBUG
	return((Handle)NULL);
}

FontError InitGlyphOutline(GlyphOutline* out)
{
	out->contourCount = 0;
	out->pointCount = 0;
	out->endPoints = (short**)NewHandle(0);
	out->onCurve = (Byte**)NewHandle(0);
	out->x = (Fixed**)NewHandle(0);
	out->y = (Fixed**)NewHandle(0);
	return( fNoError );
}

FontError KillGlyphOutline(GlyphOutline* out)
{
	DisposeHandle((Handle)out->endPoints);
	DisposeHandle((Handle)out->onCurve);
	DisposeHandle((Handle)out->x);
	DisposeHandle((Handle)out->y);

	return fNoError;
}

void LockGlyphOutline(GlyphOutline* out)
{
	HLock((Handle)out->endPoints);
	HLock((Handle)out->onCurve);
	HLock((Handle)out->x);
	HLock((Handle)out->y);
}

void UnlockGlyphOutline(GlyphOutline* out)
{
	HUnlock((Handle)out->endPoints);
	HUnlock((Handle)out->onCurve);
	HUnlock((Handle)out->x);
	HUnlock((Handle)out->y);
}

void MoveGlyphOutline( GlyphOutline* out, Fixed xDelta, Fixed yDelta )
{	
	Fixed* x = *out->x;
	Fixed* y = *out->y;
	short count = out->pointCount;

	for (--count; count >= 0; --count)
	{
		*x++ += xDelta;
		*y++ += yDelta;
	}
	out->origin.x += xDelta;
	out->origin.y += yDelta;
}

void MoveToGlyphOutline(GlyphOutline* out, Fixed xCoord, Fixed yCoord )
{
	MoveGlyphOutline(out, xCoord - out->origin.x, yCoord - out->origin.y );
}

void ScaleGlyphOutline(GlyphOutline* out, Fixed xScale, Fixed yScale)
{	
	Fixed* x = *out->x;
	Fixed* y = *out->y;
	short count = out->pointCount;

	for (--count; count >= 0; --count)
	{	*x = FixMul( *x, xScale );
		x++;
		*y = FixMul( *y, yScale );
		y++;
	}
	out->origin.x = FixMul( out->origin.x, xScale );
	out->origin.y = FixMul( out->origin.y, yScale );
	out->advance.x = FixMul( out->advance.x, xScale );
	out->advance.y = FixMul( out->advance.y, yScale );
}

void InitMatrix(Matrix mat)
{
	mat[0][0] = mat[1][1] = mat[2][2] = 0x10000;
	mat[0][1] = mat[0][2] = mat[1][0] = mat[1][2] = mat[2][0] = mat[2][1] = 0;
}


//
//	MapGlyphOutline():
//
//	This routine transformas all of the points on the outline out using the matrix mat which
//	a 3x3 matrix.  Typically the Z coordinates are the identity and don't affect the
//	calculations.  The new boundaries and origin of the glyph are also determined.  The glyph
//	is rotated around the glyph orign which is specified in out->origin.  Therfore, all points
//	are moved before being transformed.  After each point is transformed, the new boundary is
//	calculated and all points are moved again ensuring that they exist in the positive X and
//	positive Y quadrant.  The new origin is determined from this last point shift.
//

void MapGlyphOutline(GlyphOutline* out, Matrix mat, Rect* bounds, Point* origin )
{
	Fixed*	x = *out->x;
	Fixed*	y = *out->y;
	short	count = out->pointCount;
	Fixed	transX = mat[2][0] - out->origin.x;		//	determine how the rotation origin
	Fixed	transY = mat[2][1] - out->origin.y;		//	"                               "
	Fixed	lowX = 0x7FFFFFFF;
	Fixed	lowY = 0x7FFFFFFF;
	Fixed	hiX = 0x80000000;
	Fixed	hiY = 0x80000000;

	for (--count; count >= 0; --count)
	{	Fixed* m0 = &mat[0][0];
		Fixed* m1 = &mat[1][0];
		Fixed xTemp = *x + transX;	//	move each point to rotate around the glyph origin
		Fixed yTemp = *y + transY;	//	"                                               "

//
//	Transform each point
//
		*x = FixMul(*m0++, xTemp) + FixMul(*m1++, yTemp);
		*y = FixMul(*m0++, xTemp) + FixMul(*m1++, yTemp);

//
//	Add in the Z plane computation (normally the identity)
//
		if (*m0 || *m1 || mat[2][2] != 0x10000) {	
			Fixed tmp = FracMul(*m0, xTemp) + FracMul(*m1, yTemp);
			tmp += mat[2][2];
			if (tmp && tmp != 0x10000)
			{	*x = FixDiv(*x, tmp);
				*y = FixDiv(*y, tmp);
			}
		}
//
//	Calculate the min and max for the boundaries
//
		if ( *x < lowX )
			lowX = *x;
		if ( *x > hiX )
			hiX = *x;
		if ( *y < lowY )
			lowY = *y;
		if ( *y > hiY )
			hiY = *y;
		x++;
		y++;
	}

//
//	Move all of the points and the bounds into positive X,Y quadrant
//
	count = out->pointCount;
	x = *out->x;
	y = *out->y;
	for ( --count; count >= 0; --count ) {
		*x++ -= lowX;
		*y++ -= lowY;
	}

//
//	Store the boundary points. Left and top should be (0,0).
//
	bounds->left = FixRound( lowX - lowX );
	bounds->right = FixRound( hiX - lowX );
	bounds->top = FixRound( lowY - lowY );
	bounds->bottom = FixRound( hiY - lowY );

//
//	Determine the new glyph origin.
//
	origin->h = FixRound( lowX );
	origin->v = FixRound( -lowY );
}

#define APPENDHANDLE(a,b)	AppendHandle((Handle)a, (Handle)b)

void AppendHandle(Handle dst, Handle extra)
{
	long dstSize = GetHandleSize(dst);
	long extraSize = GetHandleSize(extra);
	
	SetHandleSize(dst, dstSize + extraSize);
	if (MemError()) {
#ifdef	DEBUG
		DebugStr("\pAppendHandle(): Not enough memory.");
#endif	//	DEBUG
	}
	else
		BlockMove(*extra, *dst + dstSize, extraSize);
}

/*	a += b
*/
void AppendGlyphOutline(GlyphOutline* a, GlyphOutline* b)
{
	APPENDHANDLE(a->endPoints, b->endPoints);
	{	short* p = *a->endPoints + a->contourCount;
		short* endp = p + b->contourCount;
		short newFirstPoint = a->contourCount ? p[-1] + 1 : 0;
		for (; p < endp; p++)
			*p = *p + newFirstPoint;
	}
	a->contourCount += b->contourCount;
	a->pointCount += b->pointCount;
	APPENDHANDLE(a->onCurve, b->onCurve);
	APPENDHANDLE(a->x, b->x);
	APPENDHANDLE(a->y, b->y);
}

void FrameText( unsigned char* text, FixPoint* scale, int mark )
{
	FixPoint	pen;
	Rect		bounds;
	Point		origin;

	paths* myPath = Text2Paths( text, scale, nil, &pen, &bounds, &origin );

	if (myPath)
	{	FramePaths(myPath, true);
		if (mark)
			MarkPaths( myPath );	
		DisposePaths(myPath);
		fmoveto( pen.x, pen.y );
	}
}


Boolean
FrameTextTransform( unsigned char* text, TransMatrixPtr m, Rect* bounds, Point* origin )
{
	FixPoint	pen;
	FixPoint	scale = { 0x10000, 0x10000 };

	paths*		myPath = Text2Paths( text, &scale, m, &pen, bounds, origin );

	if (myPath)
	{	FramePaths(myPath, true);
		DisposePaths(myPath);
		fmoveto( pen.x, pen.y );
	}
	else
		return( false );
	return( true );
}

paths* Text2Paths( unsigned char* text, FixPoint* scale, TransMatrixPtr m, FixPoint* pen, Rect* bounds, Point* origin )
{
	unsigned	state;
	GrafPtr		curPort;
	Handle		sfnt;
	paths*		compositPath = 0;

	GetPort( &curPort );
	sfnt = GetSfntHandle( curPort->txFont, curPort->txFace );

	if (!sfnt)
		return 0;

	state = HGetState( sfnt );
	HNoPurge( sfnt );

	{	GlyphOutline out;
		Fixed originX = ff( curPort->pnLoc.h );
		Fixed originY = ff( curPort->pnLoc.v );
		Fixed xScale = scale->x * curPort->txSize;
		Fixed yScale = scale->y * curPort->txSize;
		int i;

		InitGlyphOutline( &out );
		for (i = 1; i <= text[0]; i++)
		{	long glyphIndex = GetCharGlyphIndex( sfnt, text[i] );
			if ( GetGlyphOutline( sfnt, glyphIndex, &out, 0 ) == noErr ) {
				ScaleGlyphOutline( &out, xScale, yScale );
				MoveToGlyphOutline( &out, originX, originY );
				if ( m ) {
					Matrix	mat;
				
					InitMatrix( mat );
					mat[0][0] = m->m11;
					mat[0][1] = m->m12;
					mat[1][0] = m->m21;
					mat[1][1] = m->m22;
					MapGlyphOutline( &out, mat, bounds, origin );
				}

				{	paths* p = OutlineToPaths( &out );
					if ( p ) {
						compositPath = AppendPaths( compositPath, p );
						DisposePaths(p);
					}
				}
				originX += out.advance.x;
				originY += out.advance.y;
			}
		}
		KillGlyphOutline( &out );
		
		if (pen)
		{	pen->x = originX;
			pen->y = originY;
		}
	}
	HSetState( sfnt, state );
	
	return compositPath;
}

static long* PackControlBits(long* p, Byte* onCurve, long count);
static long* PackControlBits(long* p, Byte* onCurve, long count)
{
	unsigned long mask = 0x80000000;
	
	*p = 0;
	while (count--)
	{	if (!mask)
		{	mask = 0x80000000;
			*++p = 0;
		}
		if (!*onCurve++)
			*p |= mask;
		mask >>= 1;
	}
	return p + 1;
}

paths* OutlineToPaths(GlyphOutline* out)
{
	long size, *p, *origP;

	size = sizeof(long);		/* paths.contours */

	{	long i, sp = 0;
		for (i = 0; i < out->contourCount; i++)
		{	long pts = (*out->endPoints)[i] - sp + 1;
			size += sizeof(long);			/* path.vectors */
			size += (pts + 31 >> 5) << 2;	/* path.controlBits */
			size += pts << 3;			/* path.vector[] */
			sp = (*out->endPoints)[i] + 1;
		}
	}

	origP = p = (long*)NewPtr( size );
	if (!p || MemError()) {
#ifdef	DEBUG
		DebugStr("\pOutlineToPaths(): Not enough memory for outline path");
#endif	//	DEBUG
		return( nil );
	}

	*p++ = out->contourCount;
	{	long i, sp = 0;
		Fixed* x = *out->x;
		Fixed* y = *out->y;
		short* ep = *out->endPoints;
		Byte* onCurve = *out->onCurve;
		for (i = 0; i < out->contourCount; i++)
		{	long pts = *ep - sp + 1;
			*p++ = pts;
			p = PackControlBits(p, onCurve, pts);
			onCurve += pts;
			while (pts--)
			{	*p++ = *x++;
				*p++ = *y++;
			}
			sp = *ep++ + 1;
		}
	}
	return (paths*)origP;
}
