llfontregistry.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:18k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llfontregistry.cpp
  3.  * @author Brad Payne
  4.  * @brief Storage for fonts.
  5.  *
  6.  * $LicenseInfo:firstyear=2008&license=viewergpl$
  7.  * 
  8.  * Copyright (c) 2008-2010, Linden Research, Inc.
  9.  * 
  10.  * Second Life Viewer Source Code
  11.  * The source code in this file ("Source Code") is provided by Linden Lab
  12.  * to you under the terms of the GNU General Public License, version 2.0
  13.  * ("GPL"), unless you have obtained a separate licensing agreement
  14.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  15.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17.  * 
  18.  * There are special exceptions to the terms and conditions of the GPL as
  19.  * it is applied to this Source Code. View the full text of the exception
  20.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  21.  * online at
  22.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23.  * 
  24.  * By copying, modifying or distributing this software, you acknowledge
  25.  * that you have read and understood your obligations described above,
  26.  * and agree to abide by those obligations.
  27.  * 
  28.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30.  * COMPLETENESS OR PERFORMANCE.
  31.  * $/LicenseInfo$
  32.  */
  33. #include "linden_common.h"
  34. #include "llgl.h"
  35. #include "llfontfreetype.h"
  36. #include "llfontgl.h"
  37. #include "llfontregistry.h"
  38. #include <boost/tokenizer.hpp>
  39. #include "llcontrol.h"
  40. #include "lldir.h"
  41. #include "llwindow.h"
  42. extern LLControlGroup gSavedSettings;
  43. using std::string;
  44. using std::map;
  45. bool fontDescInitFromXML(LLXMLNodePtr node, LLFontDescriptor& desc);
  46. LLFontDescriptor::LLFontDescriptor():
  47. mStyle(0)
  48. {
  49. }
  50. LLFontDescriptor::LLFontDescriptor(const std::string& name,
  51.    const std::string& size, 
  52.    const U8 style,
  53.    const string_vec_t& file_names):
  54. mName(name),
  55. mSize(size),
  56. mStyle(style),
  57. mFileNames(file_names)
  58. {
  59. }
  60. LLFontDescriptor::LLFontDescriptor(const std::string& name,
  61.    const std::string& size, 
  62.    const U8 style):
  63. mName(name),
  64. mSize(size),
  65. mStyle(style)
  66. {
  67. }
  68. bool LLFontDescriptor::operator<(const LLFontDescriptor& b) const
  69. {
  70. if (mName < b.mName)
  71. return true;
  72. else if (mName > b.mName)
  73. return false;
  74. if (mStyle < b.mStyle)
  75. return true;
  76. else if (mStyle > b.mStyle)
  77. return false;
  78. if (mSize < b.mSize)
  79. return true;
  80. else 
  81. return false;
  82. }
  83. static const std::string s_template_string("TEMPLATE");
  84. bool LLFontDescriptor::isTemplate() const
  85. {
  86. return getSize() == s_template_string;
  87. }
  88. // Look for substring match and remove substring if matched.
  89. bool removeSubString(std::string& str, const std::string& substr)
  90. {
  91. size_t pos = str.find(substr);
  92. if (pos != string::npos)
  93. {
  94. str.erase(pos, substr.size());
  95. return true;
  96. }
  97. return false;
  98. }
  99. // Check for substring match without modifying the source string.
  100. bool findSubString(std::string& str, const std::string& substr)
  101. {
  102. size_t pos = str.find(substr);
  103. if (pos != string::npos)
  104. {
  105. return true;
  106. }
  107. return false;
  108. }
  109. // Normal form is
  110. // - raw name
  111. // - bold, italic style info reflected in both style and font name.
  112. // - other style info removed.
  113. // - size info moved to mSize, defaults to Medium
  114. // For example,
  115. // - "SansSerifHuge" would normalize to { "SansSerif", "Huge", 0 }
  116. // - "SansSerifBold" would normalize to { "SansSerifBold", "Medium", BOLD }
  117. LLFontDescriptor LLFontDescriptor::normalize() const
  118. {
  119. std::string new_name(mName);
  120. std::string new_size(mSize);
  121. U8 new_style(mStyle);
  122. // Only care about style to extent it can be picked up by font.
  123. new_style &= (LLFontGL::BOLD | LLFontGL::ITALIC);
  124. // All these transformations are to support old-style font specifications.
  125. if (removeSubString(new_name,"Small"))
  126. new_size = "Small";
  127. if (removeSubString(new_name,"Big"))
  128. new_size = "Large";
  129. if (removeSubString(new_name,"Medium"))
  130. new_size = "Medium";
  131. if (removeSubString(new_name,"Large"))
  132. new_size = "Large";
  133. if (removeSubString(new_name,"Huge"))
  134. new_size = "Huge";
  135. // HACK - Monospace is the only one we don't remove, so
  136. // name "Monospace" doesn't get taken down to ""
  137. // For other fonts, there's no ambiguity between font name and size specifier.
  138. if (new_size != s_template_string && new_size.empty() && findSubString(new_name,"Monospace"))
  139. new_size = "Monospace";
  140. if (new_size.empty())
  141. new_size = "Medium";
  142. if (removeSubString(new_name,"Bold"))
  143. new_style |= LLFontGL::BOLD;
  144. if (removeSubString(new_name,"Italic"))
  145. new_style |= LLFontGL::ITALIC;
  146. return LLFontDescriptor(new_name,new_size,new_style,getFileNames());
  147. }
  148. LLFontRegistry::LLFontRegistry(const string_vec_t& xui_paths,
  149.    bool create_gl_textures)
  150. : mCreateGLTextures(create_gl_textures)
  151. {
  152. // Propagate this down from LLUICtrlFactory so LLRender doesn't
  153. // need an upstream dependency on LLUI.
  154. mXUIPaths = xui_paths;
  155. // This is potentially a slow directory traversal, so we want to
  156. // cache the result.
  157. mUltimateFallbackList = LLWindow::getDynamicFallbackFontList();
  158. }
  159. LLFontRegistry::~LLFontRegistry()
  160. {
  161. clear();
  162. }
  163. bool LLFontRegistry::parseFontInfo(const std::string& xml_filename)
  164. {
  165. bool success = false;  // Succeed if we find at least one XUI file
  166. const string_vec_t& xml_paths = mXUIPaths;
  167. for (string_vec_t::const_iterator path_it = xml_paths.begin();
  168.  path_it != xml_paths.end();
  169.  ++path_it)
  170. {
  171. LLXMLNodePtr root;
  172. std::string full_filename = gDirUtilp->findSkinnedFilename(*path_it, xml_filename);
  173. bool parsed_file = LLXMLNode::parseFile(full_filename, root, NULL);
  174. if (!parsed_file)
  175. continue;
  176. if ( root.isNull() || ! root->hasName( "fonts" ) )
  177. {
  178. llwarns << "Bad font info file: "
  179. << full_filename << llendl;
  180. continue;
  181. }
  182. std::string root_name;
  183. root->getAttributeString("name",root_name);
  184. if (root->hasName("fonts"))
  185. {
  186. // Expect a collection of children consisting of "font" or "font_size" entries
  187. bool init_succ = initFromXML(root);
  188. success = success || init_succ;
  189. }
  190. }
  191. //if (success)
  192. // dump();
  193. return success;
  194. }
  195. std::string currentOsName()
  196. {
  197. #if LL_WINDOWS
  198. return "Windows";
  199. #elif LL_DARWIN
  200. return "Mac";
  201. #elif LL_SDL
  202. return "Linux";
  203. #else
  204. return "";
  205. #endif
  206. }
  207. bool fontDescInitFromXML(LLXMLNodePtr node, LLFontDescriptor& desc)
  208. {
  209. if (node->hasName("font"))
  210. {
  211. std::string attr_name;
  212. if (node->getAttributeString("name",attr_name))
  213. {
  214. desc.setName(attr_name);
  215. }
  216. std::string attr_style;
  217. if (node->getAttributeString("font_style",attr_style))
  218. {
  219. desc.setStyle(LLFontGL::getStyleFromString(attr_style));
  220. }
  221. desc.setSize(s_template_string);
  222. }
  223. LLXMLNodePtr child;
  224. for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
  225. {
  226. std::string child_name;
  227. child->getAttributeString("name",child_name);
  228. if (child->hasName("file"))
  229. {
  230. std::string font_file_name = child->getTextContents();
  231. desc.getFileNames().push_back(font_file_name);
  232. }
  233. else if (child->hasName("os"))
  234. {
  235. if (child_name == currentOsName())
  236. {
  237. fontDescInitFromXML(child, desc);
  238. }
  239. }
  240. }
  241. return true;
  242. }
  243. bool LLFontRegistry::initFromXML(LLXMLNodePtr node)
  244. {
  245. LLXMLNodePtr child;
  246. for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
  247. {
  248. std::string child_name;
  249. child->getAttributeString("name",child_name);
  250. if (child->hasName("font"))
  251. {
  252. LLFontDescriptor desc;
  253. bool font_succ = fontDescInitFromXML(child, desc);
  254. LLFontDescriptor norm_desc = desc.normalize();
  255. if (font_succ)
  256. {
  257. // if this is the first time we've seen this font name,
  258. // create a new template map entry for it.
  259. const LLFontDescriptor *match_desc = getMatchingFontDesc(desc);
  260. if (match_desc == NULL)
  261. {
  262. // Create a new entry (with no corresponding font).
  263. mFontMap[norm_desc] = NULL;
  264. }
  265. // otherwise, find the existing entry and combine data. 
  266. else
  267. {
  268. // Prepend files from desc.
  269. // A little roundabout because the map key is const,
  270. // so we have to fetch it, make a new map key, and
  271. // replace the old entry.
  272. string_vec_t match_file_names = match_desc->getFileNames();
  273. match_file_names.insert(match_file_names.begin(),
  274. desc.getFileNames().begin(),
  275. desc.getFileNames().end());
  276. LLFontDescriptor new_desc = *match_desc;
  277. new_desc.getFileNames() = match_file_names;
  278. mFontMap.erase(*match_desc);
  279. mFontMap[new_desc] = NULL;
  280. }
  281. }
  282. }
  283. else if (child->hasName("font_size"))
  284. {
  285. std::string size_name;
  286. F32 size_value;
  287. if (child->getAttributeString("name",size_name) &&
  288. child->getAttributeF32("size",size_value))
  289. {
  290. mFontSizes[size_name] = size_value;
  291. }
  292. }
  293. }
  294. return true;
  295. }
  296. bool LLFontRegistry::nameToSize(const std::string& size_name, F32& size)
  297. {
  298. font_size_map_t::iterator it = mFontSizes.find(size_name);
  299. if (it != mFontSizes.end())
  300. {
  301. size = it->second;
  302. return true;
  303. }
  304. return false;
  305. }
  306. LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc)
  307. {
  308. // Name should hold a font name recognized as a setting; the value
  309. // of the setting should be a list of font files.
  310. // Size should be a recognized string value
  311. // Style should be a set of flags including any implied by the font name.
  312. // First decipher the requested size.
  313. LLFontDescriptor norm_desc = desc.normalize();
  314. F32 point_size;
  315. bool found_size = nameToSize(norm_desc.getSize(),point_size);
  316. if (!found_size)
  317. {
  318. llwarns << "createFont unrecognized size " << norm_desc.getSize() << llendl;
  319. return NULL;
  320. }
  321. llinfos << "createFont " << norm_desc.getName() << " size " << norm_desc.getSize() << " style " << ((S32) norm_desc.getStyle()) << llendl;
  322. F32 fallback_scale = 1.0;
  323. // Find corresponding font template (based on same descriptor with no size specified)
  324. LLFontDescriptor template_desc(norm_desc);
  325. template_desc.setSize(s_template_string);
  326. const LLFontDescriptor *match_desc = getClosestFontTemplate(template_desc);
  327. if (!match_desc)
  328. {
  329. llwarns << "createFont failed, no template found for "
  330. << norm_desc.getName() << " style [" << ((S32)norm_desc.getStyle()) << "]" << llendl;
  331. return NULL;
  332. }
  333. // See whether this best-match font has already been instantiated in the requested size.
  334. LLFontDescriptor nearest_exact_desc = *match_desc;
  335. nearest_exact_desc.setSize(norm_desc.getSize());
  336. font_reg_map_t::iterator it = mFontMap.find(nearest_exact_desc);
  337. // If we fail to find a font in the fonts directory, it->second might be NULL.
  338. // We shouldn't construcnt a font with a NULL mFontFreetype.
  339. // This may not be the best solution, but it at least prevents a crash.
  340. if (it != mFontMap.end() && it->second != NULL)
  341. {
  342. llinfos << "-- matching font exists: " << nearest_exact_desc.getName() << " size " << nearest_exact_desc.getSize() << " style " << ((S32) nearest_exact_desc.getStyle()) << llendl;
  343. // copying underlying Freetype font, and storing in LLFontGL with requested font descriptor
  344. LLFontGL *font = new LLFontGL;
  345. font->mFontDescriptor = desc;
  346. font->mFontFreetype = it->second->mFontFreetype;
  347. mFontMap[desc] = font;
  348. return font;
  349. }
  350. // Build list of font names to look for.
  351. // Files specified for this font come first, followed by those from the default descriptor.
  352. string_vec_t file_names = match_desc->getFileNames();
  353. string_vec_t default_file_names;
  354. LLFontDescriptor default_desc("default",s_template_string,0);
  355. const LLFontDescriptor *match_default_desc = getMatchingFontDesc(default_desc);
  356. if (match_default_desc)
  357. {
  358. file_names.insert(file_names.end(),
  359.   match_default_desc->getFileNames().begin(),
  360.   match_default_desc->getFileNames().end());
  361. }
  362. // Add ultimate fallback list - generated dynamically on linux,
  363. // null elsewhere.
  364. file_names.insert(file_names.end(),
  365.   getUltimateFallbackList().begin(),
  366.   getUltimateFallbackList().end());
  367. // Load fonts based on names.
  368. if (file_names.empty())
  369. {
  370. llwarns << "createFont failed, no file names specified" << llendl;
  371. return NULL;
  372. }
  373. LLFontFreetype::font_vector_t fontlist;
  374. LLFontGL *result = NULL;
  375. // Snarf all fonts we can into fontlist.  First will get pulled
  376. // off the list and become the "head" font, set to non-fallback.
  377. // Rest will consitute the fallback list.
  378. BOOL is_first_found = TRUE;
  379. std::string local_path = LLFontGL::getFontPathLocal();
  380. std::string sys_path = LLFontGL::getFontPathSystem();
  381. // The fontname string may contain multiple font file names separated by semicolons.
  382. // Break it apart and try loading each one, in order.
  383. for(string_vec_t::iterator file_name_it = file_names.begin();
  384. file_name_it != file_names.end(); 
  385. ++file_name_it)
  386. {
  387. LLFontGL *fontp = new LLFontGL;
  388. std::string font_path = local_path + *file_name_it;
  389. // *HACK: Fallback fonts don't render, so we can use that to suppress
  390. // creation of OpenGL textures for test apps. JC
  391. BOOL is_fallback = !is_first_found || !mCreateGLTextures;
  392. F32 extra_scale = (is_fallback)?fallback_scale:1.0;
  393. if (!fontp->loadFace(font_path, extra_scale * point_size,
  394.  LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback))
  395. {
  396. font_path = sys_path + *file_name_it;
  397. if (!fontp->loadFace(font_path, extra_scale * point_size,
  398.  LLFontGL::sVertDPI, LLFontGL::sHorizDPI, 2, is_fallback))
  399. {
  400. LL_INFOS_ONCE("LLFontRegistry") << "Couldn't load font " << *file_name_it << LL_ENDL;
  401. delete fontp;
  402. fontp = NULL;
  403. }
  404. }
  405. if(fontp)
  406. {
  407. if (is_first_found)
  408. {
  409. result = fontp;
  410. is_first_found = false;
  411. }
  412. else
  413. {
  414. fontlist.push_back(fontp->mFontFreetype);
  415. }
  416. }
  417. }
  418. if (result && !fontlist.empty())
  419. {
  420. result->mFontFreetype->setFallbackFonts(fontlist);
  421. }
  422. if (result)
  423. {
  424. result->mFontDescriptor = desc;
  425. }
  426. else
  427. {
  428. llwarns << "createFont failed in some way" << llendl;
  429. }
  430. mFontMap[desc] = result;
  431. return result;
  432. }
  433. void LLFontRegistry::reset()
  434. {
  435. for (font_reg_map_t::iterator it = mFontMap.begin();
  436.  it != mFontMap.end();
  437.  ++it)
  438. {
  439. // Reset the corresponding font but preserve the entry.
  440. if (it->second)
  441. it->second->reset();
  442. }
  443. }
  444. void LLFontRegistry::clear()
  445. {
  446. for (font_reg_map_t::iterator it = mFontMap.begin();
  447.  it != mFontMap.end();
  448.  ++it)
  449. {
  450. LLFontGL *fontp = it->second;
  451. delete fontp;
  452. }
  453. mFontMap.clear();
  454. }
  455. void LLFontRegistry::destroyGL()
  456. {
  457. for (font_reg_map_t::iterator it = mFontMap.begin();
  458.  it != mFontMap.end();
  459.  ++it)
  460. {
  461. // Reset the corresponding font but preserve the entry.
  462. if (it->second)
  463. it->second->destroyGL();
  464. }
  465. }
  466. LLFontGL *LLFontRegistry::getFont(const LLFontDescriptor& desc)
  467. {
  468. font_reg_map_t::iterator it = mFontMap.find(desc);
  469. if (it != mFontMap.end())
  470. return it->second;
  471. else
  472. {
  473. LLFontGL *fontp = createFont(desc);
  474. if (!fontp)
  475. {
  476. llwarns << "getFont failed, name " << desc.getName()
  477. <<" style=[" << ((S32) desc.getStyle()) << "]"
  478. << " size=[" << desc.getSize() << "]" << llendl;
  479. }
  480. return fontp;
  481. }
  482. }
  483. const LLFontDescriptor *LLFontRegistry::getMatchingFontDesc(const LLFontDescriptor& desc)
  484. {
  485. LLFontDescriptor norm_desc = desc.normalize();
  486. font_reg_map_t::iterator it = mFontMap.find(norm_desc);
  487. if (it != mFontMap.end())
  488. return &(it->first);
  489. else
  490. return NULL;
  491. }
  492. static U32 bitCount(U8 c)
  493. {
  494. U32 count = 0;
  495. if (c & 1) 
  496. count++;
  497. if (c & 2)
  498. count++;
  499. if (c & 4)
  500. count++;
  501. if (c & 8)
  502. count++;
  503. if (c & 16)
  504. count++;
  505. if (c & 32)
  506. count++;
  507. if (c & 64)
  508. count++;
  509. if (c & 128)
  510. count++;
  511. return count;
  512. }
  513. // Find nearest match for the requested descriptor.
  514. const LLFontDescriptor *LLFontRegistry::getClosestFontTemplate(const LLFontDescriptor& desc)
  515. {
  516. const LLFontDescriptor *exact_match_desc = getMatchingFontDesc(desc);
  517. if (exact_match_desc)
  518. {
  519. return exact_match_desc;
  520. }
  521. LLFontDescriptor norm_desc = desc.normalize();
  522. const LLFontDescriptor *best_match_desc = NULL;
  523. for (font_reg_map_t::iterator it = mFontMap.begin();
  524.  it != mFontMap.end();
  525.  ++it)
  526. {
  527. const LLFontDescriptor* curr_desc = &(it->first);
  528. // Ignore if not a template.
  529. if (!curr_desc->isTemplate())
  530. continue;
  531. // Ignore if font name is wrong.
  532. if (curr_desc->getName() != norm_desc.getName())
  533. continue;
  534. // Reject font if it matches any bits we don't want
  535. if (curr_desc->getStyle() & ~norm_desc.getStyle())
  536. {
  537. continue;
  538. }
  539. // Take if it's the first plausible candidate we've found.
  540. if (!best_match_desc)
  541. {
  542. best_match_desc = curr_desc;
  543. continue;
  544. }
  545. // Take if it matches more bits than anything before.
  546. U8 best_style_match_bits =
  547. norm_desc.getStyle() & best_match_desc->getStyle();
  548. U8 curr_style_match_bits =
  549. norm_desc.getStyle() & curr_desc->getStyle();
  550. if (bitCount(curr_style_match_bits) > bitCount(best_style_match_bits))
  551. {
  552. best_match_desc = curr_desc;
  553. continue;
  554. }
  555. // Tie-breaker: take if it matches bold.
  556. if (curr_style_match_bits & LLFontGL::BOLD)  // Bold is requested and this descriptor matches it.
  557. {
  558. best_match_desc = curr_desc;
  559. continue;
  560. }
  561. }
  562. // Nothing matched.
  563. return best_match_desc;
  564. }
  565. void LLFontRegistry::dump()
  566. {
  567. llinfos << "LLFontRegistry dump: " << llendl;
  568. for (font_size_map_t::iterator size_it = mFontSizes.begin();
  569.  size_it != mFontSizes.end();
  570.  ++size_it)
  571. {
  572. llinfos << "Size: " << size_it->first << " => " << size_it->second << llendl;
  573. }
  574. for (font_reg_map_t::iterator font_it = mFontMap.begin();
  575.  font_it != mFontMap.end();
  576.  ++font_it)
  577. {
  578. const LLFontDescriptor& desc = font_it->first;
  579. llinfos << "Font: name=" << desc.getName()
  580. << " style=[" << ((S32)desc.getStyle()) << "]"
  581. << " size=[" << desc.getSize() << "]"
  582. << " fileNames="
  583. << llendl;
  584. for (string_vec_t::const_iterator file_it=desc.getFileNames().begin();
  585.  file_it != desc.getFileNames().end();
  586.  ++file_it)
  587. {
  588. llinfos << "  file: " << *file_it <<llendl;
  589. }
  590. }
  591. }
  592. const string_vec_t& LLFontRegistry::getUltimateFallbackList() const 
  593. return mUltimateFallbackList;
  594. }