// Copyright Electonic Arts(C) 2006 - All Rights Reserved #include "filesystem.h" #include "CTLFont.h" #include "t2k.h" #include "tlfont/fusionrasterizer.h" #include #include "MemMgr/inc/MemMgr.h" // CTLFontManager and CTLFont act as a wrapper for Font Fusion. It uses the TLFont // wrapper for rasterizing with Font Fusion. We ignore all the other systems provided // by TLFont (caching, drawlists, rendering, etc). // MARLETT: // Marlett causes some problems with Font Fusion. It renders the glyphs correctly, // but can return incorrect metric information. In particular, the descent is always // 0 and ascent is the height of the glyph; the maximum height calculated from // the ascent and descent does not always indicate the highest glyph - it is possible // to render a glyph heigher than (ascent+descent). // // For some reason, you need to offset any marlett character codes by 0xf000 otherwise // Font Fusion will just render invalid glyphs. using namespace TLFont; //#define DISABLE_FONT CTLFontManager::CTLFontManager(IFileSystem *pFileSystem) { #ifndef DISABLE_FONT // The allocator that uses MemMgr static MemMgr_ICoreAllocator_Adapter memMgrAdapter; SetAllocator(&memMgrAdapter); FontFusionMemObject::SetAllocatorCallbacks(FontFusionAlloc, FontFusionFree, 0); // Load the filesystem m_pFileSystem = pFileSystem; ASSERT(m_pFileSystem); // Reset the font data cache for (int i = 0; i < MAX_FONTDATACACHE; ++i) { memset(m_fontDataCache[i].m_dataName, 0, 256); m_fontDataCache[i].m_dataMem = NULL; m_fontDataCache[i].m_dataSize = 0; m_fontDataCache[i].m_refCount = 0; } #endif } CTLFontManager::~CTLFontManager() { // Clean the cache for (int i = 0; i < MAX_FONTDATACACHE; ++i) { if ( m_fontDataCache[i].m_dataMem ) { //delete m_fontDataCache[i].m_dataMem;//Now allocated permanently m_fontDataCache[i].m_dataMem = NULL; m_fontDataCache[i].m_refCount = 0; } } } unsigned char* CTLFontManager::LoadFontFile(const char *pFontPath, unsigned int *pDataSize) { #if 1 Assert(!" - temporarily disabling this"); return NULL; #else #ifndef DISABLE_FONT unsigned char *pData = NULL; unsigned int dataSize = 0; MEM_ALLOC_CREDIT_("CTLFontManager::LoadFontFile"); // Load a new font file FileHandle_t hFont = m_pFileSystem->Open(pFontPath, "rb"); if(hFont == NULL) { ASSERT(hFont); return NULL; } dataSize = m_pFileSystem->Size(hFont); if(dataSize == 0) { // error getting the file size ASSERT(dataSize > 0); return NULL; } //pData = new unsigned char[dataSize]; pData= ( unsigned char * )gMemMgr.PermanentAlloc(dataSize);// This is an alloc thats never freed (gives us some flexibility to reuse scraps of memory) ASSERT(pData); //printf("LoadFontFile(%s) allocating %d",pFontPath,dataSize); int ret = m_pFileSystem->Read(pData, dataSize, hFont); if(pDataSize) { *pDataSize = dataSize; } m_pFileSystem->Close(hFont); return pData; #else return NULL; #endif #endif } CTLFont *CTLFontManager::CreateFont(const char *pName, const char *pFontPath, int tall, int weight) { #ifndef DISABLE_FONT unsigned int dataSize = 0; unsigned char *pData = NULL; // Check the cache if we have already loaded this font for (int i = 0; i < MAX_FONTDATACACHE; ++i) { if ( m_fontDataCache[i].m_dataMem ) { if ( strncmp(pName, m_fontDataCache[i].m_dataName, strlen(pName)) == 0 ) { //printf("CTLFontManager::CreateFont(%s) - found in cache\n",pName); pData = m_fontDataCache[i].m_dataMem; dataSize = m_fontDataCache[i].m_dataSize; m_fontDataCache[i].m_refCount++; break; } } else { // Load the TTF font from disk //printf("CTLFontManager::CreateFont(%s) - loading from disk\n",pName); m_fontDataCache[i].m_dataMem = LoadFontFile(pFontPath, &m_fontDataCache[i].m_dataSize); pData = m_fontDataCache[i].m_dataMem; dataSize = m_fontDataCache[i].m_dataSize; Q_strncpy(m_fontDataCache[i].m_dataName, pName, strlen(pName)+1); m_fontDataCache[i].m_refCount=1; break; } } if(pData == NULL || dataSize == 0) { Assert(0); return NULL; } // Simply allocate a new font and return it. One could, potentially, have a more complex way // of handling the fonts memory management. CTLFont *pFont = new CTLFont(pName, pData, dataSize, tall, weight); ASSERT(pFont); return pFont; #else return NULL; #endif } void CTLFontManager::DestroyFont(CTLFont *pFont) { #ifndef DISABLE_FONT size_t size; void* pData; pData=pFont->GetData(&size); //printf("CTLFontManager::DestroyFont(%s)\n",pFont->GetName()); //Delete CTLFont delete pFont; //Update refcount in m_fontDataCache for (int i = 0; i < MAX_FONTDATACACHE; ++i) { if (pData==m_fontDataCache[i].m_dataMem) { m_fontDataCache[i].m_refCount--; break; } } #endif } CTLFont::CTLFont(const char *pName, unsigned char *pData, unsigned int dataSize, int tall, int weight) { // Font name int len = strlen(pName); if(len >= MAX_NAME) { memcpy(m_name, pName, (MAX_NAME-2)); m_name[MAX_NAME-1] = 0; } else { // include the null terminator memcpy(m_name, pName, len+1); } // Dimensions //note: Point and Logical Size values should be the same, unless you start scaling things. m_tall = tall; m_weight = weight; // empirically derived factor to achieve desired cell height m_pointSize = m_tall * 0.82f; // Load the TTF file into memory m_pData = pData; m_dataSize = dataSize; ASSERT(m_pData); ASSERT(m_dataSize > 0); // The rasterizer #ifndef DISABLE_FONT m_pRasterizer = new FusionRasterizer(m_pData, m_dataSize, FONTFILE_TTF, NULL, 0.0f, 1.0f, 72, 72, 0, // padding must be zero m_weight); ASSERT(m_pRasterizer); #endif // Gross marlett hack! m_charOffset = 0; if(stricmp(m_name, "Marlett") == 0) { m_charOffset = 0xf000; } } CTLFont::~CTLFont() { delete m_pRasterizer; } void CTLFont::RenderToBuffer(int ch, int offsetx, int width, int height, unsigned char *pBuffer) { #ifndef DISABLE_FONT IRasterizer::RasterizationResult res; FixedAngle angle(0.0f); GlyphImage *pImage = m_pRasterizer->Rasterize(ch+m_charOffset, 1.0f, m_pointSize, angle, res); // Return if we try and render a character we don't understand. This can include white spaces // and other special control characters. if(pImage == NULL || pImage->GetFormat() == GlyphImage::GLYPHIMAGE_INVALID || res == IRasterizer::RASTERIZE_FAILURE) { memset(pBuffer, 0x00, (width*height*4)); return; } // Image dimensions unsigned int imageWidth, imageHeight; pImage->GetGlyphDimensions(imageWidth, imageHeight); // Marlett can be slightly bigger than the maximum height calculated by adding // the ascent and descent values together. if(imageHeight > height) { imageHeight = height; } // The offset from the baseline.. int xOffset, yOffset; pImage->GetGlyphOffset(xOffset, yOffset); // Determine the baseline of the image (using the ascent and descent values) int ascent = (int)ceil(m_pRasterizer->GetAscent(m_pointSize)); int descent = (int)ceil(m_pRasterizer->GetDescent(m_pointSize)); int maxHeight = (descent + ascent); int baseOffset = maxHeight - (descent + yOffset); if(baseOffset < 0) { // Marlett can produce a negative offset, which is BAD, so we correct this. baseOffset = 0; } ASSERT((imageHeight+baseOffset) <= height); // We only support copying of an alpha-only image ASSERT(pImage->GetFormat() == GlyphImage::GLYPHIMAGE_A8); if(pImage->GetFormat() == GlyphImage::GLYPHIMAGE_A8) { // The rasterized image is stored as single bytes; we need to copy // this into a 32-bit image. for(int h = 0; h < imageHeight; h++) { unsigned char *pSrc = (unsigned char *)pImage->GetBitmap() + h*imageWidth; unsigned int *pDst = (unsigned int *)pBuffer + ( (baseOffset+h) * width ); for(int w = 0; w < imageWidth; w++) { unsigned char val = pSrc[w]; pDst[w + offsetx] = (0xff << 24) | (0xff << 16) | (0xff << 8) | val; } } } delete pImage; #endif } // This is a slow function. You should use it with care; possibly implement a basic // caching system to prevent it being called all the time. bool CTLFont::GetCharABCWidth(int ch, int &a, int &b, int &c) { #ifndef DISABLE_FONT IRasterizer::RasterizationResult res; FixedAngle angle(0.0f); GlyphImage *pImage = m_pRasterizer->Rasterize(ch+m_charOffset, 1.0f, m_pointSize, angle, res); // Return if we try and render a character we don't understand. This can include white spaces // and other special control characters. if(pImage == NULL || res == IRasterizer::RASTERIZE_FAILURE) { a = 0; b = 0; c = 0; return false; } unsigned int width, height; pImage->GetGlyphDimensions(width, height); int offsetX, offsetY; pImage->GetGlyphOffset(offsetX, offsetY); int advance = pImage->GetGlyphAdvance(); ASSERT((int)width >= 0); // We simply provide the advance distance as the glyph width, because 'a' and 'c' // have some special meaning to the Source engine.. b = width; a = offsetX; c = advance - ((int)width + offsetX); // In case the advance value is smaller than the glyph width if(c < 0) { c = 0; } delete pImage; return true; #else return false; #endif } int CTLFont::GetMaxHeight() { #ifndef DISABLE_FONT float ascent = m_pRasterizer->GetAscent(m_pointSize); float descent = m_pRasterizer->GetDescent(m_pointSize); return (int)(ceil(ascent) + ceil(descent)); #else return 0; #endif } int CTLFont::GetMaxWidth() { // Unimplemented //ASSERT(0); return 0; } int CTLFont::GetAscent() { #ifndef DISABLE_FONT float ascent = m_pRasterizer->GetAscent(m_pointSize); return (int)ceil(ascent); #else return 0; #endif } void* CTLFont::GetData(size_t * pSizeOut) { if (pSizeOut) *pSizeOut=m_dataSize; return m_pData; } const char * CTLFont::GetName() { return m_name; }