#include "pch.hpp"
//==========================================================================//
// File:	 Font3D.cpp														//
// Project:  GameOS					                                        //
// Contents: Font3D drawing routines										//
//--------------------------------------------------------------------------//
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                            //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL              //
//==========================================================================//
#include "Font3D.hpp"
#include "DXRasterizer.hpp"
#include "3DRasterizer.hpp"
#include "Texture Manager.hpp"


// for memory tracking
extern HGOSHEAP DefaultHeap;
extern volatile __int64 TimeFont3D;
extern DWORD gDisableText;
extern FontInfo*	Fonts;			// Linked list of fonts loaded
extern HGOSHEAP Heap_Font;

extern int FontX,FontY;
extern float FontMinX,FontMinY,FontMaxX,FontMaxY;
extern DWORD CurrentFontTexture;
extern HGOSFONT3D FontHandle;

//
// Vertices used for text
//
extern gos_VERTEX FontVertices[128];
extern DWORD FontIndex;
extern DWORD FontClipping;

//
// Get the text routines ready to display
//
void Init3DFont()
{
	CurrentFontTexture=-1;
	FontX=0;
	FontY=0;
	FontClipping=0;
	FontIndex=0;
	FontMinX=0.0f;
	FontMinY=0.0f;
	FontMaxX=(float)Environment.screenWidth;
	FontMaxY=(float)Environment.screenHeight;
	Fonts=0;
	memset( FontVertices, 0, sizeof(FontVertices) );
	for( int t0=0; t0<128; t0++ )
	{
		FontVertices[t0].rhw = 1.0f;
	}
}





//
//
//
void Destroy3DFont()
{
	FontInfo* pFont=Fonts;
	while( pFont )
	{
		FontInfo* pThis=pFont;
		pFont=pFont->pNext;
		for( int t0=0; t0<pThis->NumberOfTextures; t0++ )
		{
			if( pThis->Texture[t0] )
			{
				gos_DestroyTexture( pThis->Texture[t0] );
				pThis->Texture[t0]=0;
			}
		}
		gos_Free( pThis );						// Delete memory block
	}
	Fonts=0;
}

//
// Check a vertical line in the font for pixels
//
bool BlankLine( FontInfo* pFont, DWORD* pt )
{
	for( int temp=0; temp<pFont->Height; temp++,pt+=pFont->TexturePitch )
		if( (*pt&0xff000000)!=0 )
			return FALSE;
	return TRUE;
}

