summaryrefslogtreecommitdiff
path: root/indra/llrender/llfontgl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llrender/llfontgl.cpp')
-rw-r--r--indra/llrender/llfontgl.cpp1827
1 files changed, 744 insertions, 1083 deletions
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp
index 3a408b5550..386bb987f9 100644
--- a/indra/llrender/llfontgl.cpp
+++ b/indra/llrender/llfontgl.cpp
@@ -2,43 +2,46 @@
* @file llfontgl.cpp
* @brief Wrapper around FreeType
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2007, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlife.com/developers/opensource/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlife.com/developers/opensource/flossexception
+ * This library 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
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
-#include <boost/tokenizer.hpp>
-
-#include "llfont.h"
#include "llfontgl.h"
+
+// Linden library includes
+#include "llfontfreetype.h"
+#include "llfontbitmapcache.h"
+#include "llfontregistry.h"
#include "llgl.h"
-#include "llglimmediate.h"
-#include "v4color.h"
+#include "llimagegl.h"
+#include "llrender.h"
#include "llstl.h"
+#include "v4color.h"
+#include "lltexture.h"
+#include "lldir.h"
+
+// Third party library includes
+#include <boost/tokenizer.hpp>
const S32 BOLD_OFFSET = 1;
@@ -48,603 +51,144 @@ F32 LLFontGL::sHorizDPI = 96.f;
F32 LLFontGL::sScaleX = 1.f;
F32 LLFontGL::sScaleY = 1.f;
BOOL LLFontGL::sDisplayFont = TRUE ;
-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::sMonospaceFallback = NULL;
-LLFontList* LLFontGL::sSSFallback = NULL;
-LLFontList* LLFontGL::sSSSmallFallback = NULL;
-LLFontList* LLFontGL::sSSBigFallback = NULL;
-LLFontList* LLFontGL::sSSHugeFallback = NULL;
-LLFontList* LLFontGL::sSSBoldFallback = NULL;
+std::string LLFontGL::sAppDir;
+
LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f);
+LLFontRegistry* LLFontGL::sFontRegistry = NULL;
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;
+const F32 PAD_UVY = 0.5f; // half of vertical padding between glyphs in the glyph texture
const F32 DROP_SHADOW_SOFT_STRENGTH = 0.3f;
-F32 llfont_round_x(F32 x)
+static 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)
+static 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;
- }
- if (style.find("SHADOW") != style.npos)
- {
- ret |= DROP_SHADOW;
- }
- if (style.find("SOFT_SHADOW") != style.npos)
- {
- ret |= DROP_SHADOW_SOFT;
- }
- 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();
- // we allow bilinear filtering to get sub-pixel positioning for drop shadows
- //mImageGLp->setMipFilterNearest(TRUE, TRUE);
- }
- if (mRawImageGLp.isNull())
- {
- mRawImageGLp = new LLImageRaw; // Note LLFontGL owns the image, not LLFont.
- }
- setRawImage( mRawImageGLp );
}
void LLFontGL::reset()
{
- init();
- resetBitmap();
+ mFontFreetype->reset(sVertDPI, sHorizDPI);
}
-// 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)
+void LLFontGL::destroyGL()
{
- 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, point_size, sVertDPI, sHorizDPI, 2, TRUE))
- {
- font_path = sys_path + *token_iter;
- if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, TRUE))
- {
- LL_INFOS_ONCE("ViewerImages") << "Couldn't load font " << *token_iter << LL_ENDL;
- 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);
+ mFontFreetype->destroyGL();
}
-//static
-bool LLFontGL::loadFace(LLFontGL *fontp, const LLString& fontname, const F32 point_size, LLFontList *fallback_fontp)
+BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback)
{
- LLString local_path = getFontPathLocal();
- LLString font_path = local_path + fontname;
- if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, FALSE))
+ if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL))
{
- LLString sys_path = getFontPathSystem();
- font_path = sys_path + fontname;
- if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, FALSE))
- {
- LL_WARNS("ViewerImages") << "Couldn't load font " << fontname << LL_ENDL;
- return false;
- }
+ mFontFreetype = new LLFontFreetype;
}
- fontp->setFallbackFont(fallback_fontp);
- return true;
+ return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback);
}
+static LLFastTimer::DeclareTimer FTM_RENDER_FONTS("Fonts");
-// 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)
+S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, const LLRect& rect, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,
+ ShadowType shadow, S32 max_chars, F32* right_x, BOOL use_ellipses) const
{
- 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();
- }
-
- if (sMonospaceFallback)
- {
- delete sMonospaceFallback;
- }
- sMonospaceFallback = new LLFontList();
- if (!loadFaceFallback(
- sMonospaceFallback,
- sanserif_fallback_file,
- monospace_size * ss_fallback_scale))
- {
- delete sMonospaceFallback;
- sMonospaceFallback = NULL;
- }
-
- failed |= !loadFace(sMonospace, monospace_file, monospace_size, sMonospaceFallback);
-
- //
- // 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();
- }
+ F32 x = rect.mLeft;
+ F32 y = 0.f;
- if(sSSSmallFallback)
+ switch(valign)
{
- 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 sMonospaceFallback;
- sMonospaceFallback = 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 std::string& filename,
- const F32 point_size, const F32 vert_dpi, const F32 horz_dpi,
- const S32 components, BOOL is_fallback)
-{
- if (!LLFont::loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback))
- {
- 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;
+ case TOP:
+ y = rect.mTop;
+ break;
+ case VCENTER:
+ y = rect.getCenterY();
+ break;
+ case BASELINE:
+ case BOTTOM:
+ y = rect.mBottom;
+ break;
+ default:
+ y = rect.mBottom;
+ break;
}
-
- stop_glerror();
- mImageGLp->setSubImage(mRawImageGLp, 0, 0, mImageGLp->getWidth(), mImageGLp->getHeight());
- mImageGLp->bind();
- mImageGLp->setMipFilterNearest(TRUE, TRUE);
- stop_glerror();
- return TRUE;
+ return render(wstr, begin_offset, x, y, color, halign, valign, style, shadow, max_chars, rect.getWidth(), right_x, use_ellipses);
}
-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
+S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style,
+ ShadowType shadow, S32 max_chars, 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);
-}
+ LLFastTimer _(FTM_RENDER_FONTS);
-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
-{
if(!sDisplayFont) //do not display texts
{
return wstr.length() ;
}
- LLGLEnable tex(GL_TEXTURE_2D);
-
if (wstr.empty())
{
return 0;
}
+ gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE);
+
S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX);
- // 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);
- }
- }
+ // determine which style flags need to be added programmatically by stripping off the
+ // style bits that are drawn by the underlying Freetype font
+ U8 style_to_add = (style | mFontDescriptor.getStyle()) & ~mFontFreetype->getStyle();
F32 drop_shadow_strength = 0.f;
- if (style & (DROP_SHADOW | DROP_SHADOW_SOFT))
+ if (shadow != NO_SHADOW)
{
F32 luminance;
color.calcHSL(NULL, NULL, &luminance);
drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f);
if (luminance < 0.35f)
{
- style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT);
+ shadow = NO_SHADOW;
}
}
- gGL.pushMatrix();
- glLoadIdentity();
- gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ);
- //glScalef(sScaleX, sScaleY, 1.0f);
+ gGL.pushUIMatrix();
+
+ gGL.loadUIIdentity();
- // 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)
- //{
- gGL.translatef(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);
- //gGL.translatef(-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);
-
- gGL.color4fv( color.mV );
+ //gGL.translateUI(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ);
+
+ // this code snaps the text origin to a pixel grid to start with
+ //F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX);
+ //F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY);
+ //gGL.translateUI(-pixel_offset_x, -pixel_offset_y, 0.f);
+
+ LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY));
+ // snap the text origin to a pixel grid to start with
+ origin.mV[VX] -= llround((F32)sCurOrigin.mX) - (sCurOrigin.mX);
+ origin.mV[VY] -= llround((F32)sCurOrigin.mY) - (sCurOrigin.mY);
+
S32 chars_drawn = 0;
S32 i;
@@ -661,26 +205,23 @@ S32 LLFontGL::render(const LLWString &wstr,
F32 cur_x, cur_y, cur_render_x, cur_render_y;
- // Bind the font texture
-
- mImageGLp->bind(0);
-
- gGL.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Not guaranteed to be set correctly
+ // Not guaranteed to be set correctly
+ gGL.setSceneBlendType(LLRender::BT_ALPHA);
- cur_x = ((F32)x * sScaleX);
- cur_y = ((F32)y * sScaleY);
+ cur_x = ((F32)x * sScaleX) + origin.mV[VX];
+ cur_y = ((F32)y * sScaleY) + origin.mV[VY];
// Offset y by vertical alignment.
switch (valign)
{
case TOP:
- cur_y -= mAscender;
+ cur_y -= mFontFreetype->getAscenderHeight();
break;
case BOTTOM:
- cur_y += mDescender;
+ cur_y += mFontFreetype->getDescenderHeight();
break;
case VCENTER:
- cur_y -= ((mAscender - mDescender)/2.f);
+ cur_y -= (mFontFreetype->getAscenderHeight() - mFontFreetype->getDescenderHeight()) / 2.f;
break;
case BASELINE:
// Baseline, do nothing.
@@ -694,214 +235,217 @@ S32 LLFontGL::render(const LLWString &wstr,
case LEFT:
break;
case RIGHT:
- cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX));
+ cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX));
break;
case HCENTER:
- cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2;
+ cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, 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 start_x = llround(cur_x);
+
+ const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache();
- F32 inv_width = 1.f / mImageGLp->getWidth();
- F32 inv_height = 1.f / mImageGLp->getHeight();
+ F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth();
+ F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight();
- const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL;
+ const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL;
BOOL draw_ellipses = FALSE;
- if (use_ellipses && halign == LEFT)
+ if (use_ellipses)
{
// check for too long of a string
- if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels)
+ S32 string_width = llround(getWidthF32(wstr.c_str(), begin_offset, max_chars) * sScaleX);
+ if (string_width > scaled_max_pixels)
{
// use four dots for ellipsis width to generate padding
- const LLWString dots(utf8str_to_wstring(LLString("....")));
+ const LLWString dots(utf8str_to_wstring(std::string("....")));
scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str())));
draw_ellipses = TRUE;
}
}
+ const LLFontGlyphInfo* next_glyph = NULL;
+
+ const S32 GLYPH_BATCH_SIZE = 30;
+ LLVector3 vertices[GLYPH_BATCH_SIZE * 4];
+ LLVector2 uvs[GLYPH_BATCH_SIZE * 4];
+ LLColor4U colors[GLYPH_BATCH_SIZE * 4];
+
+ LLColor4U text_color(color);
+
+ S32 bitmap_num = -1;
+ S32 glyph_count = 0;
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)
+ const LLFontGlyphInfo* fgi = next_glyph;
+ next_glyph = NULL;
+ if(!fgi)
{
- 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;
- }
+ fgi = mFontFreetype->getGlyphInfo(wch);
+ }
+ if (!fgi)
+ {
+ llerrs << "Missing Glyph Info" << llendl;
+ break;
+ }
+ // Per-glyph bitmap texture.
+ S32 next_bitmap_num = fgi->mBitmapNum;
+ if (next_bitmap_num != bitmap_num)
+ {
+ bitmap_num = next_bitmap_num;
+ LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num);
+ gGL.getTexUnit(0)->bind(font_image);
+ }
+
+ if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth))
+ {
+ // Not enough room for this character.
+ break;
+ }
- if (start_x + scaled_max_pixels < cur_x + ext_advance)
+ // Draw the text at the appropriate location
+ //Specify vertices and texture coordinates
+ LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width,
+ (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height,
+ (fgi->mXBitmapOffset + fgi->mWidth) * inv_width,
+ (fgi->mYBitmapOffset - PAD_UVY) * inv_height);
+ // snap glyph origin to whole screen pixel
+ LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing),
+ llround(cur_render_y + (F32)fgi->mYBearing),
+ llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth,
+ llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight);
+
+ if (glyph_count >= GLYPH_BATCH_SIZE)
+ {
+ gGL.begin(LLRender::QUADS);
{
- // Not enough room for this character.
- break;
+ gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
}
+ gGL.end();
- 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);
+ glyph_count = 0;
+ }
- LLRectf uv_rect(0.f, 1.f, 1.f, 0.f);
- LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y);
- drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength);
+ drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength);
- if (!label.empty())
- {
- gGL.pushMatrix();
- //glLoadIdentity();
- //gGL.translatef(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 );
- gGL.popMatrix();
- }
+ chars_drawn++;
+ cur_x += fgi->mXAdvance;
+ cur_y += fgi->mYAdvance;
- gGL.color4fv(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();
- }
- else
+ llwchar next_char = wstr[i+1];
+ if (next_char && (next_char < LAST_CHARACTER))
{
- if (!hasGlyph(wch))
- {
- (const_cast<LLFontGL*>(this))->addChar(wch);
- }
-
- const LLFontGlyphInfo* fgi= getGlyphInfo(wch);
- if (!fgi)
- {
- llerrs << "Missing Glyph Info" << llendl;
- break;
- }
- if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth))
- {
- // Not enough room for this character.
- break;
- }
+ // Kern this puppy.
+ next_glyph = mFontFreetype->getGlyphInfo(next_char);
+ cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
+ }
- // Draw the text at the appropriate location
- //Specify vertices and texture coordinates
- LLRectf uv_rect((fgi->mXBitmapOffset - PAD_AMT) * inv_width,
- (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height,
- (fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width,
- (fgi->mYBitmapOffset - PAD_AMT) * inv_height);
- LLRectf screen_rect(cur_render_x + (F32)fgi->mXBearing - PAD_AMT,
- cur_render_y + (F32)fgi->mYBearing + PAD_AMT,
- cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + PAD_AMT,
- cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT);
-
- drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength);
-
- 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))
- {
- (const_cast<LLFontGL*>(this))->addChar(next_char);
- }
- 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)llround(cur_x);
+ //cur_y = (F32)llround(cur_y);
- // 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;
+ }
- cur_render_x = cur_x;
- cur_render_y = cur_y;
- }
+ gGL.begin(LLRender::QUADS);
+ {
+ gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4);
}
+ gGL.end();
+
if (right_x)
{
- *right_x = cur_x / sScaleX;
+ *right_x = (cur_x - origin.mV[VX]) / sScaleX;
}
- if (style & UNDERLINE)
+ //FIXME: add underline as glyph?
+ if (style_to_add & UNDERLINE)
{
- LLGLSNoTexture no_texture;
- gGL.begin(LLVertexBuffer::LINES);
- gGL.vertex2f(start_x, cur_y - (mDescender));
- gGL.vertex2f(cur_x, cur_y - (mDescender));
+ F32 descender = mFontFreetype->getDescenderHeight();
+
+ gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+ gGL.begin(LLRender::LINES);
+ gGL.vertex2f(start_x, cur_y - (descender));
+ gGL.vertex2f(cur_x, cur_y - (descender));
gGL.end();
}
- // *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
- gGL.pushMatrix();
- //glLoadIdentity();
- //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f);
- //glScalef(sScaleX, sScaleY, 1.f);
- renderUTF8("...",
+ gGL.pushUIMatrix();
+ renderUTF8(std::string("..."),
0,
- cur_x / sScaleX, (F32)y,
+ (cur_x - origin.mV[VX]) / sScaleX, (F32)y,
color,
LEFT, valign,
- style,
+ style_to_add,
+ shadow,
S32_MAX, max_pixels,
right_x,
FALSE);
- gGL.popMatrix();
+ gGL.popUIMatrix();
}
- gGL.popMatrix();
+ gGL.popUIMatrix();
return chars_drawn;
}
-
-LLImageGL *LLFontGL::getImageGL() const
+S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const
+{
+ return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
+}
+
+S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const
+{
+ return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses);
+}
+
+S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const
{
- return mImageGLp;
+ return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
}
-S32 LLFontGL::getWidth(const LLString& utf8text) const
+S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow) const
+{
+ return renderUTF8(text, begin_offset, (F32)x, (F32)y, color, halign, valign, style, shadow, S32_MAX, S32_MAX, NULL, FALSE);
+}
+
+// font metrics - override for LLFontFreetype that returns units of virtual pixels
+F32 LLFontGL::getLineHeight() const
+{
+ return (F32)llround(mFontFreetype->getLineHeight() / sScaleY);
+}
+
+F32 LLFontGL::getAscenderHeight() const
+{
+ return (F32)llround(mFontFreetype->getAscenderHeight() / sScaleY);
+}
+
+F32 LLFontGL::getDescenderHeight() const
+{
+ return (F32)llround(mFontFreetype->getDescenderHeight() / sScaleY);
+}
+
+S32 LLFontGL::getWidth(const std::string& utf8text) const
{
LLWString wtext = utf8str_to_wstring(utf8text);
return getWidth(wtext.c_str(), 0, S32_MAX);
@@ -912,19 +456,19 @@ 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
+S32 LLFontGL::getWidth(const std::string& utf8text, S32 begin_offset, 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
+S32 LLFontGL::getWidth(const llwchar* wchars, S32 begin_offset, S32 max_chars) const
{
- F32 width = getWidthF32(wchars, begin_offset, max_chars, use_embedded);
+ F32 width = getWidthF32(wchars, begin_offset, max_chars);
return llround(width);
}
-F32 LLFontGL::getWidthF32(const LLString& utf8text) const
+F32 LLFontGL::getWidthF32(const std::string& utf8text) const
{
LLWString wtext = utf8str_to_wstring(utf8text);
return getWidthF32(wtext.c_str(), 0, S32_MAX);
@@ -935,62 +479,65 @@ 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
+F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, 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
+F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars) const
{
- const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL;
+ const S32 LAST_CHARACTER = LLFontFreetype::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 LLFontGlyphInfo* next_glyph = NULL;
+
+ F32 width_padding = 0.f;
+ for (S32 i = begin_offset; i < max_index && wchars[i] != 0; i++)
{
- const llwchar wch = wchars[i];
- if (wch == 0)
+ llwchar wch = wchars[i];
+
+ const LLFontGlyphInfo* fgi = next_glyph;
+ next_glyph = NULL;
+ if(!fgi)
{
- break; // done
+ fgi = mFontFreetype->getGlyphInfo(wch);
}
- 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];
+ F32 advance = mFontFreetype->getXAdvance(fgi);
- if (((i + 1) < max_chars)
- && next_char
- && (next_char < LAST_CHARACTER))
- {
- // Kern this puppy.
- cur_x += getXKerning(wch, next_char);
- }
+ // for the last character we want to measure the greater of its width and xadvance values
+ // so keep track of the difference between these values for the each character we measure
+ // so we can fix things up at the end
+ width_padding = llmax( 0.f, // always use positive padding amount
+ width_padding - advance, // previous padding left over after advance of current character
+ (F32)(fgi->mWidth + fgi->mXBearing) - advance); // difference between width of this character and advance to next character
+
+ cur_x += advance;
+ llwchar next_char = wchars[i+1];
+
+ if (((i + 1) < begin_offset + max_chars)
+ && next_char
+ && (next_char < LAST_CHARACTER))
+ {
+ // Kern this puppy.
+ next_glyph = mFontFreetype->getGlyphInfo(next_char);
+ cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
}
// Round after kerning.
- cur_x = (F32)llfloor(cur_x + 0.5f);
+ cur_x = (F32)llround(cur_x);
}
+ // add in extra pixels for last character's width past its xadvance
+ cur_x += width_padding;
+
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
+S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, EWordWrapStyle end_on_word_boundary) const
{
if (!wchars || !wchars[0] || max_chars == 0)
{
@@ -1007,7 +554,11 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch
S32 start_of_last_word = 0;
BOOL in_word = FALSE;
- F32 scaled_max_pixels = (F32)llceil(max_pixels * sScaleX);
+ // avoid S32 overflow when max_pixels == S32_MAX by staying in floating point
+ F32 scaled_max_pixels = ceil(max_pixels * sScaleX);
+ F32 width_padding = 0.f;
+
+ LLFontGlyphInfo* next_glyph = NULL;
S32 i;
for (i=0; (i < max_chars); i++)
@@ -1020,85 +571,92 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch
break;
}
- const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
- if (ext_data)
+ if (in_word)
{
- if (in_word)
- {
- in_word = FALSE;
- }
- else
+ if (iswspace(wch))
{
- 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))
+ if(wch !=(0x00A0))
{
in_word = FALSE;
}
}
- else
+ if (iswindividual(wch))
{
- start_of_last_word = i;
- if (!iswspace(wch))
+ if (iswpunct(wchars[i+1]))
+ {
+ in_word=TRUE;
+ }
+ else
{
- in_word = TRUE;
+ in_word=FALSE;
+ start_of_last_word = i;
}
}
-
- cur_x += getXAdvance(wch);
-
- if (scaled_max_pixels < cur_x)
+ }
+ else
+ {
+ start_of_last_word = i;
+ if (!iswspace(wch)||!iswindividual(wch))
{
- clip = TRUE;
- break;
+ in_word = TRUE;
}
+ }
+
+ LLFontGlyphInfo* fgi = next_glyph;
+ next_glyph = NULL;
+ if(!fgi)
+ {
+ fgi = mFontFreetype->getGlyphInfo(wch);
+ }
- if (((i+1) < max_chars) && wchars[i+1])
- {
- // Kern this puppy.
- cur_x += getXKerning(wch, wchars[i+1]);
- }
+ // account for glyphs that run beyond the starting point for the next glyphs
+ width_padding = llmax( 0.f, // always use positive padding amount
+ width_padding - fgi->mXAdvance, // previous padding left over after advance of current character
+ (F32)(fgi->mWidth + fgi->mXBearing) - fgi->mXAdvance); // difference between width of this character and advance to next character
+
+ cur_x += fgi->mXAdvance;
+
+ // clip if current character runs past scaled_max_pixels (using width_padding)
+ if (scaled_max_pixels < cur_x + width_padding)
+ {
+ clip = TRUE;
+ break;
+ }
+
+ if (((i+1) < max_chars) && wchars[i+1])
+ {
+ // Kern this puppy.
+ next_glyph = mFontFreetype->getGlyphInfo(wchars[i+1]);
+ cur_x += mFontFreetype->getXKerning(fgi, next_glyph);
}
+
// Round after kerning.
- cur_x = (F32)llfloor(cur_x + 0.5f);
+ cur_x = llround(cur_x);
drawn_x = cur_x;
}
- if( clip && end_on_word_boundary && (start_of_last_word != 0) )
- {
- i = start_of_last_word;
- }
- if (drawn_pixels)
+ if( clip )
{
- *drawn_pixels = drawn_x;
+ switch (end_on_word_boundary)
+ {
+ case ONLY_WORD_BOUNDARIES:
+ i = start_of_last_word;
+ break;
+ case WORD_BOUNDARY_IF_POSSIBLE:
+ if (start_of_last_word != 0)
+ {
+ i = start_of_last_word;
+ }
+ break;
+ default:
+ case ANYWHERE:
+ // do nothing
+ break;
+ }
}
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)
@@ -1116,64 +674,51 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_
{
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;
- }
+ const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch);
- if ( i > 0 )
- {
- total_width += EXT_KERNING * sScaleX;
- }
+ // last character uses character width, since the whole character needs to be visible
+ // other characters just use advance
+ F32 width = (i == start)
+ ? (F32)(fgi->mWidth + fgi->mXBearing) // use actual width for last character
+ : fgi->mXAdvance; // use advance for all other characters
- // Round after kerning.
- total_width = (F32)llfloor(total_width + 0.5f);
- }
- else
+ if( scaled_max_pixels < (total_width + width) )
{
- F32 char_width = getXAdvance(wch);
- if( scaled_max_pixels < (total_width + char_width) )
- {
- break;
- }
-
- total_width += char_width;
+ break;
+ }
- drawable_chars++;
- if( max_chars >= 0 && drawable_chars >= max_chars )
- {
- break;
- }
+ total_width += width;
+ drawable_chars++;
- if ( i > 0 )
- {
- // Kerning
- total_width += getXKerning(wchars[i-1], wch);
- }
+ if( max_chars >= 0 && drawable_chars >= max_chars )
+ {
+ break;
+ }
- // Round after kerning.
- total_width = (F32)llfloor(total_width + 0.5f);
+ if ( i > 0 )
+ {
+ // kerning
+ total_width += mFontFreetype->getXKerning(wchars[i-1], wch);
}
+
+ // Round after kerning.
+ total_width = llround(total_width);
}
- return text_len - drawable_chars;
+ if (drawable_chars == 0)
+ {
+ return start_pos; // just draw last character
+ }
+ else
+ {
+ // if only 1 character is drawable, we want to return start_pos as the first character to draw
+ // if 2 are drawable, return start_pos and character before start_pos, etc.
+ return start_pos + 1 - 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
+S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round) const
{
if (!wchars || !wchars[0] || max_chars == 0)
{
@@ -1181,327 +726,202 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, const S32 begin_offset,
}
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);
+ const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars - 1);
F32 scaled_max_pixels = max_pixels * sScaleX;
+
+ const LLFontGlyphInfo* next_glyph = NULL;
- for (S32 i = begin_offset; (i < max_index); i++)
+ S32 pos;
+ for (pos = begin_offset; pos < max_index; pos++)
{
- llwchar wch = wchars[i];
+ llwchar wch = wchars[pos];
if (!wch)
{
break; // done
}
- const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
- if (ext_data)
+
+ const LLFontGlyphInfo* glyph = next_glyph;
+ next_glyph = NULL;
+ if(!glyph)
{
- 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;
- }
- }
+ glyph = mFontFreetype->getGlyphInfo(wch);
+ }
+
+ F32 char_width = mFontFreetype->getXAdvance(glyph);
- if (scaled_max_pixels < cur_x + ext_advance)
+ 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;
}
-
- 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
+ else if (target_x < cur_x + char_width)
{
- F32 char_width = getXAdvance(wch);
+ break;
+ }
- 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;
+ }
- if (scaled_max_pixels < cur_x + char_width)
- {
- break;
- }
+ cur_x += char_width;
- pos++;
- cur_x += char_width;
+ if (((pos + 1) < max_index)
+ && (wchars[(pos + 1)]))
+ {
+ // Kern this puppy.
+ next_glyph = mFontFreetype->getGlyphInfo(wchars[pos + 1]);
+ cur_x += mFontFreetype->getXKerning(glyph, next_glyph);
+ }
- 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);
- }
+ // Round after kerning.
+ cur_x = llround(cur_x);
}
- return pos;
+ return llmin(max_chars, pos - begin_offset);
}
-
-const LLFontGL::embedded_data_t* LLFontGL::getEmbeddedCharData(const llwchar wch) const
+const LLFontDescriptor& LLFontGL::getFontDesc() const
{
- // Handle crappy embedded hack
- embedded_map_t::const_iterator iter = mEmbeddedChars.find(wch);
- if (iter != mEmbeddedChars.end())
- {
- return iter->second;
- }
- return NULL;
+ return mFontDescriptor;
}
-
-F32 LLFontGL::getEmbeddedCharAdvance(const embedded_data_t* ext_data) const
+// static
+void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector<std::string>& xui_paths, bool create_gl_textures)
{
- const LLWString& label = ext_data->mLabel;
- LLImageGL* ext_image = ext_data->mImage;
+ sVertDPI = (F32)llfloor(screen_dpi * y_scale);
+ sHorizDPI = (F32)llfloor(screen_dpi * x_scale);
+ sScaleX = x_scale;
+ sScaleY = y_scale;
+ sAppDir = app_dir;
- F32 ext_width = (F32)ext_image->getWidth();
- if( !label.empty() )
+ // Font registry init
+ if (!sFontRegistry)
{
- ext_width += (EXT_X_BEARING + gExtCharFont->getWidthF32(label.c_str())) * sScaleX;
+ sFontRegistry = new LLFontRegistry(xui_paths, create_gl_textures);
+ sFontRegistry->parseFontInfo("fonts.xml");
}
-
- 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())
+ else
{
- delete iter->second;
- mEmbeddedChars.erase(wc);
+ sFontRegistry->reset();
}
}
-
-void LLFontGL::renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const
+// Force standard fonts to get generated up front.
+// This is primarily for error detection purposes.
+// Don't do this during initClass because it can be slow and we want to get
+// the viewer window on screen first. JC
+// static
+bool LLFontGL::loadDefaultFonts()
{
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop);
- gGL.vertex2f(llfont_round_x(screen_rect.mRight),
- llfont_round_y(screen_rect.mTop));
-
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mTop);
- gGL.vertex2f(llfont_round_x(screen_rect.mLeft),
- llfont_round_y(screen_rect.mTop));
-
- gGL.texCoord2f(uv_rect.mLeft, uv_rect.mBottom);
- gGL.vertex2f(llfont_round_x(screen_rect.mLeft + slant_amt),
- llfont_round_y(screen_rect.mBottom));
-
- gGL.texCoord2f(uv_rect.mRight, uv_rect.mBottom);
- gGL.vertex2f(llfont_round_x(screen_rect.mRight + slant_amt),
- llfont_round_y(screen_rect.mBottom));
+ bool succ = true;
+ succ &= (NULL != getFontSansSerifSmall());
+ succ &= (NULL != getFontSansSerif());
+ succ &= (NULL != getFontSansSerifBig());
+ succ &= (NULL != getFontSansSerifHuge());
+ succ &= (NULL != getFontSansSerifBold());
+ succ &= (NULL != getFontMonospace());
+ succ &= (NULL != getFontExtChar());
+ return succ;
}
-void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, F32 drop_shadow_strength) const
+// static
+void LLFontGL::destroyDefaultFonts()
{
- F32 slant_offset;
- slant_offset = ((style & ITALIC) ? ( -mAscender * 0.2f) : 0.f);
+ // Remove the actual fonts.
+ delete sFontRegistry;
+ sFontRegistry = NULL;
+}
- gGL.begin(LLVertexBuffer::QUADS);
+//static
+void LLFontGL::destroyAllGL()
+{
+ if (sFontRegistry)
{
- //FIXME: bold and drop shadow are mutually exclusive only for convenience
- //Allow both when we need them.
- if (style & BOLD)
- {
- gGL.color4fv(color.mV);
- for (S32 pass = 0; pass < 2; pass++)
- {
- LLRectf screen_rect_offset = screen_rect;
-
- screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f);
- renderQuad(screen_rect_offset, uv_rect, slant_offset);
- }
- }
- else if (style & DROP_SHADOW_SOFT)
- {
- LLColor4 shadow_color = LLFontGL::sShadowColor;
- shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH;
- gGL.color4fv(shadow_color.mV);
- for (S32 pass = 0; pass < 5; pass++)
- {
- LLRectf screen_rect_offset = screen_rect;
-
- switch(pass)
- {
- case 0:
- screen_rect_offset.translate(-1.f, -1.f);
- break;
- case 1:
- screen_rect_offset.translate(1.f, -1.f);
- break;
- case 2:
- screen_rect_offset.translate(1.f, 1.f);
- break;
- case 3:
- screen_rect_offset.translate(-1.f, 1.f);
- break;
- case 4:
- screen_rect_offset.translate(0, -2.f);
- break;
- }
-
- renderQuad(screen_rect_offset, uv_rect, slant_offset);
- }
- gGL.color4fv(color.mV);
- renderQuad(screen_rect, uv_rect, slant_offset);
- }
- else if (style & DROP_SHADOW)
- {
- LLColor4 shadow_color = LLFontGL::sShadowColor;
- shadow_color.mV[VALPHA] = color.mV[VALPHA] * drop_shadow_strength;
- gGL.color4fv(shadow_color.mV);
- LLRectf screen_rect_shadow = screen_rect;
- screen_rect_shadow.translate(1.f, -1.f);
- renderQuad(screen_rect_shadow, uv_rect, slant_offset);
- gGL.color4fv(color.mV);
- renderQuad(screen_rect, uv_rect, slant_offset);
- }
- else // normal rendering
- {
- gGL.color4fv(color.mV);
- renderQuad(screen_rect, uv_rect, slant_offset);
- }
-
+ sFontRegistry->destroyGL();
}
- gGL.end();
}
// static
-LLString LLFontGL::nameFromFont(const LLFontGL* fontp)
+U8 LLFontGL::getStyleFromString(const std::string &style)
{
- if (fontp == sSansSerifHuge)
- {
- return LLString("SansSerifHuge");
- }
- else if (fontp == sSansSerifSmall)
- {
- return LLString("SansSerifSmall");
- }
- else if (fontp == sSansSerif)
- {
- return LLString("SansSerif");
- }
- else if (fontp == sSansSerifBig)
+ S32 ret = 0;
+ if (style.find("NORMAL") != style.npos)
{
- return LLString("SansSerifBig");
+ ret |= NORMAL;
}
- else if (fontp == sSansSerifBold)
+ if (style.find("BOLD") != style.npos)
{
- return LLString("SansSerifBold");
+ ret |= BOLD;
}
- else if (fontp == sMonospace)
+ if (style.find("ITALIC") != style.npos)
{
- return LLString("Monospace");
+ ret |= ITALIC;
}
- else
+ if (style.find("UNDERLINE") != style.npos)
{
- return LLString();
+ ret |= UNDERLINE;
}
+ return ret;
}
// static
-LLFontGL* LLFontGL::fontFromName(const LLString& font_name)
+std::string LLFontGL::getStringFromStyle(U8 style)
{
- 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")
+ std::string style_string;
+ if (style & NORMAL)
{
- gl_font = LLFontGL::sSansSerif;
+ style_string += "|NORMAL";
}
- else if (font_name == "SansSerifBig")
+ if (style & BOLD)
{
- gl_font = LLFontGL::sSansSerifBig;
+ style_string += "|BOLD";
}
- else if (font_name == "SansSerifBold")
+ if (style & ITALIC)
{
- gl_font = LLFontGL::sSansSerifBold;
+ style_string += "|ITALIC";
}
- else if (font_name == "Monospace")
+ if (style & UNDERLINE)
{
- gl_font = LLFontGL::sMonospace;
+ style_string += "|UNDERLINE";
}
- return gl_font;
+ return style_string;
+}
+
+// static
+std::string LLFontGL::nameFromFont(const LLFontGL* fontp)
+{
+ return fontp->mFontDescriptor.getName();
+}
+
+
+// static
+std::string LLFontGL::sizeFromFont(const LLFontGL* fontp)
+{
+ return fontp->mFontDescriptor.getSize();
}
// static
-LLString LLFontGL::nameFromHAlign(LLFontGL::HAlign align)
+std::string 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();
+ if (align == LEFT) return std::string("left");
+ else if (align == RIGHT) return std::string("right");
+ else if (align == HCENTER) return std::string("center");
+ else return std::string();
}
// static
-LLFontGL::HAlign LLFontGL::hAlignFromName(const LLString& name)
+LLFontGL::HAlign LLFontGL::hAlignFromName(const std::string& name)
{
LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
if (name == "left")
@@ -1521,17 +941,17 @@ LLFontGL::HAlign LLFontGL::hAlignFromName(const LLString& name)
}
// static
-LLString LLFontGL::nameFromVAlign(LLFontGL::VAlign align)
+std::string 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();
+ if (align == TOP) return std::string("top");
+ else if (align == VCENTER) return std::string("center");
+ else if (align == BASELINE) return std::string("baseline");
+ else if (align == BOTTOM) return std::string("bottom");
+ else return std::string();
}
// static
-LLFontGL::VAlign LLFontGL::vAlignFromName(const LLString& name)
+LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& name)
{
LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE;
if (name == "top")
@@ -1553,3 +973,244 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const LLString& name)
//else leave baseline
return gl_vfont_align;
}
+
+//static
+LLFontGL* LLFontGL::getFontMonospace()
+{
+ return getFont(LLFontDescriptor("Monospace","Monospace",0));
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifSmall()
+{
+ return getFont(LLFontDescriptor("SansSerif","Small",0));
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerif()
+{
+ return getFont(LLFontDescriptor("SansSerif","Medium",0));
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifBig()
+{
+ return getFont(LLFontDescriptor("SansSerif","Large",0));
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifHuge()
+{
+ return getFont(LLFontDescriptor("SansSerif","Huge",0));
+}
+
+//static
+LLFontGL* LLFontGL::getFontSansSerifBold()
+{
+ return getFont(LLFontDescriptor("SansSerif","Medium",BOLD));
+}
+
+//static
+LLFontGL* LLFontGL::getFontExtChar()
+{
+ return getFontSansSerif();
+}
+
+//static
+LLFontGL* LLFontGL::getFont(const LLFontDescriptor& desc)
+{
+ return sFontRegistry->getFont(desc);
+}
+
+//static
+LLFontGL* LLFontGL::getFontByName(const std::string& name)
+{
+ // check for most common fonts first
+ if (name == "SANSSERIF")
+ {
+ return getFontSansSerif();
+ }
+ else if (name == "SANSSERIF_SMALL")
+ {
+ return getFontSansSerifSmall();
+ }
+ else if (name == "SANSSERIF_BIG")
+ {
+ return getFontSansSerifBig();
+ }
+ else if (name == "SMALL" || name == "OCRA")
+ {
+ // *BUG: Should this be "MONOSPACE"? Do we use "OCRA" anymore?
+ // Does "SMALL" mean "SERIF"?
+ return getFontMonospace();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//static
+LLFontGL* LLFontGL::getFontDefault()
+{
+ return getFontSansSerif(); // Fallback to sans serif as default font
+}
+
+
+// static
+std::string LLFontGL::getFontPathSystem()
+{
+ std::string 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
+std::string LLFontGL::getFontPathLocal()
+{
+ std::string 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;
+}
+
+LLFontGL::LLFontGL(const LLFontGL &source)
+{
+ llerrs << "Not implemented!" << llendl;
+}
+
+LLFontGL &LLFontGL::operator=(const LLFontGL &source)
+{
+ llerrs << "Not implemented" << llendl;
+ return *this;
+}
+
+void LLFontGL::renderQuad(LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, F32 slant_amt) const
+{
+ S32 index = 0;
+
+ vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mRight), llfont_round_y(screen_rect.mTop), 0.f);
+ uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mTop);
+ colors_out[index] = color;
+ index++;
+
+ vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mLeft), llfont_round_y(screen_rect.mTop), 0.f);
+ uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mTop);
+ colors_out[index] = color;
+ index++;
+
+ vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mLeft), llfont_round_y(screen_rect.mBottom), 0.f);
+ uv_out[index] = LLVector2(uv_rect.mLeft, uv_rect.mBottom);
+ colors_out[index] = color;
+ index++;
+
+ vertex_out[index] = LLVector3(llfont_round_x(screen_rect.mRight), llfont_round_y(screen_rect.mBottom), 0.f);
+ uv_out[index] = LLVector2(uv_rect.mRight, uv_rect.mBottom);
+ colors_out[index] = color;
+}
+
+void LLFontGL::drawGlyph(S32& glyph_count, LLVector3* vertex_out, LLVector2* uv_out, LLColor4U* colors_out, const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4U& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const
+{
+ F32 slant_offset;
+ slant_offset = ((style & ITALIC) ? ( -mFontFreetype->getAscenderHeight() * 0.2f) : 0.f);
+
+ //FIXME: bold and drop shadow are mutually exclusive only for convenience
+ //Allow both when we need them.
+ if (style & BOLD)
+ {
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ LLRectf screen_rect_offset = screen_rect;
+
+ screen_rect_offset.translate((F32)(pass * BOLD_OFFSET), 0.f);
+ renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_offset, uv_rect, color, slant_offset);
+ glyph_count++;
+ }
+ }
+ else if (shadow == DROP_SHADOW_SOFT)
+ {
+ LLColor4U shadow_color = LLFontGL::sShadowColor;
+ shadow_color.mV[VALPHA] = U8(color.mV[VALPHA] * drop_shadow_strength * DROP_SHADOW_SOFT_STRENGTH);
+ for (S32 pass = 0; pass < 5; pass++)
+ {
+ LLRectf screen_rect_offset = screen_rect;
+
+ switch(pass)
+ {
+ case 0:
+ screen_rect_offset.translate(-1.f, -1.f);
+ break;
+ case 1:
+ screen_rect_offset.translate(1.f, -1.f);
+ break;
+ case 2:
+ screen_rect_offset.translate(1.f, 1.f);
+ break;
+ case 3:
+ screen_rect_offset.translate(-1.f, 1.f);
+ break;
+ case 4:
+ screen_rect_offset.translate(0, -2.f);
+ break;
+ }
+
+ renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_offset, uv_rect, shadow_color, slant_offset);
+ glyph_count++;
+ }
+ renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
+ glyph_count++;
+ }
+ else if (shadow == DROP_SHADOW)
+ {
+ LLColor4U shadow_color = LLFontGL::sShadowColor;
+ shadow_color.mV[VALPHA] = U8(color.mV[VALPHA] * drop_shadow_strength);
+ LLRectf screen_rect_shadow = screen_rect;
+ screen_rect_shadow.translate(1.f, -1.f);
+ renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect_shadow, uv_rect, shadow_color, slant_offset);
+ glyph_count++;
+ renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
+ glyph_count++;
+ }
+ else // normal rendering
+ {
+ renderQuad(&vertex_out[glyph_count * 4], &uv_out[glyph_count * 4], &colors_out[glyph_count * 4], screen_rect, uv_rect, color, slant_offset);
+ glyph_count++;
+ }
+}