1457 lines
32 KiB
C++
1457 lines
32 KiB
C++
/**
|
|
* @file llfontgl.cpp
|
|
* @brief Wrapper around FreeType
|
|
*
|
|
* Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
|
|
* $License$
|
|
*/
|
|
|
|
#include "linden_common.h"
|
|
|
|
#include <boost/tokenizer.hpp>
|
|
|
|
#include "llfont.h"
|
|
#include "llfontgl.h"
|
|
#include "llgl.h"
|
|
#include "v4color.h"
|
|
#include "llstl.h"
|
|
|
|
const S32 BOLD_OFFSET = 1;
|
|
|
|
// static class members
|
|
F32 LLFontGL::sVertDPI = 96.f;
|
|
F32 LLFontGL::sHorizDPI = 96.f;
|
|
F32 LLFontGL::sScaleX = 1.f;
|
|
F32 LLFontGL::sScaleY = 1.f;
|
|
LLString LLFontGL::sAppDir;
|
|
|
|
LLFontGL* LLFontGL::sMonospace = NULL;
|
|
LLFontGL* LLFontGL::sSansSerifSmall = NULL;
|
|
LLFontGL* LLFontGL::sSansSerif = NULL;
|
|
LLFontGL* LLFontGL::sSansSerifBig = NULL;
|
|
LLFontGL* LLFontGL::sSansSerifHuge = NULL;
|
|
LLFontGL* LLFontGL::sSansSerifBold = NULL;
|
|
LLFontList* LLFontGL::sSSFallback = NULL;
|
|
LLFontList* LLFontGL::sSSSmallFallback = NULL;
|
|
LLFontList* LLFontGL::sSSBigFallback = NULL;
|
|
LLFontList* LLFontGL::sSSHugeFallback = NULL;
|
|
LLFontList* LLFontGL::sSSBoldFallback = NULL;
|
|
LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f);
|
|
|
|
LLCoordFont LLFontGL::sCurOrigin;
|
|
std::vector<LLCoordFont> LLFontGL::sOriginStack;
|
|
|
|
LLFontGL*& gExtCharFont = LLFontGL::sSansSerif;
|
|
|
|
const F32 EXT_X_BEARING = 1.f;
|
|
const F32 EXT_Y_BEARING = 0.f;
|
|
const F32 EXT_KERNING = 1.f;
|
|
const F32 PIXEL_BORDER_THRESHOLD = 0.0001f;
|
|
const F32 PIXEL_CORRECTION_DISTANCE = 0.01f;
|
|
|
|
const F32 PAD_AMT = 0.5f;
|
|
|
|
F32 llfont_round_x(F32 x)
|
|
{
|
|
//return llfloor((x-LLFontGL::sCurOrigin.mX)/LLFontGL::sScaleX+0.5f)*LLFontGL::sScaleX+LLFontGL::sCurOrigin.mX;
|
|
//return llfloor(x/LLFontGL::sScaleX+0.5f)*LLFontGL::sScaleY;
|
|
return x;
|
|
}
|
|
|
|
F32 llfont_round_y(F32 y)
|
|
{
|
|
//return llfloor((y-LLFontGL::sCurOrigin.mY)/LLFontGL::sScaleY+0.5f)*LLFontGL::sScaleY+LLFontGL::sCurOrigin.mY;
|
|
//return llfloor(y+0.5f);
|
|
return y;
|
|
}
|
|
|
|
// static
|
|
U8 LLFontGL::getStyleFromString(const LLString &style)
|
|
{
|
|
S32 ret = 0;
|
|
if (style.find("NORMAL") != style.npos)
|
|
{
|
|
ret |= NORMAL;
|
|
}
|
|
if (style.find("BOLD") != style.npos)
|
|
{
|
|
ret |= BOLD;
|
|
}
|
|
if (style.find("ITALIC") != style.npos)
|
|
{
|
|
ret |= ITALIC;
|
|
}
|
|
if (style.find("UNDERLINE") != style.npos)
|
|
{
|
|
ret |= UNDERLINE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
LLFontGL::LLFontGL()
|
|
: LLFont()
|
|
{
|
|
init();
|
|
clearEmbeddedChars();
|
|
}
|
|
|
|
LLFontGL::LLFontGL(const LLFontGL &source)
|
|
{
|
|
llerrs << "Not implemented!" << llendl;
|
|
}
|
|
|
|
LLFontGL::~LLFontGL()
|
|
{
|
|
mImageGLp = NULL;
|
|
mRawImageGLp = NULL;
|
|
clearEmbeddedChars();
|
|
}
|
|
|
|
void LLFontGL::init()
|
|
{
|
|
if (mImageGLp.isNull())
|
|
{
|
|
mImageGLp = new LLImageGL(FALSE);
|
|
//RN: use nearest mipmap filtering to obviate the need to do pixel-accurate positioning
|
|
mImageGLp->bind();
|
|
mImageGLp->setMipFilterNearest(TRUE, TRUE);
|
|
}
|
|
if (mRawImageGLp.isNull())
|
|
{
|
|
mRawImageGLp = new LLImageRaw; // Note LLFontGL owns the image, not LLFont.
|
|
}
|
|
setRawImage( mRawImageGLp );
|
|
}
|
|
|
|
void LLFontGL::reset()
|
|
{
|
|
init();
|
|
resetBitmap();
|
|
}
|
|
|
|
// static
|
|
LLString LLFontGL::getFontPathSystem()
|
|
{
|
|
LLString system_path;
|
|
|
|
// Try to figure out where the system's font files are stored.
|
|
char *system_root = NULL;
|
|
#if LL_WINDOWS
|
|
system_root = getenv("SystemRoot"); /* Flawfinder: ignore */
|
|
if (!system_root)
|
|
{
|
|
llwarns << "SystemRoot not found, attempting to load fonts from default path." << llendl;
|
|
}
|
|
#endif
|
|
|
|
if (system_root)
|
|
{
|
|
system_path = llformat("%s/fonts/", system_root);
|
|
}
|
|
else
|
|
{
|
|
#if LL_WINDOWS
|
|
// HACK for windows 98/Me
|
|
system_path = "/WINDOWS/FONTS/";
|
|
#elif LL_DARWIN
|
|
// HACK for Mac OS X
|
|
system_path = "/System/Library/Fonts/";
|
|
#endif
|
|
}
|
|
return system_path;
|
|
}
|
|
|
|
|
|
// static
|
|
LLString LLFontGL::getFontPathLocal()
|
|
{
|
|
LLString local_path;
|
|
|
|
// Backup files if we can't load from system fonts directory.
|
|
// We could store this in an end-user writable directory to allow
|
|
// end users to switch fonts.
|
|
if (LLFontGL::sAppDir.length())
|
|
{
|
|
// use specified application dir to look for fonts
|
|
local_path = LLFontGL::sAppDir + "/fonts/";
|
|
}
|
|
else
|
|
{
|
|
// assume working directory is executable directory
|
|
local_path = "./fonts/";
|
|
}
|
|
return local_path;
|
|
}
|
|
|
|
//static
|
|
bool LLFontGL::loadFaceFallback(LLFontList *fontlistp, const LLString& fontname, const F32 point_size)
|
|
{
|
|
LLString local_path = getFontPathLocal();
|
|
LLString sys_path = getFontPathSystem();
|
|
|
|
// The fontname string may contain multiple font file names separated by semicolons.
|
|
// Break it apart and try loading each one, in order.
|
|
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
|
boost::char_separator<char> sep(";");
|
|
tokenizer tokens(fontname, sep);
|
|
tokenizer::iterator token_iter;
|
|
|
|
for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
|
|
{
|
|
LLFont *fontp = new LLFont();
|
|
LLString font_path = local_path + *token_iter;
|
|
if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI, 2, TRUE))
|
|
{
|
|
font_path = sys_path + *token_iter;
|
|
if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI, 2, TRUE))
|
|
{
|
|
llwarns << "Couldn't load font " << *token_iter << llendl;
|
|
delete fontp;
|
|
fontp = NULL;
|
|
}
|
|
}
|
|
|
|
if(fontp)
|
|
{
|
|
fontlistp->addAtEnd(fontp);
|
|
}
|
|
}
|
|
|
|
// We want to return true if at least one fallback font loaded correctly.
|
|
return (fontlistp->size() > 0);
|
|
}
|
|
|
|
//static
|
|
bool LLFontGL::loadFace(LLFontGL *fontp, const LLString& fontname, const F32 point_size, LLFontList *fallback_fontp)
|
|
{
|
|
LLString local_path = getFontPathLocal();
|
|
LLString font_path = local_path + fontname;
|
|
if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI))
|
|
{
|
|
LLString sys_path = getFontPathSystem();
|
|
font_path = sys_path + fontname;
|
|
if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI))
|
|
{
|
|
llwarns << "Couldn't load font " << fontname << llendl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fontp->setFallbackFont(fallback_fontp);
|
|
return true;
|
|
}
|
|
|
|
|
|
// static
|
|
BOOL LLFontGL::initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale,
|
|
const LLString& monospace_file, F32 monospace_size,
|
|
const LLString& sansserif_file,
|
|
const LLString& sanserif_fallback_file, F32 ss_fallback_scale,
|
|
F32 small_size, F32 medium_size, F32 big_size, F32 huge_size,
|
|
const LLString& sansserif_bold_file, F32 bold_size,
|
|
const LLString& app_dir)
|
|
{
|
|
BOOL failed = FALSE;
|
|
sVertDPI = (F32)llfloor(screen_dpi * y_scale);
|
|
sHorizDPI = (F32)llfloor(screen_dpi * x_scale);
|
|
sScaleX = x_scale;
|
|
sScaleY = y_scale;
|
|
sAppDir = app_dir;
|
|
|
|
//
|
|
// Monospace font
|
|
//
|
|
|
|
if (!sMonospace)
|
|
{
|
|
sMonospace = new LLFontGL();
|
|
}
|
|
else
|
|
{
|
|
sMonospace->reset();
|
|
}
|
|
|
|
failed |= !loadFace(sMonospace, monospace_file, monospace_size, NULL);
|
|
|
|
//
|
|
// Sans-serif fonts
|
|
//
|
|
if(!sSansSerifHuge)
|
|
{
|
|
sSansSerifHuge = new LLFontGL();
|
|
}
|
|
else
|
|
{
|
|
sSansSerifHuge->reset();
|
|
}
|
|
|
|
if (sSSHugeFallback)
|
|
{
|
|
delete sSSHugeFallback;
|
|
}
|
|
sSSHugeFallback = new LLFontList();
|
|
if (!loadFaceFallback(
|
|
sSSHugeFallback,
|
|
sanserif_fallback_file,
|
|
huge_size*ss_fallback_scale))
|
|
{
|
|
delete sSSHugeFallback;
|
|
sSSHugeFallback = NULL;
|
|
}
|
|
|
|
failed |= !loadFace(sSansSerifHuge, sansserif_file, huge_size, sSSHugeFallback);
|
|
|
|
|
|
if(!sSansSerifBig)
|
|
{
|
|
sSansSerifBig = new LLFontGL();
|
|
}
|
|
else
|
|
{
|
|
sSansSerifBig->reset();
|
|
}
|
|
|
|
if (sSSBigFallback)
|
|
{
|
|
delete sSSBigFallback;
|
|
}
|
|
sSSBigFallback = new LLFontList();
|
|
if (!loadFaceFallback(
|
|
sSSBigFallback,
|
|
sanserif_fallback_file,
|
|
big_size*ss_fallback_scale))
|
|
{
|
|
delete sSSBigFallback;
|
|
sSSBigFallback = NULL;
|
|
}
|
|
|
|
failed |= !loadFace(sSansSerifBig, sansserif_file, big_size, sSSBigFallback);
|
|
|
|
|
|
if(!sSansSerif)
|
|
{
|
|
sSansSerif = new LLFontGL();
|
|
}
|
|
else
|
|
{
|
|
sSansSerif->reset();
|
|
}
|
|
|
|
if (sSSFallback)
|
|
{
|
|
delete sSSFallback;
|
|
}
|
|
sSSFallback = new LLFontList();
|
|
if (!loadFaceFallback(
|
|
sSSFallback,
|
|
sanserif_fallback_file,
|
|
medium_size*ss_fallback_scale))
|
|
{
|
|
delete sSSFallback;
|
|
sSSFallback = NULL;
|
|
}
|
|
failed |= !loadFace(sSansSerif, sansserif_file, medium_size, sSSFallback);
|
|
|
|
|
|
if(!sSansSerifSmall)
|
|
{
|
|
sSansSerifSmall = new LLFontGL();
|
|
}
|
|
else
|
|
{
|
|
sSansSerifSmall->reset();
|
|
}
|
|
|
|
if(sSSSmallFallback)
|
|
{
|
|
delete sSSSmallFallback;
|
|
}
|
|
sSSSmallFallback = new LLFontList();
|
|
if (!loadFaceFallback(
|
|
sSSSmallFallback,
|
|
sanserif_fallback_file,
|
|
small_size*ss_fallback_scale))
|
|
{
|
|
delete sSSSmallFallback;
|
|
sSSSmallFallback = NULL;
|
|
}
|
|
failed |= !loadFace(sSansSerifSmall, sansserif_file, small_size, sSSSmallFallback);
|
|
|
|
|
|
//
|
|
// Sans-serif bold
|
|
//
|
|
if(!sSansSerifBold)
|
|
{
|
|
sSansSerifBold = new LLFontGL();
|
|
}
|
|
else
|
|
{
|
|
sSansSerifBold->reset();
|
|
}
|
|
|
|
if (sSSBoldFallback)
|
|
{
|
|
delete sSSBoldFallback;
|
|
}
|
|
sSSBoldFallback = new LLFontList();
|
|
if (!loadFaceFallback(
|
|
sSSBoldFallback,
|
|
sanserif_fallback_file,
|
|
medium_size*ss_fallback_scale))
|
|
{
|
|
delete sSSBoldFallback;
|
|
sSSBoldFallback = NULL;
|
|
}
|
|
failed |= !loadFace(sSansSerifBold, sansserif_bold_file, medium_size, sSSBoldFallback);
|
|
|
|
return !failed;
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
void LLFontGL::destroyDefaultFonts()
|
|
{
|
|
delete sMonospace;
|
|
sMonospace = NULL;
|
|
|
|
delete sSansSerifHuge;
|
|
sSansSerifHuge = NULL;
|
|
|
|
delete sSansSerifBig;
|
|
sSansSerifBig = NULL;
|
|
|
|
delete sSansSerif;
|
|
sSansSerif = NULL;
|
|
|
|
delete sSansSerifSmall;
|
|
sSansSerifSmall = NULL;
|
|
|
|
delete sSansSerifBold;
|
|
sSansSerifBold = NULL;
|
|
|
|
delete sSSHugeFallback;
|
|
sSSHugeFallback = NULL;
|
|
|
|
delete sSSBigFallback;
|
|
sSSBigFallback = NULL;
|
|
|
|
delete sSSFallback;
|
|
sSSFallback = NULL;
|
|
|
|
delete sSSSmallFallback;
|
|
sSSSmallFallback = NULL;
|
|
|
|
delete sSSBoldFallback;
|
|
sSSBoldFallback = NULL;
|
|
}
|
|
|
|
//static
|
|
void LLFontGL::destroyGL()
|
|
{
|
|
if (!sMonospace)
|
|
{
|
|
// Already all destroyed.
|
|
return;
|
|
}
|
|
sMonospace->mImageGLp->destroyGLTexture();
|
|
sSansSerifHuge->mImageGLp->destroyGLTexture();
|
|
sSansSerifSmall->mImageGLp->destroyGLTexture();
|
|
sSansSerif->mImageGLp->destroyGLTexture();
|
|
sSansSerifBig->mImageGLp->destroyGLTexture();
|
|
sSansSerifBold->mImageGLp->destroyGLTexture();
|
|
}
|
|
|
|
|
|
|
|
LLFontGL &LLFontGL::operator=(const LLFontGL &source)
|
|
{
|
|
llerrs << "Not implemented" << llendl;
|
|
return *this;
|
|
}
|
|
|
|
BOOL LLFontGL::loadFace(const LLString& filename, const F32 point_size, const F32 vert_dpi, const F32 horz_dpi)
|
|
{
|
|
if (!LLFont::loadFace(filename, point_size, vert_dpi, horz_dpi, 2, FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
mImageGLp->createGLTexture(0, mRawImageGLp);
|
|
mImageGLp->bind();
|
|
mImageGLp->setMipFilterNearest(TRUE, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL LLFontGL::addChar(const llwchar wch)
|
|
{
|
|
if (!LLFont::addChar(wch))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
stop_glerror();
|
|
mImageGLp->setSubImage(mRawImageGLp, 0, 0, mImageGLp->getWidth(), mImageGLp->getHeight());
|
|
mImageGLp->bind();
|
|
mImageGLp->setMipFilterNearest(TRUE, TRUE);
|
|
stop_glerror();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
S32 LLFontGL::renderUTF8(const LLString &text, const S32 offset,
|
|
const F32 x, const F32 y,
|
|
const LLColor4 &color,
|
|
const HAlign halign, const VAlign valign,
|
|
U8 style,
|
|
const S32 max_chars, const S32 max_pixels,
|
|
F32* right_x,
|
|
BOOL use_ellipses) const
|
|
{
|
|
LLWString wstr = utf8str_to_wstring(text);
|
|
return render(wstr, offset, x, y, color, halign, valign, style, max_chars, max_pixels, right_x, use_ellipses);
|
|
}
|
|
|
|
S32 LLFontGL::render(const LLWString &wstr,
|
|
const S32 begin_offset,
|
|
const F32 x, const F32 y,
|
|
const LLColor4 &color,
|
|
const HAlign halign, const VAlign valign,
|
|
U8 style,
|
|
const S32 max_chars, S32 max_pixels,
|
|
F32* right_x,
|
|
BOOL use_embedded,
|
|
BOOL use_ellipses) const
|
|
{
|
|
LLGLEnable texture_2d(GL_TEXTURE_2D);
|
|
|
|
if (wstr.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (style & DROP_SHADOW)
|
|
{
|
|
LLColor4 shadow_color = sShadowColor;
|
|
shadow_color[3] = color[3];
|
|
render(wstr, begin_offset,
|
|
x + 1.f / sScaleX,
|
|
y - 1.f / sScaleY,
|
|
shadow_color,
|
|
halign,
|
|
valign,
|
|
style & (~DROP_SHADOW),
|
|
max_chars,
|
|
max_pixels,
|
|
right_x,
|
|
use_embedded,
|
|
use_ellipses);
|
|
}
|
|
|
|
S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX);
|
|
|
|
BOOL render_bold = FALSE;
|
|
|
|
// HACK for better bolding
|
|
if (style & BOLD)
|
|
{
|
|
if (this == LLFontGL::sSansSerif)
|
|
{
|
|
return LLFontGL::sSansSerifBold->render(
|
|
wstr, begin_offset,
|
|
x, y,
|
|
color,
|
|
halign, valign,
|
|
(style & ~BOLD),
|
|
max_chars, max_pixels,
|
|
right_x, use_embedded);
|
|
}
|
|
else
|
|
{
|
|
render_bold = TRUE;
|
|
}
|
|
}
|
|
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glTranslatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ);
|
|
//glScalef(sScaleX, sScaleY, 1.0f);
|
|
|
|
// avoid half pixels
|
|
// RN: if we're going to this trouble, might as well snap to nearest pixel all the time
|
|
// but the plan is to get rid of this so that fonts "just work"
|
|
//F32 half_pixel_distance = llabs(fmodf(sCurOrigin.mX * sScaleX, 1.f) - 0.5f);
|
|
//if (half_pixel_distance < PIXEL_BORDER_THRESHOLD)
|
|
//{
|
|
glTranslatef(PIXEL_CORRECTION_DISTANCE*sScaleX, 0.f, 0.f);
|
|
//}
|
|
|
|
// this code would just snap to pixel grid, although it seems to introduce more jitter
|
|
//F32 pixel_offset_x = llround(sCurOrigin.mX * sScaleX) - (sCurOrigin.mX * sScaleX);
|
|
//F32 pixel_offset_y = llround(sCurOrigin.mY * sScaleY) - (sCurOrigin.mY * sScaleY);
|
|
//glTranslatef(-pixel_offset_x, -pixel_offset_y, 0.f);
|
|
|
|
// scale back to native pixel size
|
|
//glScalef(1.f / sScaleX, 1.f / sScaleY, 1.f);
|
|
//glScaled(1.0 / (F64) sScaleX, 1.0 / (F64) sScaleY, 1.0f);
|
|
LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS);
|
|
|
|
glColor4fv( color.mV );
|
|
|
|
S32 chars_drawn = 0;
|
|
S32 i;
|
|
S32 length;
|
|
|
|
if (-1 == max_chars)
|
|
{
|
|
length = (S32)wstr.length() - begin_offset;
|
|
}
|
|
else
|
|
{
|
|
length = llmin((S32)wstr.length() - begin_offset, max_chars );
|
|
}
|
|
|
|
F32 cur_x, cur_y, cur_render_x, cur_render_y;
|
|
F32 slant_offset;
|
|
|
|
slant_offset = ((style & ITALIC) ? ( -mAscender * 0.25f) : 0.f);
|
|
|
|
// Bind the font texture
|
|
|
|
mImageGLp->bind(0);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Not guaranteed to be set correctly
|
|
|
|
cur_x = ((F32)x * sScaleX);
|
|
cur_y = ((F32)y * sScaleY);
|
|
|
|
// Offset y by vertical alignment.
|
|
switch (valign)
|
|
{
|
|
case TOP:
|
|
cur_y -= mAscender;
|
|
break;
|
|
case BOTTOM:
|
|
cur_y += mDescender;
|
|
break;
|
|
case VCENTER:
|
|
cur_y -= ((mAscender - mDescender)/2.f);
|
|
break;
|
|
case BASELINE:
|
|
// Baseline, do nothing.
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (halign)
|
|
{
|
|
case LEFT:
|
|
break;
|
|
case RIGHT:
|
|
cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX));
|
|
break;
|
|
case HCENTER:
|
|
cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Round properly.
|
|
//cur_render_y = (F32)llfloor(cur_y/sScaleY + 0.5f)*sScaleY;
|
|
//cur_render_x = (F32)llfloor(cur_x/sScaleX + 0.5f)*sScaleX;
|
|
|
|
cur_render_y = cur_y;
|
|
cur_render_x = cur_x;
|
|
|
|
F32 start_x = cur_x;
|
|
|
|
F32 inv_width = 1.f / mImageGLp->getWidth();
|
|
F32 inv_height = 1.f / mImageGLp->getHeight();
|
|
|
|
const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL;
|
|
|
|
|
|
BOOL draw_ellipses = FALSE;
|
|
if (use_ellipses && halign == LEFT)
|
|
{
|
|
// check for too long of a string
|
|
if (getWidthF32(wstr.c_str(), 0, max_chars) > scaled_max_pixels)
|
|
{
|
|
// use four dots for ellipsis width to generate padding
|
|
const LLWString dots(utf8str_to_wstring(LLString("....")));
|
|
scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str())));
|
|
draw_ellipses = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
glBegin(GL_QUADS);
|
|
for (i = begin_offset; i < begin_offset + length; i++)
|
|
{
|
|
llwchar wch = wstr[i];
|
|
|
|
// Handle embedded characters first, if they're enabled.
|
|
// Embedded characters are a hack for notecards
|
|
const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
|
|
if (ext_data)
|
|
{
|
|
LLImageGL* ext_image = ext_data->mImage;
|
|
const LLWString& label = ext_data->mLabel;
|
|
|
|
F32 ext_height = (F32)ext_image->getHeight() * sScaleY;
|
|
|
|
F32 ext_width = (F32)ext_image->getWidth() * sScaleX;
|
|
F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width;
|
|
|
|
if (!label.empty())
|
|
{
|
|
ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX;
|
|
}
|
|
|
|
if (start_x + scaled_max_pixels < cur_x + ext_advance)
|
|
{
|
|
// Not enough room for this character.
|
|
break;
|
|
}
|
|
|
|
glEnd();
|
|
|
|
glColor3f(1.f, 1.f, 1.f);
|
|
|
|
ext_image->bind();
|
|
const F32 ext_x = cur_render_x + (EXT_X_BEARING * sScaleX);
|
|
const F32 ext_y = cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight);
|
|
|
|
glBegin(GL_QUADS);
|
|
{
|
|
S32 num_passes = render_bold ? 2 : 1;
|
|
for (S32 pass = 0; pass < num_passes; pass++)
|
|
{
|
|
glTexCoord2f(1.f, 1.f);
|
|
glVertex2f(llfont_round_x(ext_x + ext_width + (F32)(pass * BOLD_OFFSET)),
|
|
llfont_round_y(ext_y + ext_height));
|
|
|
|
glTexCoord2f(0.f, 1.f);
|
|
glVertex2f(llfont_round_x(ext_x + (F32)(pass * BOLD_OFFSET)),
|
|
llfont_round_y(ext_y + ext_height));
|
|
|
|
glTexCoord2f(0.f, 0.f);
|
|
glVertex2f(llfont_round_x(ext_x + (F32)(pass * BOLD_OFFSET)), llfont_round_y(ext_y));
|
|
|
|
glTexCoord2f(1.f, 0.f);
|
|
glVertex2f(llfont_round_x(ext_x + ext_width + (F32)(pass * BOLD_OFFSET)),
|
|
llfont_round_y(ext_y));
|
|
}
|
|
}
|
|
glEnd();
|
|
|
|
if (!label.empty())
|
|
{
|
|
glPushMatrix();
|
|
//glLoadIdentity();
|
|
//glTranslatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f);
|
|
//glScalef(sScaleX, sScaleY, 1.f);
|
|
gExtCharFont->render(label, 0,
|
|
/*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX),
|
|
/*llfloor*/(cur_y / sScaleY),
|
|
color,
|
|
halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL,
|
|
TRUE );
|
|
glPopMatrix();
|
|
}
|
|
|
|
glColor4fv(color.mV);
|
|
|
|
chars_drawn++;
|
|
cur_x += ext_advance;
|
|
if (((i + 1) < length) && wstr[i+1])
|
|
{
|
|
cur_x += EXT_KERNING * sScaleX;
|
|
}
|
|
cur_render_x = cur_x;
|
|
|
|
// Bind the font texture
|
|
mImageGLp->bind();
|
|
glBegin(GL_QUADS);
|
|
}
|
|
else
|
|
{
|
|
if (!hasGlyph(wch))
|
|
{
|
|
glEnd();
|
|
(const_cast<LLFontGL*>(this))->addChar(wch);
|
|
glBegin(GL_QUADS);
|
|
}
|
|
|
|
const LLFontGlyphInfo* fgi= getGlyphInfo(wch);
|
|
if (!fgi)
|
|
{
|
|
llerrs << "Missing Glyph Info" << llendl;
|
|
}
|
|
if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth))
|
|
{
|
|
// Not enough room for this character.
|
|
break;
|
|
}
|
|
|
|
// Draw the text at the appropriate location
|
|
//Specify vertices and texture coordinates
|
|
S32 num_passes = render_bold ? 2 : 1;
|
|
for (S32 pass = 0; pass < num_passes; pass++)
|
|
{
|
|
glTexCoord2f((fgi->mXBitmapOffset - PAD_AMT) * inv_width,
|
|
(fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height);
|
|
glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + (F32)(pass * BOLD_OFFSET) - PAD_AMT),
|
|
llfont_round_y(cur_render_y + (F32)fgi->mYBearing + PAD_AMT));
|
|
glTexCoord2f((fgi->mXBitmapOffset - PAD_AMT) * inv_width,
|
|
(fgi->mYBitmapOffset - PAD_AMT) * inv_height);
|
|
glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + slant_offset + (F32)(pass * BOLD_OFFSET) - PAD_AMT),
|
|
llfont_round_y(cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT));
|
|
glTexCoord2f((fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width,
|
|
(fgi->mYBitmapOffset - PAD_AMT) * inv_height);
|
|
glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + slant_offset + (F32)fgi->mWidth + (F32)(pass * BOLD_OFFSET) + PAD_AMT),
|
|
llfont_round_y(cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT));
|
|
glTexCoord2f((fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width,
|
|
(fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height);
|
|
glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + (F32)(pass * BOLD_OFFSET) + PAD_AMT),
|
|
llfont_round_y(cur_render_y + (F32)fgi->mYBearing + PAD_AMT));
|
|
}
|
|
|
|
chars_drawn++;
|
|
|
|
cur_x += fgi->mXAdvance;
|
|
cur_y += fgi->mYAdvance;
|
|
llwchar next_char = wstr[i+1];
|
|
if (next_char && (next_char < LAST_CHARACTER))
|
|
{
|
|
// Kern this puppy.
|
|
if (!hasGlyph(next_char))
|
|
{
|
|
glEnd();
|
|
(const_cast<LLFontGL*>(this))->addChar(next_char);
|
|
glBegin(GL_QUADS);
|
|
}
|
|
cur_x += getXKerning(wch, next_char);
|
|
}
|
|
|
|
// Round after kerning.
|
|
// Must do this to cur_x, not just to cur_render_x, otherwise you
|
|
// will squish sub-pixel kerned characters too close together.
|
|
// For example, "CCCCC" looks bad.
|
|
cur_x = (F32)llfloor(cur_x + 0.5f);
|
|
//cur_y = (F32)llfloor(cur_y + 0.5f);
|
|
|
|
cur_render_x = cur_x;
|
|
cur_render_y = cur_y;
|
|
}
|
|
}
|
|
|
|
glEnd();
|
|
|
|
if (right_x)
|
|
{
|
|
*right_x = cur_x / sScaleX;
|
|
}
|
|
|
|
if (style & UNDERLINE)
|
|
{
|
|
LLGLSNoTexture no_texture;
|
|
glBegin(GL_LINES);
|
|
glVertex2f(start_x, cur_y - (mDescender));
|
|
glVertex2f(cur_x, cur_y - (mDescender));
|
|
glEnd();
|
|
}
|
|
|
|
// *FIX: get this working in all alignment cases, etc.
|
|
if (draw_ellipses)
|
|
{
|
|
// recursively render ellipses at end of string
|
|
// we've already reserved enough room
|
|
glPushMatrix();
|
|
//glLoadIdentity();
|
|
//glTranslatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f);
|
|
//glScalef(sScaleX, sScaleY, 1.f);
|
|
renderUTF8("...",
|
|
0,
|
|
cur_x / sScaleX, (F32)y,
|
|
color,
|
|
LEFT, valign,
|
|
style,
|
|
S32_MAX, max_pixels,
|
|
right_x,
|
|
FALSE);
|
|
glPopMatrix();
|
|
}
|
|
|
|
glPopMatrix();
|
|
|
|
return chars_drawn;
|
|
}
|
|
|
|
|
|
LLImageGL *LLFontGL::getImageGL() const
|
|
{
|
|
return mImageGLp;
|
|
}
|
|
|
|
S32 LLFontGL::getWidth(const LLString& utf8text) const
|
|
{
|
|
LLWString wtext = utf8str_to_wstring(utf8text);
|
|
return getWidth(wtext.c_str(), 0, S32_MAX);
|
|
}
|
|
|
|
S32 LLFontGL::getWidth(const llwchar* wchars) const
|
|
{
|
|
return getWidth(wchars, 0, S32_MAX);
|
|
}
|
|
|
|
S32 LLFontGL::getWidth(const LLString& utf8text, const S32 begin_offset, const S32 max_chars) const
|
|
{
|
|
LLWString wtext = utf8str_to_wstring(utf8text);
|
|
return getWidth(wtext.c_str(), begin_offset, max_chars);
|
|
}
|
|
|
|
S32 LLFontGL::getWidth(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const
|
|
{
|
|
F32 width = getWidthF32(wchars, begin_offset, max_chars, use_embedded);
|
|
return llround(width);
|
|
}
|
|
|
|
F32 LLFontGL::getWidthF32(const LLString& utf8text) const
|
|
{
|
|
LLWString wtext = utf8str_to_wstring(utf8text);
|
|
return getWidthF32(wtext.c_str(), 0, S32_MAX);
|
|
}
|
|
|
|
F32 LLFontGL::getWidthF32(const llwchar* wchars) const
|
|
{
|
|
return getWidthF32(wchars, 0, S32_MAX);
|
|
}
|
|
|
|
F32 LLFontGL::getWidthF32(const LLString& utf8text, const S32 begin_offset, const S32 max_chars ) const
|
|
{
|
|
LLWString wtext = utf8str_to_wstring(utf8text);
|
|
return getWidthF32(wtext.c_str(), begin_offset, max_chars);
|
|
}
|
|
|
|
F32 LLFontGL::getWidthF32(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const
|
|
{
|
|
const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL;
|
|
|
|
F32 cur_x = 0;
|
|
const S32 max_index = begin_offset + max_chars;
|
|
for (S32 i = begin_offset; i < max_index; i++)
|
|
{
|
|
const llwchar wch = wchars[i];
|
|
if (wch == 0)
|
|
{
|
|
break; // done
|
|
}
|
|
const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
|
|
if (ext_data)
|
|
{
|
|
// Handle crappy embedded hack
|
|
cur_x += getEmbeddedCharAdvance(ext_data);
|
|
|
|
if( ((i+1) < max_chars) && (i+1 < max_index))
|
|
{
|
|
cur_x += EXT_KERNING * sScaleX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cur_x += getXAdvance(wch);
|
|
llwchar next_char = wchars[i+1];
|
|
|
|
if (((i + 1) < max_chars)
|
|
&& next_char
|
|
&& (next_char < LAST_CHARACTER))
|
|
{
|
|
// Kern this puppy.
|
|
cur_x += getXKerning(wch, next_char);
|
|
}
|
|
}
|
|
// Round after kerning.
|
|
cur_x = (F32)llfloor(cur_x + 0.5f);
|
|
}
|
|
|
|
return cur_x / sScaleX;
|
|
}
|
|
|
|
|
|
|
|
// Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels
|
|
S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars,
|
|
BOOL end_on_word_boundary, const BOOL use_embedded,
|
|
F32* drawn_pixels) const
|
|
{
|
|
if (!wchars || !wchars[0] || max_chars == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
llassert(max_pixels >= 0.f);
|
|
llassert(max_chars >= 0);
|
|
|
|
BOOL clip = FALSE;
|
|
F32 cur_x = 0;
|
|
F32 drawn_x = 0;
|
|
|
|
S32 start_of_last_word = 0;
|
|
BOOL in_word = FALSE;
|
|
|
|
F32 scaled_max_pixels = (F32)llceil(max_pixels * sScaleX);
|
|
|
|
S32 i;
|
|
for (i=0; (i < max_chars); i++)
|
|
{
|
|
llwchar wch = wchars[i];
|
|
|
|
if(wch == 0)
|
|
{
|
|
// Null terminator. We're done.
|
|
break;
|
|
}
|
|
|
|
const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
|
|
if (ext_data)
|
|
{
|
|
if (in_word)
|
|
{
|
|
in_word = FALSE;
|
|
}
|
|
else
|
|
{
|
|
start_of_last_word = i;
|
|
}
|
|
cur_x += getEmbeddedCharAdvance(ext_data);
|
|
|
|
if (scaled_max_pixels < cur_x)
|
|
{
|
|
clip = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (((i+1) < max_chars) && wchars[i+1])
|
|
{
|
|
cur_x += EXT_KERNING * sScaleX;
|
|
}
|
|
|
|
if( scaled_max_pixels < cur_x )
|
|
{
|
|
clip = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (in_word)
|
|
{
|
|
if (iswspace(wch))
|
|
{
|
|
in_word = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
start_of_last_word = i;
|
|
if (!iswspace(wch))
|
|
{
|
|
in_word = TRUE;
|
|
}
|
|
}
|
|
|
|
cur_x += getXAdvance(wch);
|
|
|
|
if (scaled_max_pixels < cur_x)
|
|
{
|
|
clip = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (((i+1) < max_chars) && wchars[i+1])
|
|
{
|
|
// Kern this puppy.
|
|
cur_x += getXKerning(wch, wchars[i+1]);
|
|
}
|
|
}
|
|
// Round after kerning.
|
|
cur_x = (F32)llfloor(cur_x + 0.5f);
|
|
drawn_x = cur_x;
|
|
}
|
|
|
|
if( clip && end_on_word_boundary && (start_of_last_word != 0) )
|
|
{
|
|
i = start_of_last_word;
|
|
}
|
|
if (drawn_pixels)
|
|
{
|
|
*drawn_pixels = drawn_x;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos, S32 max_chars) const
|
|
{
|
|
if (!wchars || !wchars[0] || max_chars == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
F32 total_width = 0.0;
|
|
S32 drawable_chars = 0;
|
|
|
|
F32 scaled_max_pixels = max_pixels * sScaleX;
|
|
|
|
S32 start = llmin(start_pos, text_len - 1);
|
|
for (S32 i = start; i >= 0; i--)
|
|
{
|
|
llwchar wch = wchars[i];
|
|
|
|
const embedded_data_t* ext_data = getEmbeddedCharData(wch);
|
|
if (ext_data)
|
|
{
|
|
F32 char_width = getEmbeddedCharAdvance(ext_data);
|
|
|
|
if( scaled_max_pixels < (total_width + char_width) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
total_width += char_width;
|
|
|
|
drawable_chars++;
|
|
if( max_chars >= 0 && drawable_chars >= max_chars )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( i > 0 )
|
|
{
|
|
total_width += EXT_KERNING * sScaleX;
|
|
}
|
|
|
|
// Round after kerning.
|
|
total_width = (F32)llfloor(total_width + 0.5f);
|
|
}
|
|
else
|
|
{
|
|
F32 char_width = getXAdvance(wch);
|
|
if( scaled_max_pixels < (total_width + char_width) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
total_width += char_width;
|
|
|
|
drawable_chars++;
|
|
if( max_chars >= 0 && drawable_chars >= max_chars )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ( i > 0 )
|
|
{
|
|
// Kerning
|
|
total_width += getXKerning(wchars[i-1], wch);
|
|
}
|
|
|
|
// Round after kerning.
|
|
total_width = (F32)llfloor(total_width + 0.5f);
|
|
}
|
|
}
|
|
|
|
return text_len - drawable_chars;
|
|
}
|
|
|
|
|
|
S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, const S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round, BOOL use_embedded) const
|
|
{
|
|
if (!wchars || !wchars[0] || max_chars == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
F32 cur_x = 0;
|
|
S32 pos = 0;
|
|
|
|
target_x *= sScaleX;
|
|
|
|
// max_chars is S32_MAX by default, so make sure we don't get overflow
|
|
const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars);
|
|
|
|
F32 scaled_max_pixels = max_pixels * sScaleX;
|
|
|
|
for (S32 i = begin_offset; (i < max_index); i++)
|
|
{
|
|
llwchar wch = wchars[i];
|
|
if (!wch)
|
|
{
|
|
break; // done
|
|
}
|
|
const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
|
|
if (ext_data)
|
|
{
|
|
F32 ext_advance = getEmbeddedCharAdvance(ext_data);
|
|
|
|
if (round)
|
|
{
|
|
// Note: if the mouse is on the left half of the character, the pick is to the character's left
|
|
// If it's on the right half, the pick is to the right.
|
|
if (target_x < cur_x + ext_advance/2)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (target_x < cur_x + ext_advance)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (scaled_max_pixels < cur_x + ext_advance)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pos++;
|
|
cur_x += ext_advance;
|
|
|
|
if (((i + 1) < max_index)
|
|
&& (wchars[(i + 1)]))
|
|
{
|
|
cur_x += EXT_KERNING * sScaleX;
|
|
}
|
|
// Round after kerning.
|
|
cur_x = (F32)llfloor(cur_x + 0.5f);
|
|
}
|
|
else
|
|
{
|
|
F32 char_width = getXAdvance(wch);
|
|
|
|
if (round)
|
|
{
|
|
// Note: if the mouse is on the left half of the character, the pick is to the character's left
|
|
// If it's on the right half, the pick is to the right.
|
|
if (target_x < cur_x + char_width*0.5f)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (target_x < cur_x + char_width)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (scaled_max_pixels < cur_x + char_width)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pos++;
|
|
cur_x += char_width;
|
|
|
|
if (((i + 1) < max_index)
|
|
&& (wchars[(i + 1)]))
|
|
{
|
|
llwchar next_char = wchars[i + 1];
|
|
// Kern this puppy.
|
|
cur_x += getXKerning(wch, next_char);
|
|
}
|
|
|
|
// Round after kerning.
|
|
cur_x = (F32)llfloor(cur_x + 0.5f);
|
|
}
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
|
|
const LLFontGL::embedded_data_t* LLFontGL::getEmbeddedCharData(const llwchar wch) const
|
|
{
|
|
// Handle crappy embedded hack
|
|
embedded_map_t::const_iterator iter = mEmbeddedChars.find(wch);
|
|
if (iter != mEmbeddedChars.end())
|
|
{
|
|
return iter->second;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
F32 LLFontGL::getEmbeddedCharAdvance(const embedded_data_t* ext_data) const
|
|
{
|
|
const LLWString& label = ext_data->mLabel;
|
|
LLImageGL* ext_image = ext_data->mImage;
|
|
|
|
F32 ext_width = (F32)ext_image->getWidth();
|
|
if( !label.empty() )
|
|
{
|
|
ext_width += (EXT_X_BEARING + gExtCharFont->getWidthF32(label.c_str())) * sScaleX;
|
|
}
|
|
|
|
return (EXT_X_BEARING * sScaleX) + ext_width;
|
|
}
|
|
|
|
|
|
void LLFontGL::clearEmbeddedChars()
|
|
{
|
|
for_each(mEmbeddedChars.begin(), mEmbeddedChars.end(), DeletePairedPointer());
|
|
mEmbeddedChars.clear();
|
|
}
|
|
|
|
void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLString& label )
|
|
{
|
|
LLWString wlabel = utf8str_to_wstring(label);
|
|
addEmbeddedChar(wc, image, wlabel);
|
|
}
|
|
|
|
void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& wlabel )
|
|
{
|
|
embedded_data_t* ext_data = new embedded_data_t(image, wlabel);
|
|
mEmbeddedChars[wc] = ext_data;
|
|
}
|
|
|
|
void LLFontGL::removeEmbeddedChar( llwchar wc )
|
|
{
|
|
embedded_map_t::iterator iter = mEmbeddedChars.find(wc);
|
|
if (iter != mEmbeddedChars.end())
|
|
{
|
|
delete iter->second;
|
|
mEmbeddedChars.erase(wc);
|
|
}
|
|
}
|
|
|
|
// static
|
|
LLString LLFontGL::nameFromFont(const LLFontGL* fontp)
|
|
{
|
|
if (fontp == sSansSerifHuge)
|
|
{
|
|
return LLString("SansSerifHuge");
|
|
}
|
|
else if (fontp == sSansSerifSmall)
|
|
{
|
|
return LLString("SansSerifSmall");
|
|
}
|
|
else if (fontp == sSansSerif)
|
|
{
|
|
return LLString("SansSerif");
|
|
}
|
|
else if (fontp == sSansSerifBig)
|
|
{
|
|
return LLString("SansSerifBig");
|
|
}
|
|
else if (fontp == sSansSerifBold)
|
|
{
|
|
return LLString("SansSerifBold");
|
|
}
|
|
else if (fontp == sMonospace)
|
|
{
|
|
return LLString("Monospace");
|
|
}
|
|
else
|
|
{
|
|
return LLString();
|
|
}
|
|
}
|
|
|
|
// static
|
|
LLFontGL* LLFontGL::fontFromName(const LLString& font_name)
|
|
{
|
|
LLFontGL* gl_font = NULL;
|
|
if (font_name == "SansSerifHuge")
|
|
{
|
|
gl_font = LLFontGL::sSansSerifHuge;
|
|
}
|
|
else if (font_name == "SansSerifSmall")
|
|
{
|
|
gl_font = LLFontGL::sSansSerifSmall;
|
|
}
|
|
else if (font_name == "SansSerif")
|
|
{
|
|
gl_font = LLFontGL::sSansSerif;
|
|
}
|
|
else if (font_name == "SansSerifBig")
|
|
{
|
|
gl_font = LLFontGL::sSansSerifBig;
|
|
}
|
|
else if (font_name == "SansSerifBold")
|
|
{
|
|
gl_font = LLFontGL::sSansSerifBold;
|
|
}
|
|
else if (font_name == "Monospace")
|
|
{
|
|
gl_font = LLFontGL::sMonospace;
|
|
}
|
|
return gl_font;
|
|
}
|
|
|
|
// static
|
|
LLString LLFontGL::nameFromHAlign(LLFontGL::HAlign align)
|
|
{
|
|
if (align == LEFT) return LLString("left");
|
|
else if (align == RIGHT) return LLString("right");
|
|
else if (align == HCENTER) return LLString("center");
|
|
else return LLString();
|
|
}
|
|
|
|
// static
|
|
LLFontGL::HAlign LLFontGL::hAlignFromName(const LLString& name)
|
|
{
|
|
LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
|
|
if (name == "left")
|
|
{
|
|
gl_hfont_align = LLFontGL::LEFT;
|
|
}
|
|
else if (name == "right")
|
|
{
|
|
gl_hfont_align = LLFontGL::RIGHT;
|
|
}
|
|
else if (name == "center")
|
|
{
|
|
gl_hfont_align = LLFontGL::HCENTER;
|
|
}
|
|
//else leave left
|
|
return gl_hfont_align;
|
|
}
|
|
|
|
// static
|
|
LLString LLFontGL::nameFromVAlign(LLFontGL::VAlign align)
|
|
{
|
|
if (align == TOP) return LLString("top");
|
|
else if (align == VCENTER) return LLString("center");
|
|
else if (align == BASELINE) return LLString("baseline");
|
|
else if (align == BOTTOM) return LLString("bottom");
|
|
else return LLString();
|
|
}
|
|
|
|
// static
|
|
LLFontGL::VAlign LLFontGL::vAlignFromName(const LLString& name)
|
|
{
|
|
LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE;
|
|
if (name == "top")
|
|
{
|
|
gl_vfont_align = LLFontGL::TOP;
|
|
}
|
|
else if (name == "center")
|
|
{
|
|
gl_vfont_align = LLFontGL::VCENTER;
|
|
}
|
|
else if (name == "baseline")
|
|
{
|
|
gl_vfont_align = LLFontGL::BASELINE;
|
|
}
|
|
else if (name == "bottom")
|
|
{
|
|
gl_vfont_align = LLFontGL::BOTTOM;
|
|
}
|
|
//else leave baseline
|
|
return gl_vfont_align;
|
|
}
|