//
// Init font - reads a .TGA file and decodes it as a font
//
// Return value is a handle that can be passed to the font routines
//
// Rules for FONT.TGA files:
//
//  Fonts must be drawn on a regular grid, ie: 6*8, 8*8, 8*12, 16*16
//
//  Fonts must be drawn in solid white (255,255,255) with a valid alpha
//  channel. (Either keyed or full alpha for antialiasing)
//
//  The grid must start at the top left of the .TGA file and go all the
//  way to the edge. The next letters must start directly below the top
//  line of letters.
//
//  The first character must be a single pixel thick box, the same size
//  as the grid - this is how GOS measures grid size.
//
//  The 2nd character must be character ascii code 33 (!) all the other
//  characters must be incrementing ascii characters up to character 255.
//
//  The remainder of any .TGA file is used. See Ariel8.tga for an example
//
//  A proportional font need not be centered in the grid squares. GameOS
//  will calculate the leading blank pixels and width of each character.
//  It is important the characters should be centered vertically properly
//  and if the font will be displayed as a fixed pich font then the letters
//  should be centered. Letters should not use the right or bottom row of
//  the grid as these rows do not render accuratly across all cards. So
//  and 8*8 font would have 7*7 sized letters aligned in the top left.
//
//  The StartLine parameter is the Y position for the top of the font. There
//  maybe multiple fonts in a single texture.
//
//  CharCount is the ASCII value of the last character in the font
//  texture plus one.
//
HGOSFONT3D __stdcall gos_LoadFont( const char* FontFile, DWORD StartLine, int CharCount, DWORD TextureHandle )
{
//
// Check arguments
//
	if( TextureHandle )
	{
		gosASSERT( TEXHANDLE_TO_PTR(TextureHandle)->ValidTexture() );
	}
	else
	{
		gosASSERT( FontFile );
	}
//
//
//
	#if defined(_ARMOR)
		if( CharCount < 33 || CharCount > 256 )
			STOP(( "CharCount must be in the range 33 to 256" ));
	#endif
//
//
//
	bool Found;
	FontInfo* pFont;
	char Buffer[MAX_PATH];
	Buffer[0]=0;
//
// Get full name to font
//
	if( 0==TextureHandle )
	{
		gos_GetFullPathName( Buffer, FontFile );
//
// Search to see if font already loaded
//
		pFont=Fonts;
		while( pFont )
		{
			if(	(pFont->StartLine == StartLine) &&
				(pFont->CharCount >= CharCount) &&
				(0==stricmp( pFont->FontFile, Buffer )) )
			{
				pFont->ReferenceCount++;
				return pFont;
			}
			pFont=pFont->pNext;
		}
	}
//
// Create a new font, fill in information
//
	pFont=(HGOSFONT3D)gos_Malloc( sizeof(FontInfo), Heap_Font );
	memset( pFont, 0, sizeof(FontInfo) );
	pFont->pNext=Fonts;
	pFont->MagicNumber=0x4587abba;
	pFont->ReferenceCount=1;
	Fonts=pFont;
	if( 0==TextureHandle )
		memcpy( pFont->FontFile, Buffer, MAX_PATH );
	pFont->StartLine=StartLine;
	pFont->CharCount=CharCount;
	pFont->NumberOfTextures=1;
	pFont->FromTextureHandle=TextureHandle;
	pFont->hFontTTF = 0;
//
// Support for TrueType Fonts
//
//	if( 0==TextureHandle && (0==stricmp(".TTF", &FontFile[strlen(FontFile)-4]) || 0==stricmp(".TTC", &FontFile[strlen(FontFile)-4])) )
	const char* psz = FontFile;
	if( 0==TextureHandle )
	{
		while (NULL != (psz = (char*)_mbschr((unsigned char*)psz, '.'))) {
			psz++;
			if ( tolower(psz[0]) == 't' &&
				 tolower(psz[1]) == 't' &&
				 ( tolower(psz[2]) == 'f' ||  tolower(psz[0]) == 'c'))
			{
				strcpy(pFont->FontFile, FontFile);
				pFont->FontFile[psz - FontFile + 3] = 0;
				char* pszBackslash = (char*)_mbsrchr((unsigned char*)pFont->FontFile, '\\');
				if (pszBackslash)
				{
					int len = strlen(pszBackslash + 1);
					memmove((void*)pFont->FontFile, pszBackslash + 1, len);
					pFont->FontFile[len] = 0;
				}
				pFont->hFontTTF = (HFONT) 1;
				pFont->Height = 12;	// 9 Point Font

				psz += 3;
				if (*psz++ == ',')	// check if size is specified
				{
					pFont->Height = strtoul(psz, (char**)&psz, 0);
					if (0 == pFont->Height)
					{
#ifdef LAB_ONLY
						PAUSE(("Font height is 0."));
#endif
						pFont->Height = 12;
					}
				}
				return pFont;
			}
		}
	}
//
// Support for Access .d3f font files
//
	if( 0==TextureHandle && 0==stricmp(".D3F", &FontFile[strlen(FontFile)-4]) )
	{
		BYTE *pData=NULL;
		DWORD dwSize;
		gos_PushCurrentHeap( DefaultHeap );
		gos_GetFile(FontFile,&pData,&dwSize);			// Read in file
		gos_PopCurrentHeap();

		D3DFontData* pFontData = (D3DFontData*)pData;
//
// New FontEdit 3.0 files?
//
		if( pFontData->dwSig=='4F3D' )
		{
			D3DFontData1* pFontData=(D3DFontData1*)pData;
//
// Create textures and convert data to 8888
//
			gosASSERT( pFontData->iTextureCount<8 );
			pFont->NumberOfTextures=pFontData->iTextureCount;
			D3DFontTexture* pTexture=(D3DFontTexture*)(pFontData+1);
			DWORD TextureSize=pTexture->dwSize;

			for( int t0=0; t0<pFontData->iTextureCount; t0++ )
			{
				pFont->Texture[t0] = gos_NewEmptyTexture( gos_Texture_Alpha, FontFile, TextureSize, gosHint_DisableMipmap | gosHint_DontShrink );

				TEXTUREPTR Texture;
				gos_LockTexture( pFont->Texture[t0], 0, false, &Texture);

				pFont->TexturePitch=Texture.Pitch;
				DWORD dwPixelCount = TextureSize * TextureSize;
				DWORD dwPixelsLeft = dwPixelCount;
				BYTE *pCurPix = (BYTE *)Texture.pTexture;
				BYTE *pCurSource = pTexture->bPixels;

				while (dwPixelsLeft)
				{
					BYTE Pixel=(BYTE)(*pCurSource==0 ? 0 : 0xff);
					*pCurPix++ = Pixel;
					*pCurPix++ = Pixel;
					*pCurPix++ = Pixel;
					*pCurPix++ = *pCurSource++;
					dwPixelsLeft--;
				}

				pTexture=(D3DFontTexture*)pCurSource;
				
				gos_UnLockTexture( pFont->Texture[t0] );
			}
//
// Now get character sizes of .d3f font characters
//
			TEXTUREPTR Texture;

			BYTE LockedTexture=255;
			memset( &Texture, 0, sizeof(Texture) );

			pFont->TopU[0]=0;
			pFont->TopV[0]=0;
			pFont->BlankPixels[0]=0;
			pFont->UsedPixels[0]=(BYTE)pFontData->bW[0];
			pFont->Height=pFontData->dwFontHeight-2;
			pFont->Width=pFontData->bW[0];
			pFont->Aliased=1;
			pFont->TextureSize=TextureSize;
			pFont->rhSize=1.0f/TextureSize;
			memcpy( &pFont->TextureHandle, &pFontData->bTexture[32], 256-32 );

			for( int Chr=33; Chr<CharCount; Chr++ )
			{
				if( LockedTexture!=pFont->TextureHandle[Chr-32] )
				{
					if( 255!=LockedTexture )
						gos_UnLockTexture( pFont->Texture[LockedTexture] );
					gos_LockTexture( pFont->Texture[pFont->TextureHandle[Chr-32]], 0, true, &Texture);
					LockedTexture=pFont->TextureHandle[Chr-32];
				}

				pFont->TopU[Chr-32]=(BYTE)pFontData->bX[Chr];
				pFont->TopV[Chr-32]=(BYTE)(pFontData->bY[Chr]);
				pFont->BlankPixels[Chr-32]=0;
				pFont->UsedPixels[Chr-32]=(BYTE)(pFontData->bW[Chr]-1);
//
// Find offset from top and height of character
//
				DWORD* Ptr=(DWORD*)Texture.pTexture;
				Ptr=Ptr+pFont->TopU[Chr-32]+pFont->TopV[Chr-32]*Texture.Pitch;
				pFont->TopOffset[Chr-32]=0;
				pFont->RealHeight[Chr-32]=(BYTE)pFont->Height;

				Found=0;
				do
				{
					for( int t0=0; t0<pFont->UsedPixels[Chr-32]; t0++ )
					{
						if( (Ptr[t0]&0xff000000) )
						{
							Found=1;
							break;
						}
					}											// Find blank lines ontop of character
					if( !Found )
					{
						Ptr+=Texture.Pitch;
						pFont->TopOffset[Chr-32]++;
						pFont->RealHeight[Chr-32]--;
					}
				} while( !Found && pFont->RealHeight[Chr-32] );

				Ptr+=pFont->RealHeight[Chr-32]*Texture.Pitch;

				Found=0;
				if( pFont->RealHeight[Chr-32] )
				{
					do
					{
						Ptr-=Texture.Pitch;
						for( int t0=0; t0<pFont->UsedPixels[Chr-32]; t0++ )
						{
							if( (Ptr[t0]&0xff000000) )
							{
								Found=1;
								break;								// Find lines character takes up
							}
						}
						if( !Found )
								pFont->RealHeight[Chr-32]--;
					} while( !Found && pFont->RealHeight[Chr-32] );
				}
			}

			gos_UnLockTexture( pFont->Texture[LockedTexture] );

			gos_Free(pData);

			return pFont;
		}
		else if( pFontData->dwSig=='FD3D' )			// Old FontEdit signature
		{
//
// Original FontEdit type data
//
			TEXTUREPTR Texture;
			if( pFontData->dwWidth>256 )
				STOP(( "Font too large" ));
			pFont->Texture[0] = gos_NewEmptyTexture( gos_Texture_Alpha, FontFile, pFontData->dwWidth, gosHint_DisableMipmap | gosHint_DontShrink );
			gos_LockTexture( pFont->Texture[0], 0, false, &Texture );

			// convert data to 8888 format that GameOS likes
			DWORD dwPixelCount = pFontData->dwWidth * pFontData->dwHeight;
			DWORD dwPixelsLeft = dwPixelCount;
			BYTE *pCurPix = (BYTE *)Texture.pTexture;
			BYTE *pCurSource = pFontData->bPixels;
			while (dwPixelsLeft)
			{
				// copy source byte to 4 dest bytes
				BYTE Pixel=(BYTE)(*pCurSource==0 ? 0 : 0xff);
				*pCurPix++ = Pixel;
				*pCurPix++ = Pixel;
				*pCurPix++ = Pixel;
				*pCurPix++ = *pCurSource++;
				dwPixelsLeft--;
			}
//
// Hacked in code to get character sizes of .d3f font characters
//
			pFont->TopU[0]=0;
			pFont->TopV[0]=0;
			pFont->BlankPixels[0]=0;
			pFont->UsedPixels[0]=(BYTE)pFontData->dwWidths[0];
			pFont->Height=pFontData->dwFontHeight-2;
			pFont->Width=pFontData->dwWidths[0];
			pFont->Aliased=1;
			pFont->TextureSize=Texture.Width;
			pFont->rhSize=1.0f/Texture.Width;
			pFont->TexturePitch=Texture.Pitch;

			for( int Chr=33; Chr<CharCount; Chr++ )
			{
				pFont->TopU[Chr-32]=(BYTE)pFontData->dwX[Chr];
				pFont->TopV[Chr-32]=(BYTE)(pFontData->dwY[Chr]);
				pFont->BlankPixels[Chr-32]=0;
				pFont->UsedPixels[Chr-32]=(BYTE)(pFontData->dwWidths[Chr]-1);
//
// Find offset from top and height of character
//
				DWORD* Ptr=(DWORD*)Texture.pTexture;
				Ptr=Ptr+pFont->TopU[Chr-32]+pFont->TopV[Chr-32]*Texture.Pitch;
				pFont->TopOffset[Chr-32]=0;
				pFont->RealHeight[Chr-32]=(BYTE)pFont->Height;

				Found=0;
				do
				{
					for( int t0=0; t0<pFont->UsedPixels[Chr-32]; t0++ )
					{
						if( (Ptr[t0]&0xff000000) )
						{
							Found=1;
							break;
						}
					}											// Find blank lines ontop of character
					if( !Found )
					{
						Ptr+=Texture.Pitch;
						pFont->TopOffset[Chr-32]++;
						pFont->RealHeight[Chr-32]--;
					}
				} while( !Found && pFont->RealHeight[Chr-32] );

				Ptr+=pFont->RealHeight[Chr-32]*Texture.Pitch;

				Found=0;
				do
				{
					Ptr-=Texture.Pitch;
					for( int t0=0; t0<pFont->UsedPixels[Chr-32]; t0++ )
					{
						if( (Ptr[t0]&0xff000000) )
						{
							Found=1;
							break;								// Find lines character takes up
						}
					}
					if( !Found )
							pFont->RealHeight[Chr-32]--;
				} while( !Found );
			}

			gos_UnLockTexture( pFont->Texture[0] );

			gos_Free(pData);

			return pFont;
		}
		else
		{
			STOP(( "Unknown .d3f file format" ));
		}
	}
	else
	{
//
// Load font as a texture
//
		if( TextureHandle )
			pFont->Texture[0]=TextureHandle;
		else
			pFont->Texture[0]=gos_NewTextureFromFile( gos_Texture_Detect, FontFile, gosHint_ForceAlpha|gosHint_DontShrink|gosHint_DisableMipmap );
	}
//
// Lock and get texture info
//
	TEXTUREPTR Texture;
	gos_LockTexture( pFont->Texture[0], 0, 1, &Texture );
	DWORD* pt=Texture.pTexture;
//
// Get font information
//
	pFont->Aliased=(Texture.Type==gos_Texture_Alpha);
	pFont->TextureSize=Texture.Width;
	pFont->rhSize=1.0f/Texture.Width;
	pFont->TexturePitch=Texture.Pitch;
	if( StartLine >= Texture.Height )
		STOP(( "StartLine greater than texture height" ));
//
// Look for top left four pixel being the top of a square
//
	pt+=StartLine*Texture.Pitch;
	if( ((*pt&0xff000000) != 0xff000000) ||							// Top left pixel
		((*(pt+Texture.Pitch)&0xff000000) != 0xff000000) ||			// One line down
		((*(pt+1)&0xff000000) != 0xff000000) ||						// One pixel right
		((*(pt+Texture.Pitch+1)&0xff000000) == 0xff000000) )		// Down and right must not be white
		STOP(( "Fonts need a white square in top left" ));
//
// Find width of font
//
	pFont->Width=1;
	do
	{
		pFont->Width++;
		pt++;
	} while( ((*pt&0xff000000) == 0xff000000) && ((*(pt+Texture.Pitch)&0xff000000) == 0) && pFont->Width<Texture.Width );
//
// Find height of text
//
	pFont->Height=1;
	pt=Texture.pTexture+StartLine*Texture.Pitch;
	do
	{
		pFont->Height++;
		pt+=Texture.Pitch;

	} while( ((*pt&0xff000000) == 0xff000000) && ((*(pt+1)&0xff000000) == 0) && (pFont->Height+StartLine)<Texture.Height );
//
// Make sure texture is big enough for whole font
//
	pFont->Across=Texture.Width/pFont->Width;
	if( ((CharCount-32)/pFont->Across)*pFont->Height+StartLine > Texture.Height )
		STOP(( "Font must include %d characters from 32 to %d", CharCount-32, CharCount-1 ));
//
// Work out size of each character
//
	DWORD Xp=pFont->Width;
	DWORD Yp=StartLine;
	pFont->TopU[0]=1;
	pFont->TopV[0]=1;
	pFont->BlankPixels[0]=0;
	pFont->UsedPixels[0]=(BYTE)(pFont->Width>>1);

	for( int Chr=33; Chr<CharCount; Chr++ )
	{
//
// Work out top left of character
//
		pFont->TopU[Chr-32]=(BYTE)Xp;
		pFont->TopV[Chr-32]=(BYTE)Yp;
		pt=Texture.pTexture+Xp+Yp*Texture.Pitch;
		pFont->BlankPixels[Chr-32]=0;
		pFont->UsedPixels[Chr-32]=0;
//
// Find out how many leading blank lines
//
		while( BlankLine( pFont, pt ) )
		{
			pt++;
			pFont->BlankPixels[Chr-32]++;
			if( pFont->BlankPixels[Chr-32]==pFont->Width )
				break;
		}
//
// Blank character?
//
		if( pFont->BlankPixels[Chr-32]==pFont->Width )
		{
			pFont->BlankPixels[Chr-32]=0;
			pFont->UsedPixels[Chr-32]=(BYTE)(pFont->Width>>1);
			continue;
		}
//
// Find width of character in pixels
//
		while( !BlankLine( pFont, pt ) )
		{
			pt++;
			pFont->UsedPixels[Chr-32]++;
			if( (pFont->BlankPixels[Chr-32]+pFont->UsedPixels[Chr-32])==pFont->Width )
				break;
		}
//
// Find Height of character
//
		DWORD* Ptr=(DWORD*)Texture.pTexture;
		Ptr=Ptr+pFont->TopU[Chr-32]+pFont->TopV[Chr-32]*Texture.Pitch+pFont->BlankPixels[Chr-32];
		pFont->TopOffset[Chr-32]=0;
		pFont->RealHeight[Chr-32]=(BYTE)pFont->Height;

		Found=0;
		do
		{
			for( int t0=0; t0<pFont->UsedPixels[Chr-32]; t0++ )
			{
				if( (Ptr[t0]&0xff000000) )
				{
					Found=1;
					break;
				}
			}											// Find blank lines ontop of character
			if( !Found )
			{
				Ptr+=Texture.Pitch;
				pFont->TopOffset[Chr-32]++;
				pFont->RealHeight[Chr-32]--;
			}
		} while( !Found && pFont->RealHeight[Chr-32] );

		Ptr+=pFont->RealHeight[Chr-32]*Texture.Pitch;

		Found=0;
		do
		{
			Ptr-=Texture.Pitch;
			for( int t0=0; t0<pFont->UsedPixels[Chr-32]; t0++ )
			{
				if( (Ptr[t0]&0xff000000) )
				{
					Found=1;
					break;								// Find lines character takes up
				}
			}
			if( !Found )
					pFont->RealHeight[Chr-32]--;
		} while( !Found );
//
// Move to next character (skip leading space)
//
		Xp+=pFont->Width;
		if( Xp+pFont->Width>Texture.Width )
		{
			Xp=0;
			Yp+=pFont->Height;
		}
	}
//
// Unlock texture
//
	gos_UnLockTexture( pFont->Texture[0] );

	return pFont;
}


