#include "Riostream.h"
#include "RConfigure.h"
#include "TGLFontManager.h"
#include "TVirtualX.h"
#include "TMath.h"
#include "TSystem.h"
#include "TEnv.h"
#include "TObjString.h"
#include "TGLUtil.h"
#include "TGLIncludes.h"
#ifndef BUILTIN_FTGL
# include <FTGL/ftgl.h>
#else
# include "FTFont.h"
# include "FTGLExtrdFont.h"
# include "FTGLOutlineFont.h"
# include "FTGLPolygonFont.h"
# include "FTGLTextureFont.h"
# include "FTGLPixmapFont.h"
# include "FTGLBitmapFont.h"
#endif
ClassImp(TGLFont);
TGLFont::TGLFont():
fFont(0), fManager(0), fDepth(0),
fSize(0), fFile(0), fMode(kUndef),
fTrashCount(0)
{
}
TGLFont::TGLFont(Int_t size, Int_t font, EMode mode, FTFont* f, TGLFontManager* mng):
fFont(f), fManager(mng), fDepth(0),
fSize(size), fFile(font), fMode(mode),
fTrashCount(0)
{
}
TGLFont::TGLFont(const TGLFont &o):
fFont(0), fManager(0), fDepth(0), fTrashCount(0)
{
fFont = (FTFont*)o.GetFont();
fSize = o.fSize;
fFile = o.fFile;
fMode = o.fMode;
fTrashCount = o.fTrashCount;
}
TGLFont::~TGLFont()
{
if (fManager) fManager->ReleaseFont(*this);
}
void TGLFont::CopyAttributes(const TGLFont &o)
{
SetFont(o.fFont);
SetManager(o.fManager);
SetDepth(o.fDepth);
fSize = o.fSize;
fFile = o.fFile;
fMode = o.fMode;
fTrashCount = o.fTrashCount;
}
Float_t TGLFont::GetAscent() const
{
return fFont->Ascender();
}
Float_t TGLFont::GetDescent() const
{
return -fFont->Descender();
}
Float_t TGLFont::GetLineHeight() const
{
return fFont->LineHeight();
}
void TGLFont::MeasureBaseLineParams(Float_t& ascent, Float_t& descent, Float_t& line_height,
const char* txt) const
{
Float_t dum, lly, ury;
const_cast<FTFont*>(fFont)->BBox(txt, dum, lly, dum, dum, ury, dum);
ascent = ury;
descent = -lly;
line_height = ury - lly;
}
void TGLFont::BBox(const char* txt,
Float_t& llx, Float_t& lly, Float_t& llz,
Float_t& urx, Float_t& ury, Float_t& urz) const
{
const_cast<FTFont*>(fFont)->BBox(txt, llx, lly, llz, urx, ury, urz);
}
void TGLFont::BBox(const wchar_t* txt,
Float_t& llx, Float_t& lly, Float_t& llz,
Float_t& urx, Float_t& ury, Float_t& urz) const
{
const_cast<FTFont*>(fFont)->BBox(txt, llx, lly, llz, urx, ury, urz);
}
template<class Char>
void TGLFont::RenderHelper(const Char *txt, Double_t x, Double_t y, Double_t angle, Double_t ) const
{
glPushMatrix();
Float_t llx = 0.f, lly = 0.f, llz = 0.f, urx = 0.f, ury = 0.f, urz = 0.f;
BBox(txt, llx, lly, llz, urx, ury, urz);
const Double_t dx = urx - llx, dy = ury - lly;
Double_t xc = 0., yc = 0.;
const UInt_t align = gVirtualX->GetTextAlign();
if (gVirtualX->InheritsFrom("TGCocoa")) {
const UInt_t hAlign = UInt_t(align / 10);
switch (hAlign) {
case 1:
xc = 0.5 * dx;
break;
case 2:
break;
case 3:
xc = -0.5 * dy;
break;
}
const UInt_t vAlign = UInt_t(align % 10);
switch (vAlign) {
case 1:
yc = 0.5 * dy;
break;
case 2:
break;
case 3:
yc = -0.5 * dy;
break;
}
} else {
switch (align) {
case 7:
xc += 0.5 * dx;
yc += 0.5 * dy;
break;
case 8:
yc += 0.5 * dy;
break;
case 9:
xc -= 0.5 * dx;
yc += 0.5 * dy;
break;
case 4:
xc += 0.5 * dx;
break;
case 5:
break;
case 6:
xc = -0.5 * dx;
break;
case 1:
xc += 0.5 * dx;
yc -= 0.5 * dy;
break;
case 2:
yc -= 0.5 * dy;
break;
case 3:
xc -= 0.5 * dx;
yc -= 0.5 * dy;
break;
}
}
glTranslated(x, y, 0.);
glRotated(angle, 0., 0., 1.);
glTranslated(xc, yc, 0.);
glTranslated(-0.5 * dx, -0.5 * dy, 0.);
const_cast<FTFont*>(fFont)->Render(txt);
glPopMatrix();
}
void TGLFont::Render(const wchar_t* txt, Double_t x, Double_t y, Double_t angle, Double_t mgn) const
{
RenderHelper(txt, x, y, angle, mgn);
}
void TGLFont::Render(const char* txt, Double_t x, Double_t y, Double_t angle, Double_t mgn) const
{
RenderHelper(txt, x, y, angle, mgn);
}
void TGLFont::Render(const TString &txt) const
{
Bool_t scaleDepth = (fMode == kExtrude && fDepth != 1.0f);
if (scaleDepth) {
glPushMatrix();
glTranslatef(0.0f, 0.0f, 0.5f*fDepth * 0.2f*fSize);
glScalef(1.0f, 1.0f, fDepth);
}
const_cast<FTFont*>(fFont)->Render(txt);
if (scaleDepth) {
glPopMatrix();
}
}
void TGLFont:: Render(const TString &txt, Float_t x, Float_t y, Float_t z,
ETextAlignH_e alignH, ETextAlignV_e alignV) const
{
glPushMatrix();
glTranslatef(x, y, z);
x=0, y=0;
Float_t llx, lly, llz, urx, ury, urz;
BBox(txt, llx, lly, llz, urx, ury, urz);
switch (alignH)
{
case TGLFont::kRight:
x = -urx;
break;
case TGLFont::kCenterH:
x = -urx*0.5;
break;
default:
break;
};
switch (alignV)
{
case TGLFont::kBottom:
y = -ury;
break;
case TGLFont::kCenterV:
y = -ury*0.5;
break;
default:
break;
};
if (fMode == TGLFont::kPixmap || fMode == TGLFont::kBitmap)
{
glRasterPos2i(0, 0);
glBitmap(0, 0, 0, 0, x, y, 0);
}
else
{
glTranslatef(x, y, 0);
}
Render(txt);
glPopMatrix();
}
void TGLFont::PreRender(Bool_t autoLight, Bool_t lightOn) const
{
switch (fMode)
{
case kBitmap:
case kPixmap:
glPushAttrib(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT);
glEnable(GL_ALPHA_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glAlphaFunc(GL_GEQUAL, 0.0625);
break;
case kTexture:
glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glEnable(GL_ALPHA_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glAlphaFunc(GL_GEQUAL, 0.0625);
break;
case kOutline:
case kPolygon:
case kExtrude:
glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT);
glEnable(GL_NORMALIZE);
glDisable(GL_CULL_FACE);
break;
default:
Warning("TGLFont::PreRender", "Font mode undefined.");
glPushAttrib(GL_LIGHTING_BIT);
break;
}
if ((autoLight && fMode > TGLFont::kOutline) || (!autoLight && lightOn))
glEnable(GL_LIGHTING);
else
glDisable(GL_LIGHTING);
}
void TGLFont::PostRender() const
{
glPopAttrib();
}
ClassImp(TGLFontManager);
TObjArray TGLFontManager::fgFontFileArray;
Int_t TGLFontManager::fgExtendedFontStart;
TGLFontManager::FontSizeVec_t TGLFontManager::fgFontSizeArray;
Bool_t TGLFontManager::fgStaticInitDone = kFALSE;
TGLFontManager::~TGLFontManager()
{
FontMap_i it = fFontMap.begin();
while (it != fFontMap.end()) {
delete it->first.GetFont();
it++;
}
fFontMap.clear();
}
void TGLFontManager::RegisterFont(Int_t sizeIn, Int_t fileID, TGLFont::EMode mode, TGLFont &out)
{
if (fgStaticInitDone == kFALSE) InitStatics();
Int_t size = GetFontSize(sizeIn);
if (mode == out.GetMode() && fileID == out.GetFile() && size == out.GetSize())
return;
FontMap_i it = fFontMap.find(TGLFont(size, fileID, mode));
if (it == fFontMap.end())
{
TString ttpath, file;
# ifdef TTFFONTDIR
ttpath = gEnv->GetValue("Root.TTGLFontPath", TTFFONTDIR );
# else
ttpath = gEnv->GetValue("Root.TTGLFontPath", "$(ROOTSYS)/fonts");
# endif
{
char *fp = gSystem->Which(ttpath, fileID < fgExtendedFontStart ?
((TObjString*)fgFontFileArray[fileID])->String() + ".ttf" :
((TObjString*)fgFontFileArray[fileID])->String());
file = fp;
delete [] fp;
}
FTFont* ftfont = 0;
switch (mode)
{
case TGLFont::kBitmap:
ftfont = new FTGLBitmapFont(file);
break;
case TGLFont::kPixmap:
ftfont = new FTGLPixmapFont(file);
break;
case TGLFont::kOutline:
ftfont = new FTGLOutlineFont(file);
break;
case TGLFont::kPolygon:
ftfont = new FTGLPolygonFont(file);
break;
case TGLFont::kExtrude:
ftfont = new FTGLExtrdFont(file);
ftfont->Depth(0.2*size);
break;
case TGLFont::kTexture:
ftfont = new FTGLTextureFont(file);
break;
default:
Error("TGLFontManager::RegisterFont", "invalid FTGL type");
return;
break;
}
ftfont->FaceSize(size);
const TGLFont &mf = fFontMap.insert(std::make_pair(TGLFont(size, fileID, mode, ftfont, 0), 1)).first->first;
out.CopyAttributes(mf);
}
else
{
if (it->first.GetTrashCount() > 0) {
fFontTrash.remove(&(it->first));
it->first.SetTrashCount(0);
}
++(it->second);
out.CopyAttributes(it->first);
}
out.SetManager(this);
}
void TGLFontManager::RegisterFont(Int_t size, const char* name, TGLFont::EMode mode, TGLFont &out)
{
TObjArray* farr = GetFontFileArray();
TIter next(farr);
TObjString* os;
Int_t cnt = 0;
while ((os = (TObjString*) next()) != 0)
{
if (os->String() == name)
break;
cnt++;
}
if (cnt < farr->GetEntries())
RegisterFont(size, cnt, mode, out);
else
Error("TGLFontManager::RegisterFont", "unknown font name %s", name);
}
void TGLFontManager::ReleaseFont(TGLFont& font)
{
FontMap_i it = fFontMap.find(font);
if (it != fFontMap.end())
{
--(it->second);
if (it->second == 0)
{
assert(it->first.GetTrashCount() == 0);
it->first.IncTrashCount();
fFontTrash.push_back(&it->first);
}
}
}
TObjArray* TGLFontManager::GetFontFileArray()
{
if (fgStaticInitDone == kFALSE) InitStatics();
return &fgFontFileArray;
}
TGLFontManager::FontSizeVec_t* TGLFontManager::GetFontSizeArray()
{
if (fgStaticInitDone == kFALSE) InitStatics();
return &fgFontSizeArray;
}
Int_t TGLFontManager::GetExtendedFontStartIndex()
{
if (fgStaticInitDone == kFALSE) InitStatics();
assert(fgExtendedFontStart > 0 && "GetExtendedFontStartIndex, invalid index");
return fgExtendedFontStart;
}
Int_t TGLFontManager::GetFontSize(Int_t ds)
{
if (fgStaticInitDone == kFALSE) InitStatics();
Int_t idx = TMath::BinarySearch(fgFontSizeArray.size(), &fgFontSizeArray[0],
TMath::CeilNint(ds));
if (idx < 0) idx = 0;
return fgFontSizeArray[idx];
}
Int_t TGLFontManager::GetFontSize(Int_t ds, Int_t min, Int_t max)
{
if (ds < min) ds = min;
if (ds > max) ds = max;
return GetFontSize(ds);
}
const char* TGLFontManager::GetFontNameFromId(Int_t id)
{
if (fgStaticInitDone == kFALSE) InitStatics();
Int_t fontIndex = id / 10;
if (fontIndex > fgFontFileArray.GetEntries() || !fontIndex)
fontIndex = 5;
else
fontIndex -= 1;
TObjString* os = (TObjString*)fgFontFileArray[fontIndex];
return os->String().Data();
}
void TGLFontManager::InitStatics()
{
fgFontFileArray.Add(new TObjString("timesi"));
fgFontFileArray.Add(new TObjString("timesbd"));
fgFontFileArray.Add(new TObjString("timesbi"));
fgFontFileArray.Add(new TObjString("arial"));
fgFontFileArray.Add(new TObjString("ariali"));
fgFontFileArray.Add(new TObjString("arialbd"));
fgFontFileArray.Add(new TObjString("arialbi"));
fgFontFileArray.Add(new TObjString("cour"));
fgFontFileArray.Add(new TObjString("couri"));
fgFontFileArray.Add(new TObjString("courbd"));
fgFontFileArray.Add(new TObjString("courbi"));
fgFontFileArray.Add(new TObjString("symbol"));
fgFontFileArray.Add(new TObjString("times"));
fgFontFileArray.Add(new TObjString("wingding"));
fgFontFileArray.Add(new TObjString("symbol"));
fgExtendedFontStart = fgFontFileArray.GetEntries();
fgFontFileArray.Add(new TObjString("FreeSerifItalic.otf"));
fgFontFileArray.Add(new TObjString("FreeSerifBold.otf"));
fgFontFileArray.Add(new TObjString("FreeSerifBoldItalic.otf"));
fgFontFileArray.Add(new TObjString("FreeSans.otf"));
fgFontFileArray.Add(new TObjString("FreeSansOblique.otf"));
fgFontFileArray.Add(new TObjString("FreeSansBold.otf"));
fgFontFileArray.Add(new TObjString("FreeSansBoldOblique.otf"));
fgFontFileArray.Add(new TObjString("FreeMono.otf"));
fgFontFileArray.Add(new TObjString("FreeMonoOblique.otf"));
fgFontFileArray.Add(new TObjString("FreeMonoBold.otf"));
fgFontFileArray.Add(new TObjString("FreeMonoBoldOblique.otf"));
fgFontFileArray.Add(new TObjString("symbol.ttf"));
fgFontFileArray.Add(new TObjString("FreeSerif.otf"));
fgFontFileArray.Add(new TObjString("wingding.ttf"));
fgFontFileArray.Add(new TObjString("symbol.ttf"));
fgFontFileArray.Add(new TObjString("STIXGeneral.otf"));
fgFontFileArray.Add(new TObjString("STIXGeneralItalic.otf"));
fgFontFileArray.Add(new TObjString("STIXGeneralBol.otf"));
fgFontFileArray.Add(new TObjString("STIXGeneralBolIta.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz1Sym.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz1SymBol.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz2Sym.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz2SymBol.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz3Sym.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz3SymBol.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz4Sym.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz4SymBol.otf"));
fgFontFileArray.Add(new TObjString("STIXSiz5Sym.otf"));
fgFontFileArray.Add(new TObjString("DroidSansFallback.ttf"));
fgFontFileArray.Add(new TObjString("DroidSansFallback.ttf"));
fgFontFileArray.Add(new TObjString("DroidSansFallback.ttf"));
for (Int_t i = 10; i <= 20; i+=2)
fgFontSizeArray.push_back(i);
for (Int_t i = 24; i <= 64; i+=4)
fgFontSizeArray.push_back(i);
for (Int_t i = 72; i <= 128; i+=8)
fgFontSizeArray.push_back(i);
fgStaticInitDone = kTRUE;
}
void TGLFontManager::ClearFontTrash()
{
FontList_i it = fFontTrash.begin();
while (it != fFontTrash.end())
{
if ((*it)->IncTrashCount() > 10000)
{
FontMap_i mi = fFontMap.find(**it);
assert(mi != fFontMap.end());
fFontMap.erase(mi);
delete (*it)->GetFont();
FontList_i li = it++;
fFontTrash.erase(li);
}
else
{
++it;
}
}
}