// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" #include #include "nel/3d/texture_font.h" #include "nel/3d/font_generator.h" #include "nel/misc/common.h" #include "nel/misc/rect.h" #include "nel/misc/file.h" using namespace std; using namespace NLMISC; namespace NL3D { // Config 1 const int TextureSizeX = 1024; const int TextureSizeY = 1024; // If change this value -> change NbLine too const int Categories[TEXTUREFONT_NBCATEGORY] = { 8, 16, 24, 32, 64 }; const int NbLine[TEXTUREFONT_NBCATEGORY] = { 8, 24, 16, 4, 1 }; // Based on textsize /* const int TextureSizeX = 256; const int TextureSizeY = 256; const int Categories[TEXTUREFONT_NBCATEGORY] = { 8, 16, 24, 32 }; const int NbLine[TEXTUREFONT_NBCATEGORY] = { 4, 6, 4, 1 }; // Based on textsize */ // --------------------------------------------------------------------------- inline uint32 CTextureFont::SLetterKey::getVal() { // this limits Size to 6bits // Large sizes already render wrong when many // different glyphs are used due to limited texture atlas uint8 eb = ((uint)Embolden) + ((uint)Oblique << 1); if (FontGenerator == NULL) return Char + ((Size&255)<<16) + (eb << 22); else return Char + ((Size&255)<<16) + (eb << 22) + ((FontGenerator->getUID()&0xFF)<<24); } // --------------------------------------------------------------------------- CTextureFont::CTextureFont() { uint i; setFilterMode (ITexture::Linear, ITexture::LinearMipMapOff); setWrapS (ITexture::Repeat); setWrapT (ITexture::Repeat); setUploadFormat (Alpha); setReleasable (false); resize (TextureSizeX, TextureSizeY, CBitmap::Alpha); for(i = 0; i < TextureSizeX*TextureSizeY; ++i) getPixels()[i] = 0; // convertToType (CBitmap::Alpha); sint posY = 0; for(i = 0; i < TEXTUREFONT_NBCATEGORY; ++i) { // Number of chars per cache Letters[i].resize ((TextureSizeX/Categories[i])*NbLine[i]); for(uint32 j = 0; j < Letters[i].size(); ++j) { SLetterInfo &rLetter = Letters[i][j]; rLetter.Char = 0xffff; rLetter.FontGenerator = NULL; rLetter.Size= 0; rLetter.Embolden = false; rLetter.Oblique = false; // The less recently used infos if (j < Letters[i].size()-1) rLetter.Next = &Letters[i][j+1]; else rLetter.Next = NULL; if (j > 0) rLetter.Prev = &Letters[i][j-1]; else rLetter.Prev = NULL; rLetter.Cat = i; sint sizeX = TextureSizeX/Categories[i]; rLetter.U = (Categories[i]*(j%sizeX)) / ((float)TextureSizeX); rLetter.V = (posY + Categories[i]*((sint)(j/sizeX))) / ((float)TextureSizeY); ///////////////////////////////////////////////// rLetter.CharWidth = rLetter.CharHeight = 0; rLetter.GlyphIndex = rLetter.Top = rLetter.Left = rLetter.AdvX = 0; } Front[i] = &Letters[i][0]; Back[i] = &Letters[i][Letters[i].size()-1]; posY += NbLine[i] * Categories[i]; } } CTextureFont::~CTextureFont() { } // --------------------------------------------------------------------------- void CTextureFont::dumpTextureFont(const char *filename) { CBitmap b; COFile f( filename ); b.resize (TextureSizeX, TextureSizeY, CBitmap::RGBA); CObjectVector&bits = b.getPixels(); CObjectVector&src = getPixels(); for (uint i = 0; i < (TextureSizeX*TextureSizeY); ++i) { bits[i*4+0] = bits[i*4+1] = bits[i*4+2] = bits[i*4+3] = src[i]; } b.writeTGA (f, 32); } // --------------------------------------------------------------------------- // cat : categories where the letter is // x : pos x of the letter // y : pos y of the letter void CTextureFont::rebuildLetter (sint cat, sint x, sint y) { sint sizex = TextureSizeX / Categories[cat]; sint index = x + y*sizex; SLetterInfo &rLetter = Letters[cat][index]; if (rLetter.FontGenerator == NULL) return; sint catTopY = 0; sint c = 0; while (c < cat) { catTopY += NbLine[c] * Categories[c]; ++c; } // Destination position in pixel of the letter sint posx = x * Categories[cat]; sint posy = catTopY + y * Categories[cat]; uint32 pitch = 0; uint8 *bitmap = rLetter.FontGenerator->getBitmap ( rLetter.Char, rLetter.Size, rLetter.Embolden, rLetter.Oblique, rLetter.CharWidth, rLetter.CharHeight, pitch, rLetter.Left, rLetter.Top, rLetter.AdvX, rLetter.GlyphIndex ); // Copy FreeType buffer uint i; for (i = 0; i < rLetter.CharHeight; ++i) { uint8 *pDst = &_Data[0][posx + (posy+i)*TextureSizeY]; uint8 *pSrc = &bitmap[i*pitch]; for (uint j = 0; j < rLetter.CharWidth; ++j) { *pDst = *pSrc; ++pDst; ++pSrc; } } // Black border bottom and right for (i = 0; i < rLetter.CharHeight+1; ++i) { _Data[0][posx + rLetter.CharWidth + (posy+i)*TextureSizeY] = 0; } for (i = 0; i < rLetter.CharWidth+1; ++i) { _Data[0][posx + i + (posy+rLetter.CharHeight)*TextureSizeY] = 0; } /* dumpTextureFont (this); int a = 5; a++; */ } // --------------------------------------------------------------------------- void CTextureFont::doGenerate(bool async) { // Rectangle invalidate ? if (_ListInvalidRect.begin()!=_ListInvalidRect.end()) { // Yes, rebuild only those rectangles. // For each rectangle to compute std::list::iterator ite=_ListInvalidRect.begin(); while (ite!=_ListInvalidRect.end()) { // Compute rectangle coordinates sint x = ite->left(); sint y = ite->bottom(); // Look in which category is the rectangle sint cat = 0; sint catTopY = 0; sint catBotY = NbLine[cat] * Categories[cat]; while (y > catBotY) { if (y < catBotY) break; ++cat; nlassert (cat < TEXTUREFONT_NBCATEGORY); catTopY = catBotY; catBotY += NbLine[cat] * Categories[cat]; } x = x / Categories[cat]; y = ite->top(); y = y - catTopY; y = y / Categories[cat]; rebuildLetter (cat, x, y); // Next rectangle ite++; } } else { for(int cat = 0; cat < TEXTUREFONT_NBCATEGORY; ++cat) { sint sizex = TextureSizeX / Categories[cat]; sint sizey = NbLine[cat]; for (sint y = 0; y < sizey; y++) for (sint x = 0; x < sizex; x++) { rebuildLetter (cat, x, y); } } } /* dumpTextureFont (this); int a = 5; */ } // --------------------------------------------------------------------------- CTextureFont::SLetterInfo* CTextureFont::getLetterInfo (SLetterKey& k) { sint cat; uint32 nTmp = k.getVal(); map::iterator itAccel = Accel.find (nTmp); if (itAccel != Accel.end()) { // Put it in the first place SLetterInfo *pLetterToMove = itAccel->second; cat = pLetterToMove->Cat; if (pLetterToMove != Front[cat]) { // unlink nlassert(pLetterToMove->Prev); pLetterToMove->Prev->Next = pLetterToMove->Next; if (pLetterToMove == Back[cat]) { Back[cat] = pLetterToMove->Prev; } else { pLetterToMove->Next->Prev = pLetterToMove->Prev; } // link to front pLetterToMove->Prev = NULL; pLetterToMove->Next = Front[cat]; Front[cat]->Prev = pLetterToMove; Front[cat] = pLetterToMove; } return pLetterToMove; } // The letter is not already present // Found the category of the new letter uint32 width, height; //k.FontGenerator->getSizes (k.Char, k.Size, width, height); // \todo mat : Temp !!! Try to use freetype cache uint32 nPitch, nGlyphIndex; sint32 nLeft, nTop, nAdvX; k.FontGenerator->getBitmap (k.Char, k.Size, k.Embolden, k.Oblique, width, height, nPitch, nLeft, nTop, nAdvX, nGlyphIndex ); // Add 1 pixel space for black border to get correct category cat = 0; if (((sint)width+1 > Categories[TEXTUREFONT_NBCATEGORY-1]) || ((sint)height+1 > Categories[TEXTUREFONT_NBCATEGORY-1])) return NULL; while (((sint)width+1 > Categories[cat]) || ((sint)height+1 > Categories[cat])) { ++cat; nlassert (cat != TEXTUREFONT_NBCATEGORY); } // And replace the less recently used letter SLetterKey k2; k2.Char = Back[cat]->Char; k2.FontGenerator = Back[cat]->FontGenerator; k2.Size = Back[cat]->Size; k2.Embolden = Back[cat]->Embolden; k2.Oblique = Back[cat]->Oblique; itAccel = Accel.find (k2.getVal()); if (itAccel != Accel.end()) { Accel.erase (itAccel); } SLetterInfo *NewBack = Back[cat]->Prev; NewBack->Next = NULL; Back[cat]->Cat = cat; Back[cat]->Char = k.Char; Back[cat]->FontGenerator = k.FontGenerator; Back[cat]->Size = k.Size; Back[cat]->Embolden = k.Embolden; Back[cat]->Oblique = k.Oblique; Back[cat]->CharWidth = width; Back[cat]->CharHeight = height; Back[cat]->Top = nTop; Back[cat]->Left = nLeft; Back[cat]->AdvX = nAdvX; Back[cat]->Prev = NULL; Back[cat]->Next = Front[cat]; Front[cat]->Prev = Back[cat]; Front[cat] = Back[cat]; Back[cat] = NewBack; Accel.insert (map::value_type(k.getVal(),Front[cat])); // Invalidate the zone sint index = (sint)(Front[cat] - &Letters[cat][0]);// / sizeof (SLetterInfo); sint sizex = TextureSizeX / Categories[cat]; sint x = index % sizex; sint y = index / sizex; x = x * Categories[cat]; y = y * Categories[cat]; sint c = 0; while (c < cat) { y = y + NbLine[c] * Categories[c]; ++c; } // must update the char, WITH the black borders CRect r (x, y, width+1, height+1); touchRect (r); return Front[cat]; } } // NL3D