//
// This routine should be called to release storage and textures used by fonts.
//
void __stdcall gos_DeleteFont( HGOSFONT3D Fonthandle )
{
	FontInfo* pLast=0;
	FontInfo* pFont=Fonts;
	if( Fonthandle->MagicNumber!=0x4587abba )
		STOP(( "Invalid font handle" ));

	while( pFont )
	{
		if( pFont==Fonthandle )
		{
			pFont->ReferenceCount--;
			if( pFont->ReferenceCount )				// Still being used by someone?
				return;
			if( pLast )
			{
				pLast->pNext=pFont->pNext;			// Remove from linked list
			}
			else
			{
				Fonts=pFont->pNext;			
			}
			if( 0==pFont->FromTextureHandle )
			{
				for( int t0=0; t0<pFont->NumberOfTextures; t0++ )
				{
					if( pFont->Texture[t0] )
					{
						gos_DestroyTexture( pFont->Texture[t0] );
						pFont->Texture[t0]=0;
					}
				}
			}
			if (pFont->hFontTTF)
				pFont->hFontTTF = 0;
			gos_Free( pFont );						// Delete memory block


			//
			// If the handle is currently in use, clear the current font handle
			//
			if (Fonthandle == FontHandle)
				FontHandle = NULL;
			return;
		}
		pLast=pFont;
		pFont=pFont->pNext;
	}
	STOP(( "Deleted an invalid font" ));
}

