diff options
Diffstat (limited to 'indra/llrender/llfontgl.cpp')
-rw-r--r-- | indra/llrender/llfontgl.cpp | 1485 |
1 files changed, 701 insertions, 784 deletions
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 048bfe8e0d..386bb987f9 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -2,31 +2,25 @@ * @file llfontgl.cpp * @brief Wrapper around FreeType * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, 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://secondlifegrid.net/programs/open_source/licensing/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://secondlifegrid.net/programs/open_source/licensing/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$ */ @@ -35,7 +29,7 @@ #include "llfontgl.h" // Linden library includes -#include "llfont.h" +#include "llfontfreetype.h" #include "llfontbitmapcache.h" #include "llfontregistry.h" #include "llgl.h" @@ -43,6 +37,8 @@ #include "llrender.h" #include "llstl.h" #include "v4color.h" +#include "lltexture.h" +#include "lldir.h" // Third party library includes #include <boost/tokenizer.hpp> @@ -72,349 +68,81 @@ const F32 PIXEL_CORRECTION_DISTANCE = 0.01f; 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 std::string &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() -{ - clearEmbeddedChars(); -} - -LLFontGL::LLFontGL(const LLFontGL &source) { - llerrs << "Not implemented!" << llendl; } LLFontGL::~LLFontGL() { - clearEmbeddedChars(); } void LLFontGL::reset() { - if (!mIsFallback) - { - // This is the head of the list - need to rebuild ourself and all fallbacks. - loadFace(mName,mPointSize,sVertDPI,sHorizDPI,mFontBitmapCachep->getNumComponents(),mIsFallback); - if (mFallbackFontp==NULL) - { - llwarns << "LLFontGL::reset(), no fallback fonts present" << llendl; - } - else - { - for (LLFontList::iterator it = mFallbackFontp->begin(); - it != mFallbackFontp->end(); - ++it) - { - (*it)->reset(); - } - } - } - resetBitmapCache(); -} - -// 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; -} - -bool findOrCreateFont(LLFontGL*& fontp, const LLFontDescriptor& desc) -{ - // Don't delete existing fonts, if any, here, because they've - // already been deleted by LLFontRegistry::clear() - fontp = LLFontGL::getFont(desc); - return (fontp != NULL); -} - -// 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) -{ - sVertDPI = (F32)llfloor(screen_dpi * y_scale); - sHorizDPI = (F32)llfloor(screen_dpi * x_scale); - sScaleX = x_scale; - sScaleY = y_scale; - sAppDir = app_dir; - - // Font registry init - if (!sFontRegistry) - { - sFontRegistry = new LLFontRegistry(xui_paths, create_gl_textures); - sFontRegistry->parseFontInfo("fonts.xml"); - } - else - { - sFontRegistry->reset(); - } -} - -// 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() -{ - 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; -} - - - -// static -void LLFontGL::destroyDefaultFonts() -{ - // Remove the actual fonts. - delete sFontRegistry; - sFontRegistry = NULL; -} - -//static -void LLFontGL::destroyAllGL() -{ - if (sFontRegistry) - { - sFontRegistry->destroyGL(); - } + mFontFreetype->reset(sVertDPI, sHorizDPI); } void LLFontGL::destroyGL() { - mFontBitmapCachep->destroyGL(); + mFontFreetype->destroyGL(); } - - -LLFontGL &LLFontGL::operator=(const LLFontGL &source) +BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dpi, F32 horz_dpi, S32 components, BOOL is_fallback) { - 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)) + if(mFontFreetype == reinterpret_cast<LLFontFreetype*>(NULL)) { - return FALSE; + mFontFreetype = new LLFontFreetype; } - return TRUE; -} - -//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)); + return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback); } -//static -LLFontGL* LLFontGL::getFontSansSerifHuge() -{ - return getFont(LLFontDescriptor("SansSerif","Huge",0)); -} +static LLFastTimer::DeclareTimer FTM_RENDER_FONTS("Fonts"); -//static -LLFontGL* LLFontGL::getFontSansSerifBold() -{ - return getFont(LLFontDescriptor("SansSerif","Medium",BOLD)); -} - -//static -LLFontGL* LLFontGL::getFontExtChar() -{ - return getFontSansSerif(); -} - -//static -LLFontGL* LLFontGL::getFont(const LLFontDescriptor& desc) +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 { - return sFontRegistry->getFont(desc); -} + F32 x = rect.mLeft; + F32 y = 0.f; -//static -LLFontGL* LLFontGL::getFontByName(const std::string& name) -{ - // check for most common fonts first - if (name == "SANSSERIF") - { - return getFontSansSerif(); - } - else if (name == "SANSSERIF_SMALL") + switch(valign) { - 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; - } -} - -BOOL LLFontGL::addChar(const llwchar wch) const -{ - 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(); - - LLFontGlyphInfo *glyph_info = getGlyphInfo(wch); - U32 bitmap_num = glyph_info->mBitmapNum; - LLImageGL *image_gl = mFontBitmapCachep->getImageGL(bitmap_num); - LLImageRaw *image_raw = mFontBitmapCachep->getImageRaw(bitmap_num); - image_gl->setSubImage(image_raw, 0, 0, image_gl->getWidth(), image_gl->getHeight()); - 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 std::string &text, const S32 offset, - const F32 x, const F32 y, - const LLColor4 &color, - const HAlign halign, const VAlign valign, - U8 style, - ShadowType shadow, - 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, shadow, max_chars, max_pixels, right_x, FALSE, 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, - ShadowType shadow, - 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() ; @@ -429,8 +157,9 @@ S32 LLFontGL::render(const LLWString &wstr, S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); - // Strip off any style bits that are already accounted for by the font. - style = style & (~getFontDesc().getStyle()); + // 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 (shadow != NO_SHADOW) @@ -444,18 +173,22 @@ S32 LLFontGL::render(const LLWString &wstr, } } - gGL.pushMatrix(); - glLoadIdentity(); - gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); + gGL.pushUIMatrix(); + + gGL.loadUIIdentity(); + + //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.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); + //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); - LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); + 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); - gGL.color4fv( color.mV ); S32 chars_drawn = 0; S32 i; @@ -475,20 +208,20 @@ S32 LLFontGL::render(const LLWString &wstr, // 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. @@ -502,10 +235,10 @@ 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; @@ -514,19 +247,22 @@ S32 LLFontGL::render(const LLWString &wstr, cur_render_y = cur_y; cur_render_x = cur_x; - F32 start_x = cur_x; + F32 start_x = llround(cur_x); - F32 inv_width = 1.f / mFontBitmapCachep->getBitmapWidth(); - F32 inv_height = 1.f / mFontBitmapCachep->getBitmapHeight(); + const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); - const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; + F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); + F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); + + 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(std::string("...."))); @@ -536,185 +272,178 @@ S32 LLFontGL::render(const LLWString &wstr, } - // Remember last-used texture to avoid unnecesssary bind calls. - LLImageGL *last_bound_texture = NULL; + 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 + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; - } - - if (start_x + scaled_max_pixels < cur_x + ext_advance) - { - // Not enough room for this character. - break; - } - - if (last_bound_texture != ext_image) - { - gGL.getTexUnit(0)->bind(ext_image); - last_bound_texture = ext_image; - } - - // snap origin to whole screen pixel - const F32 ext_x = (F32)llround(cur_render_x + (EXT_X_BEARING * sScaleX)); - const F32 ext_y = (F32)llround(cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight)); - - 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, shadow, drop_shadow_strength); - - if (!label.empty()) - { - gGL.pushMatrix(); - //glLoadIdentity(); - //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); - //glScalef(sScaleX, sScaleY, 1.f); - getFontExtChar()->render(label, 0, - /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), - /*llfloor*/(cur_y / sScaleY), - color, - halign, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, - TRUE ); - gGL.popMatrix(); - } - - 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; + fgi = mFontFreetype->getGlyphInfo(wch); } - else + if (!fgi) { - if (!hasGlyph(wch)) - { - addChar(wch); - } + 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; + } - const LLFontGlyphInfo* fgi= getGlyphInfo(wch); - if (!fgi) - { - llerrs << "Missing Glyph Info" << llendl; - break; - } - // Per-glyph bitmap texture. - LLImageGL *image_gl = mFontBitmapCachep->getImageGL(fgi->mBitmapNum); - if (last_bound_texture != image_gl) + // 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); { - gGL.getTexUnit(0)->bind(image_gl); - last_bound_texture = image_gl; + gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } + gGL.end(); - if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) - { - // Not enough room for this character. - break; - } + glyph_count = 0; + } - // 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); - - drawGlyph(screen_rect, uv_rect, color, style, shadow, drop_shadow_strength); + drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength); - chars_drawn++; - cur_x += fgi->mXAdvance; - cur_y += fgi->mYAdvance; + 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)) - { - addChar(next_char); - } - cur_x += getXKerning(wch, next_char); - } + llwchar next_char = wstr[i+1]; + if (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. - // 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); + // 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); + + 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) { + F32 descender = mFontFreetype->getDescenderHeight(); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); - gGL.vertex2f(start_x, cur_y - (mDescender)); - gGL.vertex2f(cur_x, cur_y - (mDescender)); + 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); + 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; } +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 renderUTF8(text, begin_offset, (F32)x, (F32)y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); +} + +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 { @@ -727,15 +456,15 @@ S32 LLFontGL::getWidth(const llwchar* wchars) const return getWidth(wchars, 0, S32_MAX); } -S32 LLFontGL::getWidth(const std::string& 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); } @@ -750,62 +479,65 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars) const return getWidthF32(wchars, 0, S32_MAX); } -F32 LLFontGL::getWidthF32(const std::string& 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) { @@ -822,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++) @@ -835,100 +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 - { - start_of_last_word = i; - } - cur_x += getEmbeddedCharAdvance(ext_data); - - if (scaled_max_pixels < cur_x) + if (iswspace(wch)) { - clip = TRUE; - break; + if(wch !=(0x00A0)) + { + in_word = FALSE; + } } - - if (((i+1) < max_chars) && wchars[i+1]) + if (iswindividual(wch)) { - cur_x += EXT_KERNING * sScaleX; - } - - if( scaled_max_pixels < cur_x ) - { - clip = TRUE; - break; - } - } - else - { - if (in_word) - { - if (iswspace(wch)) + if (iswpunct(wchars[i+1])) { - if(wch !=(0x00A0)) - { - in_word = FALSE; - } + in_word=TRUE; } - if (iswindividual(wch)) + else { - if (iswpunct(wchars[i+1])) - { - in_word=TRUE; - } - else - { - in_word=FALSE; - start_of_last_word = i; - } + in_word=FALSE; + start_of_last_word = i; } } - else + } + else + { + start_of_last_word = i; + if (!iswspace(wch)||!iswindividual(wch)) { - start_of_last_word = i; - if (!iswspace(wch)||!iswindividual(wch)) - { - in_word = TRUE; - } + in_word = TRUE; } + } + + LLFontGlyphInfo* fgi = next_glyph; + next_glyph = NULL; + if(!fgi) + { + fgi = mFontFreetype->getGlyphInfo(wch); + } - cur_x += getXAdvance(wch); - - if (scaled_max_pixels < cur_x) - { - clip = TRUE; - break; - } + // 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 - if (((i+1) < max_chars) && wchars[i+1]) - { - // Kern this puppy. - cur_x += getXKerning(wch, wchars[i+1]); - } + 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) @@ -946,15 +674,20 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_ { llwchar wch = wchars[i]; - const embedded_data_t* ext_data = getEmbeddedCharData(wch); - F32 char_width = ext_data ? getEmbeddedCharAdvance(ext_data) : getXAdvance(wch); + const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch); - if( scaled_max_pixels < (total_width + char_width) ) + // 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 + + if( scaled_max_pixels < (total_width + width) ) { break; } - total_width += char_width; + total_width += width; drawable_chars++; if( max_chars >= 0 && drawable_chars >= max_chars ) @@ -965,18 +698,27 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_ if ( i > 0 ) { // kerning - total_width += ext_data ? (EXT_KERNING * sScaleX) : getXKerning(wchars[i-1], wch); + total_width += mFontFreetype->getXKerning(wchars[i-1], wch); } // Round after kerning. total_width = llround(total_width); } - return start_pos - 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) { @@ -984,255 +726,189 @@ 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 + getFontExtChar()->getWidthF32(label.c_str())) * sScaleX; + sFontRegistry = new LLFontRegistry(xui_paths, create_gl_textures); + sFontRegistry->parseFontInfo("fonts.xml"); + } + else + { + sFontRegistry->reset(); } - - 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 std::string& label ) 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() { - LLWString wlabel = utf8str_to_wstring(label); - addEmbeddedChar(wc, image, wlabel); + 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::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& wlabel ) const +// static +void LLFontGL::destroyDefaultFonts() { - embedded_data_t* ext_data = new embedded_data_t(image, wlabel); - mEmbeddedChars[wc] = ext_data; + // Remove the actual fonts. + delete sFontRegistry; + sFontRegistry = NULL; } -void LLFontGL::removeEmbeddedChar( llwchar wc ) const +//static +void LLFontGL::destroyAllGL() { - embedded_map_t::iterator iter = mEmbeddedChars.find(wc); - if (iter != mEmbeddedChars.end()) + if (sFontRegistry) { - delete iter->second; - mEmbeddedChars.erase(wc); + sFontRegistry->destroyGL(); } } - -void LLFontGL::renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const +// static +U8 LLFontGL::getStyleFromString(const std::string &style) { - 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)); + 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; } -void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, ShadowType shadow, F32 drop_shadow_strength) const +// static +std::string LLFontGL::getStringFromStyle(U8 style) { - F32 slant_offset; - slant_offset = ((style & ITALIC) ? ( -mAscender * 0.2f) : 0.f); - - gGL.begin(LLRender::QUADS); + std::string style_string; + if (style & NORMAL) { - //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 (shadow == 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 (shadow == 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); - } - + style_string += "|NORMAL"; } - gGL.end(); + if (style & BOLD) + { + style_string += "|BOLD"; + } + if (style & ITALIC) + { + style_string += "|ITALIC"; + } + if (style & UNDERLINE) + { + style_string += "|UNDERLINE"; + } + return style_string; } +// static std::string LLFontGL::nameFromFont(const LLFontGL* fontp) { - return fontp->getFontDesc().getName(); + return fontp->mFontDescriptor.getName(); +} + + +// static +std::string LLFontGL::sizeFromFont(const LLFontGL* fontp) +{ + return fontp->mFontDescriptor.getSize(); } // static @@ -1297,3 +973,244 @@ LLFontGL::VAlign LLFontGL::vAlignFromName(const std::string& 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++; + } +} |