diff options
author | Christian Goetze <cg@lindenlab.com> | 2009-03-13 21:28:40 +0000 |
---|---|---|
committer | Christian Goetze <cg@lindenlab.com> | 2009-03-13 21:28:40 +0000 |
commit | 1aa0416aef379bb3ad1012441588b6d7fab81b40 (patch) | |
tree | 14a247470bd0d508aba923dc00e940b961d304da /indra/llrender | |
parent | 7573288ab3ede23f97bff2f5caefcb622e7e9842 (diff) |
svn merge -r114093:114412 svn+ssh://svn.lindenlab.com/svn/linden/branches/featurettes/featurettes-batch5-merge
Melinda (coco): 5th and final batch of featurettes. My work here is done.
Diffstat (limited to 'indra/llrender')
-rw-r--r-- | indra/llrender/CMakeLists.txt | 8 | ||||
-rw-r--r-- | indra/llrender/llfontbitmapcache.cpp | 168 | ||||
-rw-r--r-- | indra/llrender/llfontbitmapcache.h | 78 | ||||
-rw-r--r-- | indra/llrender/llfontgl.cpp | 552 | ||||
-rw-r--r-- | indra/llrender/llfontgl.h | 69 | ||||
-rw-r--r-- | indra/llrender/llfontregistry.cpp | 650 | ||||
-rw-r--r-- | indra/llrender/llfontregistry.h | 113 | ||||
-rw-r--r-- | indra/llrender/llimagegl.cpp | 3 |
8 files changed, 1201 insertions, 440 deletions
diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index 72aa3715c0..0bdb55f9d0 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -9,6 +9,8 @@ include(LLImage) include(LLMath) include(LLRender) include(LLWindow) +include(LLXML) +include(LLVFS) include_directories( ${FREETYPE_INCLUDE_DIRS} @@ -17,12 +19,16 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} + ${LLXML_INCLUDE_DIRS} + ${LLVFS_INCLUDE_DIRS} ) set(llrender_SOURCE_FILES llcubemap.cpp llfont.cpp llfontgl.cpp + llfontbitmapcache.cpp + llfontregistry.cpp llgldbg.cpp llglslshader.cpp llimagegl.cpp @@ -38,6 +44,8 @@ set(llrender_HEADER_FILES llcubemap.h llfontgl.h llfont.h + llfontbitmapcache.h + llfontregistry.h llgl.h llgldbg.h llglheaders.h diff --git a/indra/llrender/llfontbitmapcache.cpp b/indra/llrender/llfontbitmapcache.cpp new file mode 100644 index 0000000000..f6321b0534 --- /dev/null +++ b/indra/llrender/llfontbitmapcache.cpp @@ -0,0 +1,168 @@ +/** + * @file llfontbitmapcache.cpp + * @brief Storage for previously rendered glyphs. + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llgl.h" +#include "llfontbitmapcache.h" + +LLFontBitmapCache::LLFontBitmapCache(): + mNumComponents(0), + mMaxCharWidth(0), + mMaxCharHeight(0), + mBitmapWidth(0), + mBitmapHeight(0), + mCurrentOffsetX(1), + mCurrentOffsetY(1), + mCurrentBitmapNum(-1) +{ +} + +LLFontBitmapCache::~LLFontBitmapCache() +{ +} + +void LLFontBitmapCache::init(S32 num_components, + S32 max_char_width, + S32 max_char_height) +{ + reset(); + + mNumComponents = num_components; + mMaxCharWidth = max_char_width; + mMaxCharHeight = max_char_height; +} + +LLImageRaw *LLFontBitmapCache::getImageRaw(U32 bitmap_num) const +{ + if ((bitmap_num < 0) || (bitmap_num >= mImageRawVec.size())) + return NULL; + + return mImageRawVec[bitmap_num]; +} + +LLImageGL *LLFontBitmapCache::getImageGL(U32 bitmap_num) const +{ + if ((bitmap_num < 0) || (bitmap_num >= mImageGLVec.size())) + return NULL; + + return mImageGLVec[bitmap_num]; +} + + +BOOL LLFontBitmapCache::nextOpenPos(S32 width, S32 &pos_x, S32 &pos_y, S32& bitmap_num) +{ + if ((mBitmapNum<0) || (mCurrentOffsetX + width + 1) > mBitmapWidth) + { + if ((mBitmapNum<0) || (mCurrentOffsetY + 2*mMaxCharHeight + 2) > mBitmapHeight) + { + // We're out of space in the current image, or no image + // has been allocated yet. Make a new one. + mImageRawVec.push_back(new LLImageRaw); + mBitmapNum = mImageRawVec.size()-1; + LLImageRaw *image_raw = getImageRaw(mBitmapNum); + + // Make corresponding GL image. + mImageGLVec.push_back(new LLImageGL(FALSE)); + LLImageGL *image_gl = getImageGL(mBitmapNum); + + S32 image_width = mMaxCharWidth * 20; + S32 pow_iw = 2; + while (pow_iw < image_width) + { + pow_iw *= 2; + } + image_width = pow_iw; + image_width = llmin(512, image_width); // Don't make bigger than 512x512, ever. + S32 image_height = image_width; + + image_raw->resize(image_width, image_height, mNumComponents); + + mBitmapWidth = image_width; + mBitmapHeight = image_height; + + switch (mNumComponents) + { + case 1: + image_raw->clear(); + break; + case 2: + image_raw->clear(255, 0); + break; + } + + // Start at beginning of the new image. + mCurrentOffsetX = 1; + mCurrentOffsetY = 1; + + // Attach corresponding GL texture. + image_gl->createGLTexture(0, image_raw); + gGL.getTexUnit(0)->bind(image_gl); + image_gl->setFilteringOption(LLTexUnit::TFO_POINT); // was setMipFilterNearest(TRUE, TRUE); + } + else + { + // Move to next row in current image. + mCurrentOffsetX = 1; + mCurrentOffsetY += mMaxCharHeight + 1; + } + } + + pos_x = mCurrentOffsetX; + pos_y = mCurrentOffsetY; + bitmap_num = mBitmapNum; + + mCurrentOffsetX += width + 1; + + return TRUE; +} + +void LLFontBitmapCache::destroyGL() +{ + for (std::vector<LLPointer<LLImageGL> >::iterator it = mImageGLVec.begin(); + it != mImageGLVec.end(); ++it) + { + (*it)->destroyGLTexture(); + } +} + +void LLFontBitmapCache::reset() +{ + mImageRawVec.clear(); + mImageGLVec.clear(); + + mBitmapWidth = 0, + mBitmapHeight = 0, + mCurrentOffsetX = 0, + mCurrentOffsetY = 0, + mCurrentBitmapNum = -1; +} + diff --git a/indra/llrender/llfontbitmapcache.h b/indra/llrender/llfontbitmapcache.h new file mode 100644 index 0000000000..e5c09f8826 --- /dev/null +++ b/indra/llrender/llfontbitmapcache.h @@ -0,0 +1,78 @@ +/** + * @file llfontbitmapcache.h + * @brief Storage for previously rendered glyphs. + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLFONTBITMAPCACHE_H +#define LL_LLFONTBITMAPCACHE_H + +#include <vector> + +// Maintain a collection of bitmaps containing rendered glyphs. +// Generalizes the single-bitmap logic from LLFont and LLFontGL. +class LLFontBitmapCache: public LLRefCount +{ +public: + LLFontBitmapCache(); + ~LLFontBitmapCache(); + + // Need to call this once, before caching any glyphs. + void init(S32 num_components, + S32 max_char_width, + S32 max_char_height); + + void reset(); + + BOOL nextOpenPos(S32 width, S32 &posX, S32 &posY, S32 &bitmapNum); + + void destroyGL(); + + LLImageRaw *getImageRaw(U32 bitmapNum = 0) const; + LLImageGL *getImageGL(U32 bitmapNum = 0) const; + + S32 getMaxCharWidth() const { return mMaxCharWidth; } + S32 getNumComponents() const { return mNumComponents; } + S32 getBitmapWidth() const { return mBitmapWidth; } + S32 getBitmapHeight() const { return mBitmapHeight; } + +private: + S32 mNumComponents; + S32 mBitmapWidth; + S32 mBitmapHeight; + S32 mBitmapNum; + S32 mMaxCharWidth; + S32 mMaxCharHeight; + S32 mCurrentOffsetX; + S32 mCurrentOffsetY; + S32 mCurrentBitmapNum; + std::vector<LLPointer<LLImageRaw> > mImageRawVec; + std::vector<LLPointer<LLImageGL> > mImageGLVec; +}; + +#endif //LL_LLFONTBITMAPCACHE_H diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 3829306e25..e0c3ec3c24 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -36,6 +36,8 @@ #include "llfont.h" #include "llfontgl.h" +#include "llfontbitmapcache.h" +#include "llfontregistry.h" #include "llgl.h" #include "llrender.h" #include "v4color.h" @@ -51,25 +53,12 @@ F32 LLFontGL::sScaleY = 1.f; BOOL LLFontGL::sDisplayFont = TRUE ; std::string 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; 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; @@ -127,7 +116,6 @@ U8 LLFontGL::getStyleFromString(const std::string &style) LLFontGL::LLFontGL() : LLFont() { - init(); clearEmbeddedChars(); } @@ -138,32 +126,30 @@ LLFontGL::LLFontGL(const LLFontGL &source) LLFontGL::~LLFontGL() { - mImageGLp = NULL; - mRawImageGLp = NULL; clearEmbeddedChars(); } -void LLFontGL::init() +void LLFontGL::reset() { - if (mImageGLp.isNull()) - { - mImageGLp = new LLImageGL(FALSE); - //RN: use nearest mipmap filtering to obviate the need to do pixel-accurate positioning - gGL.getTexUnit(0)->bind(mImageGLp); - // we allow bilinear filtering to get sub-pixel positioning for drop shadows - //mImageGLp->setMipFilterNearest(TRUE, TRUE); - } - if (mRawImageGLp.isNull()) + resetBitmapCache(); + if (!mIsFallback) { - mRawImageGLp = new LLImageRaw; // Note LLFontGL owns the image, not LLFont. + // 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(); + } + } } - setRawImage( mRawImageGLp ); -} - -void LLFontGL::reset() -{ - init(); - resetBitmap(); } // static @@ -220,243 +206,48 @@ std::string LLFontGL::getFontPathLocal() return local_path; } -//static -bool LLFontGL::loadFaceFallback(LLFontList *fontlistp, const std::string& fontname, const F32 point_size) +bool findOrCreateFont(LLFontGL*& fontp, const LLFontDescriptor& desc) { - std::string local_path = getFontPathLocal(); - std::string 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(); - std::string 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); + // 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 -bool LLFontGL::loadFace(LLFontGL *fontp, const std::string& fontname, const F32 point_size, LLFontList *fallback_fontp) -{ - std::string local_path = getFontPathLocal(); - std::string font_path = local_path + fontname; - if (!fontp->loadFace(font_path, point_size, sVertDPI, sHorizDPI, 2, FALSE)) - { - std::string 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; - } - } - - fontp->setFallbackFont(fallback_fontp); - return true; -} - - // static BOOL LLFontGL::initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale, - const std::string& monospace_file, F32 monospace_size, - const std::string& sansserif_file, - const std::string& sanserif_fallback_file, F32 ss_fallback_scale, - F32 small_size, F32 medium_size, F32 big_size, F32 huge_size, - const std::string& sansserif_bold_file, F32 bold_size, - const std::string& app_dir) + const std::string& app_dir, + const std::vector<std::string>& xui_paths) { - BOOL failed = FALSE; + bool succ = true; 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) + // Font registry init + if (!sFontRegistry) { - 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(); + sFontRegistry = new LLFontRegistry(xui_paths); + sFontRegistry->parseFontInfo("fonts.xml"); } else { - sSansSerifHuge->reset(); + sFontRegistry->reset(); } - if (sSSHugeFallback) - { - delete sSSHugeFallback; - } - sSSHugeFallback = new LLFontList(); - if (!loadFaceFallback( - sSSHugeFallback, - sanserif_fallback_file, - huge_size*ss_fallback_scale)) - { - delete sSSHugeFallback; - sSSHugeFallback = NULL; - } - - failed |= !loadFace(sSansSerifHuge, sansserif_file, huge_size, sSSHugeFallback); - - - if(!sSansSerifBig) - { - sSansSerifBig = new LLFontGL(); - } - else - { - sSansSerifBig->reset(); - } - - if (sSSBigFallback) - { - delete sSSBigFallback; - } - sSSBigFallback = new LLFontList(); - if (!loadFaceFallback( - sSSBigFallback, - sanserif_fallback_file, - big_size*ss_fallback_scale)) - { - delete sSSBigFallback; - sSSBigFallback = NULL; - } - - failed |= !loadFace(sSansSerifBig, sansserif_file, big_size, sSSBigFallback); - - - if(!sSansSerif) - { - sSansSerif = new LLFontGL(); - } - else - { - sSansSerif->reset(); - } - - if (sSSFallback) - { - delete sSSFallback; - } - sSSFallback = new LLFontList(); - if (!loadFaceFallback( - sSSFallback, - sanserif_fallback_file, - medium_size*ss_fallback_scale)) - { - delete sSSFallback; - sSSFallback = NULL; - } - failed |= !loadFace(sSansSerif, sansserif_file, medium_size, sSSFallback); - - - if(!sSansSerifSmall) - { - sSansSerifSmall = new LLFontGL(); - } - else - { - sSansSerifSmall->reset(); - } - - if(sSSSmallFallback) - { - delete sSSSmallFallback; - } - sSSSmallFallback = new LLFontList(); - if (!loadFaceFallback( - sSSSmallFallback, - sanserif_fallback_file, - small_size*ss_fallback_scale)) - { - delete sSSSmallFallback; - sSSSmallFallback = NULL; - } - failed |= !loadFace(sSansSerifSmall, sansserif_file, small_size, sSSSmallFallback); - - - // - // Sans-serif bold - // - if(!sSansSerifBold) - { - sSansSerifBold = new LLFontGL(); - } - else - { - sSansSerifBold->reset(); - } - - if (sSSBoldFallback) - { - delete sSSBoldFallback; - } - sSSBoldFallback = new LLFontList(); - if (!loadFaceFallback( - sSSBoldFallback, - sanserif_fallback_file, - medium_size*ss_fallback_scale)) - { - delete sSSBoldFallback; - sSSBoldFallback = NULL; - } - failed |= !loadFace(sSansSerifBold, sansserif_bold_file, medium_size, sSSBoldFallback); - - return !failed; + // Force standard fonts to get generated up front. + // This is primarily for error detection purposes. + succ &= (NULL != getFontSansSerifSmall()); + succ &= (NULL != getFontSansSerif()); + succ &= (NULL != getFontSansSerifBig()); + succ &= (NULL != getFontSansSerifHuge()); + succ &= (NULL != getFontSansSerifBold()); + succ &= (NULL != getFontMonospace()); + succ &= (NULL != getFontExtChar()); + + return succ; } @@ -464,57 +255,23 @@ BOOL LLFontGL::initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale, // 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; + // Remove the actual fonts. + delete sFontRegistry; + sFontRegistry = NULL; } //static -void LLFontGL::destroyGL() +void LLFontGL::destroyAllGL() { - if (!sMonospace) + if (sFontRegistry) { - // Already all destroyed. - return; + sFontRegistry->destroyGL(); } - sMonospace->mImageGLp->destroyGLTexture(); - sSansSerifHuge->mImageGLp->destroyGLTexture(); - sSansSerifSmall->mImageGLp->destroyGLTexture(); - sSansSerif->mImageGLp->destroyGLTexture(); - sSansSerifBig->mImageGLp->destroyGLTexture(); - sSansSerifBold->mImageGLp->destroyGLTexture(); +} + +void LLFontGL::destroyGL() +{ + mFontBitmapCachep->destroyGL(); } @@ -533,13 +290,58 @@ BOOL LLFontGL::loadFace(const std::string& filename, { return FALSE; } - mImageGLp->createGLTexture(0, mRawImageGLp); - gGL.getTexUnit(0)->bind(mImageGLp); - mImageGLp->setFilteringOption(LLTexUnit::TFO_POINT); return TRUE; } -BOOL LLFontGL::addChar(const llwchar wch) +//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); +} + +BOOL LLFontGL::addChar(const llwchar wch) const { if (!LLFont::addChar(wch)) { @@ -547,10 +349,12 @@ BOOL LLFontGL::addChar(const llwchar wch) } stop_glerror(); - mImageGLp->setSubImage(mRawImageGLp, 0, 0, mImageGLp->getWidth(), mImageGLp->getHeight()); - gGL.getTexUnit(0)->bind(mImageGLp); - mImageGLp->setFilteringOption(LLTexUnit::TFO_POINT); - 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; } @@ -584,30 +388,17 @@ S32 LLFontGL::render(const LLWString &wstr, return wstr.length() ; } - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); - 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); - } - } + // Strip off any style bits that are already accounted for by the font. + style = style & (~getFontDesc().getStyle()); F32 drop_shadow_strength = 0.f; if (style & (DROP_SHADOW | DROP_SHADOW_SOFT)) @@ -649,10 +440,6 @@ S32 LLFontGL::render(const LLWString &wstr, F32 cur_x, cur_y, cur_render_x, cur_render_y; - // Bind the font texture - - gGL.getTexUnit(0)->bind(mImageGLp); - // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); @@ -697,8 +484,8 @@ S32 LLFontGL::render(const LLWString &wstr, F32 start_x = cur_x; - F32 inv_width = 1.f / mImageGLp->getWidth(); - F32 inv_height = 1.f / mImageGLp->getHeight(); + F32 inv_width = 1.f / mFontBitmapCachep->getBitmapWidth(); + F32 inv_height = 1.f / mFontBitmapCachep->getBitmapHeight(); const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; @@ -717,6 +504,9 @@ S32 LLFontGL::render(const LLWString &wstr, } + // Remember last-used texture to avoid unnecesssary bind calls. + LLImageGL *last_bound_texture = NULL; + for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; @@ -736,7 +526,7 @@ S32 LLFontGL::render(const LLWString &wstr, if (!label.empty()) { - ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX; + ext_advance += (EXT_X_BEARING + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) @@ -745,7 +535,12 @@ S32 LLFontGL::render(const LLWString &wstr, break; } - gGL.getTexUnit(0)->bind(ext_image); + 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)); @@ -760,7 +555,7 @@ S32 LLFontGL::render(const LLWString &wstr, //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); - gExtCharFont->render(label, 0, + getFontExtChar()->render(label, 0, /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), /*llfloor*/(cur_y / sScaleY), color, @@ -778,15 +573,12 @@ S32 LLFontGL::render(const LLWString &wstr, cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; - - // Bind the font texture - gGL.getTexUnit(0)->bind(mImageGLp); } else { if (!hasGlyph(wch)) { - (const_cast<LLFontGL*>(this))->addChar(wch); + addChar(wch); } const LLFontGlyphInfo* fgi= getGlyphInfo(wch); @@ -795,6 +587,14 @@ S32 LLFontGL::render(const LLWString &wstr, llerrs << "Missing Glyph Info" << llendl; break; } + // Per-glyph bitmap texture. + LLImageGL *image_gl = mFontBitmapCachep->getImageGL(fgi->mBitmapNum); + if (last_bound_texture != image_gl) + { + gGL.getTexUnit(0)->bind(image_gl); + last_bound_texture = image_gl; + } + if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. @@ -825,7 +625,7 @@ S32 LLFontGL::render(const LLWString &wstr, // Kern this puppy. if (!hasGlyph(next_char)) { - (const_cast<LLFontGL*>(this))->addChar(next_char); + addChar(next_char); } cur_x += getXKerning(wch, next_char); } @@ -879,14 +679,11 @@ S32 LLFontGL::render(const LLWString &wstr, gGL.popMatrix(); + gGL.getTexUnit(0)->disable(); + return chars_drawn; } - -LLImageGL *LLFontGL::getImageGL() const -{ - return mImageGLp; -} S32 LLFontGL::getWidth(const std::string& utf8text) const { @@ -1258,7 +1055,7 @@ F32 LLFontGL::getEmbeddedCharAdvance(const embedded_data_t* ext_data) const F32 ext_width = (F32)ext_image->getWidth(); if( !label.empty() ) { - ext_width += (EXT_X_BEARING + gExtCharFont->getWidthF32(label.c_str())) * sScaleX; + ext_width += (EXT_X_BEARING + getFontExtChar()->getWidthF32(label.c_str())) * sScaleX; } return (EXT_X_BEARING * sScaleX) + ext_width; @@ -1271,19 +1068,19 @@ void LLFontGL::clearEmbeddedChars() mEmbeddedChars.clear(); } -void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const std::string& label ) +void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const std::string& label ) const { LLWString wlabel = utf8str_to_wstring(label); addEmbeddedChar(wc, image, wlabel); } -void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& wlabel ) +void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& wlabel ) const { embedded_data_t* ext_data = new embedded_data_t(image, wlabel); mEmbeddedChars[wc] = ext_data; } -void LLFontGL::removeEmbeddedChar( llwchar wc ) +void LLFontGL::removeEmbeddedChar( llwchar wc ) const { embedded_map_t::iterator iter = mEmbeddedChars.find(wc); if (iter != mEmbeddedChars.end()) @@ -1387,68 +1184,9 @@ void LLFontGL::drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, con gGL.end(); } -// static std::string LLFontGL::nameFromFont(const LLFontGL* fontp) { - if (fontp == sSansSerifHuge) - { - return std::string("SansSerifHuge"); - } - else if (fontp == sSansSerifSmall) - { - return std::string("SansSerifSmall"); - } - else if (fontp == sSansSerif) - { - return std::string("SansSerif"); - } - else if (fontp == sSansSerifBig) - { - return std::string("SansSerifBig"); - } - else if (fontp == sSansSerifBold) - { - return std::string("SansSerifBold"); - } - else if (fontp == sMonospace) - { - return std::string("Monospace"); - } - else - { - return std::string(); - } -} - -// static -LLFontGL* LLFontGL::fontFromName(const std::string& font_name) -{ - LLFontGL* gl_font = NULL; - if (font_name == "SansSerifHuge") - { - gl_font = LLFontGL::sSansSerifHuge; - } - else if (font_name == "SansSerifSmall") - { - gl_font = LLFontGL::sSansSerifSmall; - } - else if (font_name == "SansSerif") - { - gl_font = LLFontGL::sSansSerif; - } - else if (font_name == "SansSerifBig") - { - gl_font = LLFontGL::sSansSerifBig; - } - else if (font_name == "SansSerifBold") - { - gl_font = LLFontGL::sSansSerifBold; - } - else if (font_name == "Monospace") - { - gl_font = LLFontGL::sMonospace; - } - return gl_font; + return fontp->getFontDesc().getName(); } // static diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index f8da460de2..6cb1727ff4 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -40,8 +40,16 @@ #include "llcoord.h" #include "llrect.h" +#include "llfontregistry.h" + class LLColor4; +// Key used to request a font. +class LLFontDescriptor; + +// Structure used to store previously requested fonts. +class LLFontRegistry; + class LLFontGL : public LLFont { public: @@ -86,18 +94,13 @@ public: LLFontGL &operator=(const LLFontGL &source); static BOOL initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale, - const std::string& monospace_file, F32 monospace_size, - const std::string& sansserif_file, - const std::string& sansserif_fallback_file, F32 ss_fallback_scale, - F32 small_size, F32 medium_size, F32 large_size, F32 huge_size, - const std::string& sansserif_bold_file, F32 bold_size, - const std::string& app_dir = LLStringUtil::null); + const std::string& app_dir, + const std::vector<std::string>& xui_paths); static void destroyDefaultFonts(); - static void destroyGL(); + static void destroyAllGL(); + void destroyGL(); - static bool loadFaceFallback(LLFontList *fontp, const std::string& fontname, const F32 point_size); - static bool loadFace(LLFontGL *fontp, const std::string& fontname, const F32 point_size, LLFontList *fallback_fontp); /* virtual*/ BOOL loadFace(const std::string& filename, const F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback); @@ -192,12 +195,11 @@ public: LLImageGL *getImageGL() const; - void addEmbeddedChar( llwchar wc, LLImageGL* image, const std::string& label); - void addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& label); - void removeEmbeddedChar( llwchar wc ); + void addEmbeddedChar( llwchar wc, LLImageGL* image, const std::string& label) const; + void addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& label) const; + void removeEmbeddedChar( llwchar wc ) const; static std::string nameFromFont(const LLFontGL* fontp); - static LLFontGL* fontFromName(const std::string& name); static std::string nameFromHAlign(LLFontGL::HAlign align); static LLFontGL::HAlign hAlignFromName(const std::string& name); @@ -228,20 +230,14 @@ public: static BOOL sDisplayFont ; static std::string sAppDir; // For loading fonts - static LLFontGL* sMonospace; // medium - static LLFontList* sMonospaceFallback; - - static LLFontGL* sSansSerifSmall; // small - static LLFontList* sSSSmallFallback; - static LLFontGL* sSansSerif; // medium - static LLFontList* sSSFallback; - static LLFontGL* sSansSerifBig; // large - static LLFontList* sSSBigFallback; - static LLFontGL* sSansSerifHuge; // very large - static LLFontList* sSSHugeFallback; - - static LLFontGL* sSansSerifBold; // medium, bolded - static LLFontList* sSSBoldFallback; + static LLFontGL* getFontMonospace(); + static LLFontGL* getFontSansSerifSmall(); + static LLFontGL* getFontSansSerif(); + static LLFontGL* getFontSansSerifBig(); + static LLFontGL* getFontSansSerifHuge(); + static LLFontGL* getFontSansSerifBold(); + static LLFontGL* getFontExtChar(); + static LLFontGL* getFont(const LLFontDescriptor& desc); static LLColor4 sShadowColor; @@ -249,19 +245,26 @@ public: friend class LLHUDText; protected: - /*virtual*/ BOOL addChar(const llwchar wch); - static std::string getFontPathLocal(); - static std::string getFontPathSystem(); + /*virtual*/ BOOL addChar(const llwchar wch) const; protected: - LLPointer<LLImageRaw> mRawImageGLp; - LLPointer<LLImageGL> mImageGLp; typedef std::map<llwchar,embedded_data_t*> embedded_map_t; - embedded_map_t mEmbeddedChars; + mutable embedded_map_t mEmbeddedChars; + LLFontDescriptor mFontDesc; + + // Registry holds all instantiated fonts. + static LLFontRegistry* sFontRegistry; + public: + static std::string getFontPathLocal(); + static std::string getFontPathSystem(); + static LLCoordFont sCurOrigin; static std::vector<LLCoordFont> sOriginStack; + + const LLFontDescriptor &getFontDesc() const { return mFontDesc; } + void setFontDesc(const LLFontDescriptor& font_desc) { mFontDesc = font_desc; } }; #endif diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp new file mode 100644 index 0000000000..9b5bc5d0af --- /dev/null +++ b/indra/llrender/llfontregistry.cpp @@ -0,0 +1,650 @@ +/** + * @file llfontregistry.cpp + * @author Brad Payne + * @brief Storage for fonts. + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llgl.h" +#include "llfontregistry.h" +#include "llfontgl.h" +#include <boost/tokenizer.hpp> +#include "llcontrol.h" +#include "lldir.h" +#include "llwindow.h" + +extern LLControlGroup gSavedSettings; + +using std::string; +using std::map; + +bool fontDescInitFromXML(LLXMLNodePtr node, LLFontDescriptor& desc); + +LLFontDescriptor::LLFontDescriptor(): + mStyle(0) +{ +} + +LLFontDescriptor::LLFontDescriptor(const std::string& name, + const std::string& size, + const U8 style, + const string_vec_t& file_names): + mName(name), + mSize(size), + mStyle(style), + mFileNames(file_names) +{ +} + +LLFontDescriptor::LLFontDescriptor(const std::string& name, + const std::string& size, + const U8 style): + mName(name), + mSize(size), + mStyle(style) +{ +} + + +bool LLFontDescriptor::operator<(const LLFontDescriptor& b) const +{ + if (mName < b.mName) + return true; + else if (mName > b.mName) + return false; + + if (mStyle < b.mStyle) + return true; + else if (mStyle > b.mStyle) + return false; + + if (mSize < b.mSize) + return true; + else + return false; +} + +static const std::string s_template_string("TEMPLATE"); + +bool LLFontDescriptor::isTemplate() const +{ + return getSize() == s_template_string; +} + +// Look for substring match and remove substring if matched. +bool removeSubString(std::string& str, const std::string& substr) +{ + size_t pos = str.find(substr); + if (pos != string::npos) + { + str.replace(pos,substr.length(),(const char *)NULL, 0); + return true; + } + return false; +} + +// Check for substring match without modifying the source string. +bool findSubString(std::string& str, const std::string& substr) +{ + size_t pos = str.find(substr); + if (pos != string::npos) + { + return true; + } + return false; +} + + +// Normal form is +// - raw name +// - bold, italic style info reflected in both style and font name. +// - other style info removed. +// - size info moved to mSize, defaults to Medium +// For example, +// - "SansSerifHuge" would normalize to { "SansSerif", "Huge", 0 } +// - "SansSerifBold" would normalize to { "SansSerifBold", "Medium", BOLD } +LLFontDescriptor LLFontDescriptor::normalize() const +{ + std::string new_name(mName); + std::string new_size(mSize); + U8 new_style(mStyle); + + // Only care about style to extent it can be picked up by font. + new_style &= (LLFontGL::BOLD | LLFontGL::ITALIC); + + // All these transformations are to support old-style font specifications. + if (removeSubString(new_name,"Small")) + new_size = "Small"; + if (removeSubString(new_name,"Big")) + new_size = "Large"; + if (removeSubString(new_name,"Medium")) + new_size = "Medium"; + if (removeSubString(new_name,"Large")) + new_size = "Large"; + if (removeSubString(new_name,"Huge")) + new_size = "Huge"; + + // HACK - Monospace is the only one we don't remove, so + // name "Monospace" doesn't get taken down to "" + // For other fonts, there's no ambiguity between font name and size specifier. + if (new_size != s_template_string && new_size.empty() && findSubString(new_name,"Monospace")) + new_size = "Monospace"; + if (new_size.empty()) + new_size = "Medium"; + + if (removeSubString(new_name,"Bold")) + new_style |= LLFontGL::BOLD; + + if (removeSubString(new_name,"Italic")) + new_style |= LLFontGL::ITALIC; + + return LLFontDescriptor(new_name,new_size,new_style,getFileNames()); +} + +LLFontRegistry::LLFontRegistry(const string_vec_t& xui_paths) +{ + // Propagate this down from LLUICtrlFactory so LLRender doesn't + // need an upstream dependency on LLUI. + mXUIPaths = xui_paths; + + // This is potentially a slow directory traversal, so we want to + // cache the result. + mUltimateFallbackList = LLWindow::getDynamicFallbackFontList(); +} + +LLFontRegistry::~LLFontRegistry() +{ + clear(); +} + +bool LLFontRegistry::parseFontInfo(const std::string& xml_filename) +{ + bool success = false; // Succeed if we find at least one XUI file + const string_vec_t& xml_paths = mXUIPaths; + for (string_vec_t::const_iterator path_it = xml_paths.begin(); + path_it != xml_paths.end(); + ++path_it) + { + + LLXMLNodePtr root; + std::string full_filename = gDirUtilp->findSkinnedFilename(*path_it, xml_filename); + bool parsed_file = LLXMLNode::parseFile(full_filename, root, NULL); + + if (!parsed_file) + continue; + + if ( root.isNull() || ! root->hasName( "fonts" ) ) + { + llwarns << "Bad font info file: " + << full_filename << llendl; + continue; + } + + std::string root_name; + root->getAttributeString("name",root_name); + if (root->hasName("fonts")) + { + // Expect a collection of children consisting of "font" or "font_size" entries + bool init_succ = initFromXML(root); + success = success || init_succ; + } + } + if (success) + dump(); + + return success; +} + +std::string currentOsName() +{ +#if LL_WINDOWS + return "Windows"; +#elif LL_DARWIN + return "Mac"; +#elif LL_SDL + return "Linux"; +#else + return ""; +#endif +} + +bool fontDescInitFromXML(LLXMLNodePtr node, LLFontDescriptor& desc) +{ + if (node->hasName("font")) + { + std::string attr_name; + if (node->getAttributeString("name",attr_name)) + { + desc.setName(attr_name); + } + + std::string attr_style; + if (node->getAttributeString("font_style",attr_style)) + { + desc.setStyle(LLFontGL::getStyleFromString(attr_style)); + } + + desc.setSize(s_template_string); + } + + LLXMLNodePtr child; + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + std::string child_name; + child->getAttributeString("name",child_name); + if (child->hasName("file")) + { + std::string font_file_name = child->getTextContents(); + desc.getFileNames().push_back(font_file_name); + } + else if (child->hasName("os")) + { + if (child_name == currentOsName()) + { + fontDescInitFromXML(child, desc); + } + } + } + return true; +} + +bool LLFontRegistry::initFromXML(LLXMLNodePtr node) +{ + LLXMLNodePtr child; + + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + std::string child_name; + child->getAttributeString("name",child_name); + if (child->hasName("font")) + { + LLFontDescriptor desc; + bool font_succ = fontDescInitFromXML(child, desc); + LLFontDescriptor norm_desc = desc.normalize(); + if (font_succ) + { + // if this is the first time we've seen this font name, + // create a new template map entry for it. + const LLFontDescriptor *match_desc = getMatchingFontDesc(desc); + if (match_desc == NULL) + { + // Create a new entry (with no corresponding font). + mFontMap[norm_desc] = NULL; + } + // otherwise, find the existing entry and combine data. + else + { + // Prepend files from desc. + // A little roundabout because the map key is const, + // so we have to fetch it, make a new map key, and + // replace the old entry. + string_vec_t match_file_names = match_desc->getFileNames(); + match_file_names.insert(match_file_names.begin(), + desc.getFileNames().begin(), + desc.getFileNames().end()); + LLFontDescriptor new_desc = *match_desc; + new_desc.getFileNames() = match_file_names; + mFontMap.erase(*match_desc); + mFontMap[new_desc] = NULL; + } + } + } + else if (child->hasName("font_size")) + { + std::string size_name; + F32 size_value; + if (child->getAttributeString("name",size_name) && + child->getAttributeF32("size",size_value)) + { + mFontSizes[size_name] = size_value; + } + + } + } + return true; +} + +bool LLFontRegistry::nameToSize(const std::string& size_name, F32& size) +{ + font_size_map_t::iterator it = mFontSizes.find(size_name); + if (it != mFontSizes.end()) + { + size = it->second; + return true; + } + return false; +} + + +LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) +{ + // Name should hold a font name recognized as a setting; the value + // of the setting should be a list of font files. + // Size should be a recognized string value + // Style should be a set of flags including any implied by the font name. + + // First decipher the requested size. + LLFontDescriptor norm_desc = desc.normalize(); + F32 point_size; + bool found_size = nameToSize(norm_desc.getSize(),point_size); + if (!found_size) + { + llwarns << "createFont unrecognized size " << norm_desc.getSize() << llendl; + return NULL; + } + llinfos << "createFont " << norm_desc.getName() << " size " << norm_desc.getSize() << " style " << ((S32) norm_desc.getStyle()) << llendl; + F32 fallback_scale = 1.0; + + // Find corresponding font template (based on same descriptor with no size specified) + LLFontDescriptor template_desc(norm_desc); + template_desc.setSize(s_template_string); + const LLFontDescriptor *match_desc = getClosestFontTemplate(template_desc); + if (!match_desc) + { + llwarns << "createFont failed, no template found for " + << norm_desc.getName() << " style [" << ((S32)norm_desc.getStyle()) << "]" << llendl; + return NULL; + } + + // See whether this best-match font has already been instantiated in the requested size. + LLFontDescriptor nearest_exact_desc = *match_desc; + nearest_exact_desc.setSize(norm_desc.getSize()); + font_reg_map_t::iterator it = mFontMap.find(nearest_exact_desc); + if (it != mFontMap.end()) + { + llinfos << "-- matching font exists: " << nearest_exact_desc.getName() << " size " << nearest_exact_desc.getSize() << " style " << ((S32) nearest_exact_desc.getStyle()) << llendl; + return it->second; + } + + // Build list of font names to look for. + // Files specified for this font come first, followed by those from the default descriptor. + string_vec_t file_names = match_desc->getFileNames(); + string_vec_t default_file_names; + LLFontDescriptor default_desc("default",s_template_string,0); + const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc); + if (match_default_desc) + { + file_names.insert(file_names.end(), + match_default_desc->getFileNames().begin(), + match_default_desc->getFileNames().end()); + } + + // Add ultimate fallback list - generated dynamically on linux, + // null elsewhere. + file_names.insert(file_names.end(), + getUltimateFallbackList().begin(), + getUltimateFallbackList().end()); + + // Load fonts based on names. + if (file_names.empty()) + { + llwarns << "createFont failed, no file names specified" << llendl; + return NULL; + } + LLFontList *fontlistp = new LLFontList; + LLFontGL *result = NULL; + + // Snarf all fonts we can into fontlistp. First will get pulled + // off the list and become the "head" font, set to non-fallback. + // Rest will consitute the fallback list. + BOOL is_first_found = TRUE; + + std::string local_path = LLFontGL::getFontPathLocal(); + std::string sys_path = LLFontGL::getFontPathSystem(); + + // The fontname string may contain multiple font file names separated by semicolons. + // Break it apart and try loading each one, in order. + for(string_vec_t::iterator file_name_it = file_names.begin(); + file_name_it != file_names.end(); + ++file_name_it) + { + LLFontGL *fontp = new LLFontGL; + std::string font_path = local_path + *file_name_it; + BOOL is_fallback = !is_first_found; + F32 extra_scale = (is_fallback)?fallback_scale:1.0; + if (!fontp->loadFace(font_path, extra_scale * point_size, + LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback)) + { + font_path = sys_path + *file_name_it; + + if (!fontp->loadFace(font_path, extra_scale * point_size, + LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback)) + { + LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << *file_name_it << LL_ENDL; + delete fontp; + fontp = NULL; + } + } + + if(fontp) + { + if (is_first_found) + { + result = fontp; + is_first_found = false; + } + else + fontlistp->addAtEnd(fontp); + } + } + if (result && !fontlistp->empty()) + { + result->setFallbackFont(fontlistp); + } + + norm_desc.setStyle(match_desc->getStyle()); + if (result) + result->setFontDesc(norm_desc); + + if (!result) + { + llwarns << "createFont failed in some way" << llendl; + } + mFontMap[norm_desc] = result; + return result; +} + +void LLFontRegistry::reset() +{ + for (font_reg_map_t::iterator it = mFontMap.begin(); + it != mFontMap.end(); + ++it) + { + // Reset the corresponding font but preserve the entry. + if (it->second) + it->second->reset(); + } +} + +void LLFontRegistry::clear() +{ + for (font_reg_map_t::iterator it = mFontMap.begin(); + it != mFontMap.end(); + ++it) + { + LLFontGL *fontp = it->second; + delete fontp; + } + mFontMap.clear(); +} + +void LLFontRegistry::destroyGL() +{ + for (font_reg_map_t::iterator it = mFontMap.begin(); + it != mFontMap.end(); + ++it) + { + // Reset the corresponding font but preserve the entry. + if (it->second) + it->second->destroyGL(); + } +} + +LLFontGL *LLFontRegistry::getFont(const LLFontDescriptor& orig_desc) +{ + LLFontDescriptor norm_desc = orig_desc.normalize(); + + font_reg_map_t::iterator it = mFontMap.find(norm_desc); + if (it != mFontMap.end()) + return it->second; + else + { + LLFontGL *fontp = createFont(orig_desc); + if (!fontp) + { + llwarns << "getFont failed, name " << orig_desc.getName() + <<" style=[" << ((S32) orig_desc.getStyle()) << "]" + << " size=[" << orig_desc.getSize() << "]" << llendl; + } + return fontp; + } +} + +const LLFontDescriptor *LLFontRegistry::getMatchingFontDesc(const LLFontDescriptor& desc) +{ + LLFontDescriptor norm_desc = desc.normalize(); + + font_reg_map_t::iterator it = mFontMap.find(norm_desc); + if (it != mFontMap.end()) + return &(it->first); + else + return NULL; +} + +static U32 bitCount(U8 c) +{ + U32 count = 0; + if (c & 1) + count++; + if (c & 2) + count++; + if (c & 4) + count++; + if (c & 8) + count++; + if (c & 16) + count++; + if (c & 32) + count++; + if (c & 64) + count++; + if (c & 128) + count++; + return count; +} + +// Find nearest match for the requested descriptor. +const LLFontDescriptor *LLFontRegistry::getClosestFontTemplate(const LLFontDescriptor& desc) +{ + const LLFontDescriptor *exact_match_desc = getMatchingFontDesc(desc); + if (exact_match_desc) + { + return exact_match_desc; + } + + LLFontDescriptor norm_desc = desc.normalize(); + + const LLFontDescriptor *best_match_desc = NULL; + for (font_reg_map_t::iterator it = mFontMap.begin(); + it != mFontMap.end(); + ++it) + { + const LLFontDescriptor* curr_desc = &(it->first); + + // Ignore if not a template. + if (!curr_desc->isTemplate()) + continue; + + // Ignore if font name is wrong. + if (curr_desc->getName() != norm_desc.getName()) + continue; + + // Reject font if it matches any bits we don't want + if (curr_desc->getStyle() & ~norm_desc.getStyle()) + { + continue; + } + + // Take if it's the first plausible candidate we've found. + if (!best_match_desc) + { + best_match_desc = curr_desc; + continue; + } + + // Take if it matches more bits than anything before. + U8 best_style_match_bits = + norm_desc.getStyle() & best_match_desc->getStyle(); + U8 curr_style_match_bits = + norm_desc.getStyle() & curr_desc->getStyle(); + if (bitCount(curr_style_match_bits) > bitCount(best_style_match_bits)) + { + best_match_desc = curr_desc; + continue; + } + + // Tie-breaker: take if it matches bold. + if (curr_style_match_bits & LLFontGL::BOLD) // Bold is requested and this descriptor matches it. + { + best_match_desc = curr_desc; + continue; + } + } + + // Nothing matched. + return best_match_desc; +} + +void LLFontRegistry::dump() +{ + llinfos << "LLFontRegistry dump: " << llendl; + for (font_size_map_t::iterator size_it = mFontSizes.begin(); + size_it != mFontSizes.end(); + ++size_it) + { + llinfos << "Size: " << size_it->first << " => " << size_it->second << llendl; + } + for (font_reg_map_t::iterator font_it = mFontMap.begin(); + font_it != mFontMap.end(); + ++font_it) + { + const LLFontDescriptor& desc = font_it->first; + llinfos << "Font: name=" << desc.getName() + << " style=[" << ((S32)desc.getStyle()) << "]" + << " size=[" << desc.getSize() << "]" + << " fileNames=" + << llendl; + for (string_vec_t::const_iterator file_it=desc.getFileNames().begin(); + file_it != desc.getFileNames().end(); + ++file_it) + { + llinfos << " file: " << *file_it <<llendl; + } + } +} diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h new file mode 100644 index 0000000000..ed775eeed0 --- /dev/null +++ b/indra/llrender/llfontregistry.h @@ -0,0 +1,113 @@ +/** + * @file llfontregistry.h + * @author Brad Payne + * @brief Storage for fonts. + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008, Linden Research, Inc. + * + * 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 + * + * 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 + * + * 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. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLFONTREGISTRY_H +#define LL_LLFONTREGISTRY_H + +#include "llxmlnode.h" + +class LLFontGL; + +typedef std::vector<std::string> string_vec_t; + +class LLFontDescriptor +{ +public: + LLFontDescriptor(); + LLFontDescriptor(const std::string& name, const std::string& size, const U8 style); + LLFontDescriptor(const std::string& name, const std::string& size, const U8 style, const string_vec_t& file_names); + LLFontDescriptor normalize() const; + + bool operator<(const LLFontDescriptor& b) const; + + bool isTemplate() const; + + const std::string& getName() const { return mName; } + void setName(const std::string& name) { mName = name; } + const std::string& getSize() const { return mSize; } + void setSize(const std::string& size) { mSize = size; } + const std::vector<std::string>& getFileNames() const { return mFileNames; } + std::vector<std::string>& getFileNames() { return mFileNames; } + const U8 getStyle() const { return mStyle; } + void setStyle(U8 style) { mStyle = style; } + +private: + std::string mName; + std::string mSize; + string_vec_t mFileNames; + U8 mStyle; +}; + +class LLFontRegistry +{ +public: + LLFontRegistry(const string_vec_t& xui_paths); + ~LLFontRegistry(); + + // Load standard font info from XML file(s). + bool parseFontInfo(const std::string& xml_filename); + bool initFromXML(LLXMLNodePtr node); + + // Clear cached glyphs for all fonts. + void reset(); + + // Destroy all fonts. + void clear(); + + // GL cleanup + void destroyGL(); + + LLFontGL *getFont(const LLFontDescriptor& desc); + const LLFontDescriptor *getMatchingFontDesc(const LLFontDescriptor& desc); + const LLFontDescriptor *getClosestFontTemplate(const LLFontDescriptor& desc); + + bool nameToSize(const std::string& size_name, F32& size); + + void dump(); + + const string_vec_t& getUltimateFallbackList() const { return mUltimateFallbackList; } + +private: + LLFontGL *createFont(const LLFontDescriptor& desc); + typedef std::map<LLFontDescriptor,LLFontGL*> font_reg_map_t; + typedef std::map<std::string,F32> font_size_map_t; + + // Given a descriptor, look up specific font instantiation. + font_reg_map_t mFontMap; + // Given a size name, look up the point size. + font_size_map_t mFontSizes; + + string_vec_t mUltimateFallbackList; + string_vec_t mXUIPaths; +}; + +#endif // LL_LLFONTREGISTRY_H diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index c784019cd9..cdf626e16f 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1373,6 +1373,9 @@ void LLImageGL::analyzeAlpha(const void* data_in, S32 w, S32 h) case GL_RGBA: stride = 4; break; + case GL_BGRA_EXT: + stride = 4; + break; default: llwarns << "Cannot analyze alpha of image with primary format " << std::hex << mFormatPrimary << std::dec << llendl; return; |