flightgear/utils/fgpanel/FGFontCache.cxx
2022-10-20 20:29:11 +08:00

187 lines
6.6 KiB
C++

//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <math.h>
#include "FGGLApplication.hxx"
#include "ApplicationProperties.hxx"
#include "FGFontCache.hxx"
////////////////////////////////////////////////////////////////////////
// FGFontCache class.
////////////////////////////////////////////////////////////////////////
const unsigned short FGFontCache::First_Printable_Char = 32;
const unsigned short FGFontCache::Last_Printable_Char = 127;
FGFontCache::FGFontCache () :
m_Face_Map (),
m_Current_Face_Ptr (NULL),
m_Pos_Map (),
m_Current_Pos (0) {
if (FT_Init_FreeType (&m_Ft)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Could not init freetype library");
}
for (unsigned int Index = 0; Index < Texture_Size * Texture_Size; ++Index) {
m_Texture[Index] = char (0);
}
glGenTextures (1, &m_Glyph_Texture);
}
FGFontCache::~FGFontCache () {
for (Face_Map_Type::iterator It = m_Face_Map.begin (); It != m_Face_Map.end (); ++It) {
delete (It->second);
}
m_Face_Map.clear ();
glDeleteTextures (1, &m_Glyph_Texture);
}
bool
FGFontCache::Set_Font (const string& Font_Name,
const float Size,
GLuint &Glyph_Texture) {
if (m_Face_Map.find (Font_Name) != m_Face_Map.end ()) {
m_Current_Face_Ptr = m_Face_Map[Font_Name];
} else {
FT_Face * const Face_Ptr (new FT_Face);
if (FT_New_Face (m_Ft, Font_Name.c_str (), 0, Face_Ptr)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Could not open font : " + Font_Name);
return false;
}
m_Face_Map.insert (pair <string, FT_Face *> (Font_Name, Face_Ptr));
m_Current_Face_Ptr = Face_Ptr;
}
if (m_Current_Face_Ptr != NULL) {
FT_Set_Pixel_Sizes (*m_Current_Face_Ptr, 0, Size);
} else {
return false;
}
const string Key_Str (Font_Name + "_" + Get_Size (Size));
if (m_Pos_Map.find (Key_Str) != m_Pos_Map.end ()) {
m_Current_Pos = m_Pos_Map[Key_Str];
} else {
m_Current_Pos = m_Pos_Map.size ();
for (unsigned short ASCII_Code = First_Printable_Char;
ASCII_Code < Last_Printable_Char;
++ASCII_Code) {
if (FT_Load_Char (*m_Current_Face_Ptr, char (ASCII_Code), FT_LOAD_RENDER)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Could not load character : " << char (ASCII_Code));
} else {
unsigned int Line;
unsigned int Column;
const FT_GlyphSlot Glyph ((*m_Current_Face_Ptr)->glyph);
for (unsigned int Row = 0; Row < Glyph->bitmap.rows; ++Row) {
for (unsigned int Width = 0; Width < Glyph->bitmap.width; ++Width) {
Get_Pos (ASCII_Code, Row, Width, Line, Column);
m_Texture[Line * Texture_Size + Column] =
Glyph->bitmap.buffer[Row * Glyph->bitmap.width + Width];
}
}
}
}
glBindTexture (GL_TEXTURE_2D, m_Glyph_Texture);
/* We require 1 byte alignment when uploading texture data */
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
/* Clamping to edges is important to prevent artifacts when scaling */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
/* Linear filtering usually looks best for text */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D (GL_TEXTURE_2D,
0,
GL_ALPHA,
Texture_Size,
Texture_Size,
0,
GL_ALPHA,
GL_UNSIGNED_BYTE,
m_Texture);
m_Pos_Map.insert (pair <string, unsigned int> (Key_Str, m_Current_Pos));
}
Glyph_Texture = m_Glyph_Texture;
return true;
}
bool
FGFontCache::Get_Char (const char Char,
int &X, int &Y,
int &Left, int &Bottom,
int &W, int &H,
double &X1, double &Y1,
double &X2, double &Y2) const {
if (m_Current_Face_Ptr != NULL) {
if (FT_Load_Char (*m_Current_Face_Ptr, Char, FT_LOAD_RENDER)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Could not load character : " + Char);
return false;
} else {
const FT_GlyphSlot Glyph ((*m_Current_Face_Ptr)->glyph);
Left = Glyph->bitmap_left;
W = Glyph->bitmap.width;
H = Glyph->bitmap.rows;
Bottom = Glyph->bitmap_top - H;
X += (Glyph->advance.x >> 6);
Y += (Glyph->advance.y >> 6);
Get_Relative_Pos (int (Char), 0, 0, X1, Y1);
Get_Relative_Pos (int (Char), Glyph->bitmap.rows, Glyph->bitmap.width, X2, Y2);
return true;
}
} else {
return false;
}
}
void
FGFontCache::Get_Pos (const unsigned short ASCII_Code,
const unsigned short Row,
const unsigned short Width,
unsigned int &Line,
unsigned int &Column) const {
const unsigned short Font_Size (32);
Line = (((((ASCII_Code - First_Printable_Char) * Font_Size) / Texture_Size) + 3 * m_Current_Pos) * Font_Size) + Row;
Column = (((ASCII_Code - First_Printable_Char) * Font_Size) % Texture_Size) + Width;
}
void
FGFontCache::Get_Relative_Pos (const unsigned short ASCII_Code,
const unsigned short Row,
const unsigned short Width,
double &X,
double &Y) const {
unsigned int Line;
unsigned int Column;
Get_Pos (ASCII_Code, Row, Width, Line, Column);
X = double (Column) / double (Texture_Size);
Y = double (Line) / double (Texture_Size);
}
string
FGFontCache::Get_Size (const float Size) {
const int Half_Size (int (round (2.0 * Size)));
const int Int_Part (Half_Size / 2);
const int Dec_Part ((Half_Size % 2) ? 5 : 0);
stringstream Result_SS;
Result_SS << Int_Part << "." << Dec_Part;
return Result_SS.str ();
}