TMP_SpriteAsset.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. using UnityEngine;
  2. using UnityEngine.TextCore;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. namespace TMPro
  6. {
  7. [ExcludeFromPresetAttribute]
  8. public class TMP_SpriteAsset : TMP_Asset
  9. {
  10. internal Dictionary<int, int> m_NameLookup;
  11. internal Dictionary<uint, int> m_GlyphIndexLookup;
  12. /// <summary>
  13. /// The version of the sprite asset class.
  14. /// Version 1.1.0 updates the asset data structure to be compatible with new font asset structure.
  15. /// </summary>
  16. public string version
  17. {
  18. get { return m_Version; }
  19. internal set { m_Version = value; }
  20. }
  21. [SerializeField]
  22. private string m_Version;
  23. /// <summary>
  24. /// Information about the sprite asset's face.
  25. /// </summary>
  26. public FaceInfo faceInfo
  27. {
  28. get { return m_FaceInfo; }
  29. internal set { m_FaceInfo = value; }
  30. }
  31. [SerializeField]
  32. internal FaceInfo m_FaceInfo;
  33. // The texture which contains the sprites.
  34. public Texture spriteSheet;
  35. /// <summary>
  36. ///
  37. /// </summary>
  38. public List<TMP_SpriteCharacter> spriteCharacterTable
  39. {
  40. get
  41. {
  42. if (m_GlyphIndexLookup == null)
  43. UpdateLookupTables();
  44. return m_SpriteCharacterTable;
  45. }
  46. internal set { m_SpriteCharacterTable = value; }
  47. }
  48. [SerializeField]
  49. private List<TMP_SpriteCharacter> m_SpriteCharacterTable = new List<TMP_SpriteCharacter>();
  50. /// <summary>
  51. /// Dictionary used to lookup sprite characters by their unicode value.
  52. /// </summary>
  53. public Dictionary<uint, TMP_SpriteCharacter> spriteCharacterLookupTable
  54. {
  55. get
  56. {
  57. if (m_SpriteCharacterLookup == null)
  58. UpdateLookupTables();
  59. return m_SpriteCharacterLookup;
  60. }
  61. internal set { m_SpriteCharacterLookup = value; }
  62. }
  63. internal Dictionary<uint, TMP_SpriteCharacter> m_SpriteCharacterLookup;
  64. public List<TMP_SpriteGlyph> spriteGlyphTable
  65. {
  66. get { return m_SpriteGlyphTable; }
  67. internal set { m_SpriteGlyphTable = value; }
  68. }
  69. [SerializeField]
  70. private List<TMP_SpriteGlyph> m_SpriteGlyphTable = new List<TMP_SpriteGlyph>();
  71. internal Dictionary<uint, TMP_SpriteGlyph> m_SpriteGlyphLookup;
  72. // List which contains the SpriteInfo for the sprites contained in the sprite sheet.
  73. public List<TMP_Sprite> spriteInfoList;
  74. /// <summary>
  75. /// List which contains the Fallback font assets for this font.
  76. /// </summary>
  77. [SerializeField]
  78. public List<TMP_SpriteAsset> fallbackSpriteAssets;
  79. internal bool m_IsSpriteAssetLookupTablesDirty = false;
  80. void Awake()
  81. {
  82. // Check version number of sprite asset to see if it needs to be upgraded.
  83. if (this.material != null && string.IsNullOrEmpty(m_Version))
  84. UpgradeSpriteAsset();
  85. }
  86. /// <summary>
  87. /// Create a material for the sprite asset.
  88. /// </summary>
  89. /// <returns></returns>
  90. Material GetDefaultSpriteMaterial()
  91. {
  92. //isEditingAsset = true;
  93. ShaderUtilities.GetShaderPropertyIDs();
  94. // Add a new material
  95. Shader shader = Shader.Find("TextMeshPro/Sprite");
  96. Material tempMaterial = new Material(shader);
  97. tempMaterial.SetTexture(ShaderUtilities.ID_MainTex, spriteSheet);
  98. tempMaterial.hideFlags = HideFlags.HideInHierarchy;
  99. #if UNITY_EDITOR
  100. UnityEditor.AssetDatabase.AddObjectToAsset(tempMaterial, this);
  101. UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(this));
  102. #endif
  103. //isEditingAsset = false;
  104. return tempMaterial;
  105. }
  106. /// <summary>
  107. /// Function to update the sprite name and unicode lookup tables.
  108. /// This function should be called when a sprite's name or unicode value changes or when a new sprite is added.
  109. /// </summary>
  110. public void UpdateLookupTables()
  111. {
  112. //Debug.Log("Updating [" + this.name + "] Lookup tables.");
  113. // Check version number of sprite asset to see if it needs to be upgraded.
  114. if (this.material != null && string.IsNullOrEmpty(m_Version))
  115. UpgradeSpriteAsset();
  116. // Initialize / Clear glyph index lookup dictionary.
  117. if (m_GlyphIndexLookup == null)
  118. m_GlyphIndexLookup = new Dictionary<uint, int>();
  119. else
  120. m_GlyphIndexLookup.Clear();
  121. //
  122. if (m_SpriteGlyphLookup == null)
  123. m_SpriteGlyphLookup = new Dictionary<uint, TMP_SpriteGlyph>();
  124. else
  125. m_SpriteGlyphLookup.Clear();
  126. // Initialize SpriteGlyphLookup
  127. for (int i = 0; i < m_SpriteGlyphTable.Count; i++)
  128. {
  129. TMP_SpriteGlyph spriteGlyph = m_SpriteGlyphTable[i];
  130. uint glyphIndex = spriteGlyph.index;
  131. if (m_GlyphIndexLookup.ContainsKey(glyphIndex) == false)
  132. m_GlyphIndexLookup.Add(glyphIndex, i);
  133. if (m_SpriteGlyphLookup.ContainsKey(glyphIndex) == false)
  134. m_SpriteGlyphLookup.Add(glyphIndex, spriteGlyph);
  135. }
  136. // Initialize name lookup
  137. if (m_NameLookup == null)
  138. m_NameLookup = new Dictionary<int, int>();
  139. else
  140. m_NameLookup.Clear();
  141. // Initialize character lookup
  142. if (m_SpriteCharacterLookup == null)
  143. m_SpriteCharacterLookup = new Dictionary<uint, TMP_SpriteCharacter>();
  144. else
  145. m_SpriteCharacterLookup.Clear();
  146. // Populate Sprite Character lookup tables
  147. for (int i = 0; i < m_SpriteCharacterTable.Count; i++)
  148. {
  149. TMP_SpriteCharacter spriteCharacter = m_SpriteCharacterTable[i];
  150. // Make sure sprite character is valid
  151. if (spriteCharacter == null)
  152. continue;
  153. uint glyphIndex = spriteCharacter.glyphIndex;
  154. // Lookup the glyph for this character
  155. if (m_SpriteGlyphLookup.ContainsKey(glyphIndex) == false)
  156. continue;
  157. // Assign glyph and text asset to this character
  158. spriteCharacter.glyph = m_SpriteGlyphLookup[glyphIndex];
  159. spriteCharacter.textAsset = this;
  160. int nameHashCode = m_SpriteCharacterTable[i].hashCode;
  161. if (m_NameLookup.ContainsKey(nameHashCode) == false)
  162. m_NameLookup.Add(nameHashCode, i);
  163. uint unicode = m_SpriteCharacterTable[i].unicode;
  164. if (unicode != 0xFFFE && m_SpriteCharacterLookup.ContainsKey(unicode) == false)
  165. m_SpriteCharacterLookup.Add(unicode, spriteCharacter);
  166. }
  167. m_IsSpriteAssetLookupTablesDirty = false;
  168. }
  169. /// <summary>
  170. /// Function which returns the sprite index using the hashcode of the name
  171. /// </summary>
  172. /// <param name="hashCode"></param>
  173. /// <returns></returns>
  174. public int GetSpriteIndexFromHashcode(int hashCode)
  175. {
  176. if (m_NameLookup == null)
  177. UpdateLookupTables();
  178. int index;
  179. if (m_NameLookup.TryGetValue(hashCode, out index))
  180. return index;
  181. return -1;
  182. }
  183. /// <summary>
  184. /// Returns the index of the sprite for the given unicode value.
  185. /// </summary>
  186. /// <param name="unicode"></param>
  187. /// <returns></returns>
  188. public int GetSpriteIndexFromUnicode (uint unicode)
  189. {
  190. if (m_SpriteCharacterLookup == null)
  191. UpdateLookupTables();
  192. TMP_SpriteCharacter spriteCharacter;
  193. if (m_SpriteCharacterLookup.TryGetValue(unicode, out spriteCharacter))
  194. return (int)spriteCharacter.glyphIndex;
  195. return -1;
  196. }
  197. /// <summary>
  198. /// Returns the index of the sprite for the given name.
  199. /// </summary>
  200. /// <param name="name"></param>
  201. /// <returns></returns>
  202. public int GetSpriteIndexFromName (string name)
  203. {
  204. if (m_NameLookup == null)
  205. UpdateLookupTables();
  206. int hashCode = TMP_TextUtilities.GetSimpleHashCode(name);
  207. return GetSpriteIndexFromHashcode(hashCode);
  208. }
  209. /// <summary>
  210. /// Used to keep track of which Sprite Assets have been searched.
  211. /// </summary>
  212. private static HashSet<int> k_searchedSpriteAssets;
  213. /// <summary>
  214. /// Search through the given sprite asset and its fallbacks for the specified sprite matching the given unicode character.
  215. /// </summary>
  216. /// <param name="spriteAsset">The font asset to search for the given character.</param>
  217. /// <param name="unicode">The character to find.</param>
  218. /// <param name="glyph">out parameter containing the glyph for the specified character (if found).</param>
  219. /// <returns></returns>
  220. public static TMP_SpriteAsset SearchForSpriteByUnicode(TMP_SpriteAsset spriteAsset, uint unicode, bool includeFallbacks, out int spriteIndex)
  221. {
  222. // Check to make sure sprite asset is not null
  223. if (spriteAsset == null) { spriteIndex = -1; return null; }
  224. // Get sprite index for the given unicode
  225. spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(unicode);
  226. if (spriteIndex != -1)
  227. return spriteAsset;
  228. // Initialize list to track instance of Sprite Assets that have already been searched.
  229. if (k_searchedSpriteAssets == null)
  230. k_searchedSpriteAssets = new HashSet<int>();
  231. else
  232. k_searchedSpriteAssets.Clear();
  233. // Get instance ID of sprite asset and add to list.
  234. int id = spriteAsset.GetInstanceID();
  235. k_searchedSpriteAssets.Add(id);
  236. // Search potential fallback sprite assets if includeFallbacks is true.
  237. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  238. return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, true, out spriteIndex);
  239. // Search default sprite asset potentially assigned in the TMP Settings.
  240. if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
  241. return SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, unicode, true, out spriteIndex);
  242. spriteIndex = -1;
  243. return null;
  244. }
  245. /// <summary>
  246. /// Search through the given list of sprite assets and fallbacks for a sprite whose unicode value matches the target unicode.
  247. /// </summary>
  248. /// <param name="spriteAssets"></param>
  249. /// <param name="unicode"></param>
  250. /// <param name="includeFallbacks"></param>
  251. /// <param name="spriteIndex"></param>
  252. /// <returns></returns>
  253. private static TMP_SpriteAsset SearchForSpriteByUnicodeInternal(List<TMP_SpriteAsset> spriteAssets, uint unicode, bool includeFallbacks, out int spriteIndex)
  254. {
  255. for (int i = 0; i < spriteAssets.Count; i++)
  256. {
  257. TMP_SpriteAsset temp = spriteAssets[i];
  258. if (temp == null) continue;
  259. int id = temp.GetInstanceID();
  260. // Skip sprite asset if it has already been searched.
  261. if (k_searchedSpriteAssets.Add(id) == false)
  262. continue;
  263. temp = SearchForSpriteByUnicodeInternal(temp, unicode, includeFallbacks, out spriteIndex);
  264. if (temp != null)
  265. return temp;
  266. }
  267. spriteIndex = -1;
  268. return null;
  269. }
  270. /// <summary>
  271. /// Search the given sprite asset and fallbacks for a sprite whose unicode value matches the target unicode.
  272. /// </summary>
  273. /// <param name="spriteAsset"></param>
  274. /// <param name="unicode"></param>
  275. /// <param name="includeFallbacks"></param>
  276. /// <param name="spriteIndex"></param>
  277. /// <returns></returns>
  278. private static TMP_SpriteAsset SearchForSpriteByUnicodeInternal(TMP_SpriteAsset spriteAsset, uint unicode, bool includeFallbacks, out int spriteIndex)
  279. {
  280. // Get sprite index for the given unicode
  281. spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(unicode);
  282. if (spriteIndex != -1)
  283. return spriteAsset;
  284. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  285. return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, true, out spriteIndex);
  286. spriteIndex = -1;
  287. return null;
  288. }
  289. /// <summary>
  290. /// Search the given sprite asset and fallbacks for a sprite whose hash code value of its name matches the target hash code.
  291. /// </summary>
  292. /// <param name="spriteAsset">The Sprite Asset to search for the given sprite whose name matches the hashcode value</param>
  293. /// <param name="hashCode">The hash code value matching the name of the sprite</param>
  294. /// <param name="includeFallbacks">Include fallback sprite assets in the search</param>
  295. /// <param name="spriteIndex">The index of the sprite matching the provided hash code</param>
  296. /// <returns>The Sprite Asset that contains the sprite</returns>
  297. public static TMP_SpriteAsset SearchForSpriteByHashCode(TMP_SpriteAsset spriteAsset, int hashCode, bool includeFallbacks, out int spriteIndex)
  298. {
  299. // Make sure sprite asset is not null
  300. if (spriteAsset == null) { spriteIndex = -1; return null; }
  301. spriteIndex = spriteAsset.GetSpriteIndexFromHashcode(hashCode);
  302. if (spriteIndex != -1)
  303. return spriteAsset;
  304. // Initialize or clear list to Sprite Assets that have already been searched.
  305. if (k_searchedSpriteAssets == null)
  306. k_searchedSpriteAssets = new HashSet<int>();
  307. else
  308. k_searchedSpriteAssets.Clear();
  309. int id = spriteAsset.instanceID;
  310. // Add to list of font assets already searched.
  311. k_searchedSpriteAssets.Add(id);
  312. TMP_SpriteAsset tempSpriteAsset;
  313. // Search potential fallbacks assigned to local sprite asset.
  314. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  315. {
  316. tempSpriteAsset = SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, true, out spriteIndex);
  317. if (spriteIndex != -1)
  318. return tempSpriteAsset;
  319. }
  320. // Search default sprite asset potentially assigned in the TMP Settings.
  321. if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
  322. {
  323. tempSpriteAsset = SearchForSpriteByHashCodeInternal(TMP_Settings.defaultSpriteAsset, hashCode, true, out spriteIndex);
  324. if (spriteIndex != -1)
  325. return tempSpriteAsset;
  326. }
  327. // Clear search list since we are now looking for the missing sprite character.
  328. k_searchedSpriteAssets.Clear();
  329. uint missingSpriteCharacterUnicode = TMP_Settings.missingCharacterSpriteUnicode;
  330. // Get sprite index for the given unicode
  331. spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(missingSpriteCharacterUnicode);
  332. if (spriteIndex != -1)
  333. return spriteAsset;
  334. // Add current sprite asset to list of assets already searched.
  335. k_searchedSpriteAssets.Add(id);
  336. // Search for the missing sprite character in the local sprite asset and potential fallbacks.
  337. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  338. {
  339. tempSpriteAsset = SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, missingSpriteCharacterUnicode, true, out spriteIndex);
  340. if (spriteIndex != -1)
  341. return tempSpriteAsset;
  342. }
  343. // Search for the missing sprite character in the default sprite asset and potential fallbacks.
  344. if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
  345. {
  346. tempSpriteAsset = SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, missingSpriteCharacterUnicode, true, out spriteIndex);
  347. if (spriteIndex != -1)
  348. return tempSpriteAsset;
  349. }
  350. spriteIndex = -1;
  351. return null;
  352. }
  353. /// <summary>
  354. /// Search through the given list of sprite assets and fallbacks for a sprite whose hash code value of its name matches the target hash code.
  355. /// </summary>
  356. /// <param name="spriteAssets"></param>
  357. /// <param name="hashCode"></param>
  358. /// <param name="searchFallbacks"></param>
  359. /// <param name="spriteIndex"></param>
  360. /// <returns></returns>
  361. private static TMP_SpriteAsset SearchForSpriteByHashCodeInternal(List<TMP_SpriteAsset> spriteAssets, int hashCode, bool searchFallbacks, out int spriteIndex)
  362. {
  363. // Search through the list of sprite assets
  364. for (int i = 0; i < spriteAssets.Count; i++)
  365. {
  366. TMP_SpriteAsset temp = spriteAssets[i];
  367. if (temp == null) continue;
  368. int id = temp.instanceID;
  369. // Skip sprite asset if it has already been searched.
  370. if (k_searchedSpriteAssets.Add(id) == false)
  371. continue;
  372. temp = SearchForSpriteByHashCodeInternal(temp, hashCode, searchFallbacks, out spriteIndex);
  373. if (temp != null)
  374. return temp;
  375. }
  376. spriteIndex = -1;
  377. return null;
  378. }
  379. /// <summary>
  380. /// Search through the given sprite asset and fallbacks for a sprite whose hash code value of its name matches the target hash code.
  381. /// </summary>
  382. /// <param name="spriteAsset"></param>
  383. /// <param name="hashCode"></param>
  384. /// <param name="searchFallbacks"></param>
  385. /// <param name="spriteIndex"></param>
  386. /// <returns></returns>
  387. private static TMP_SpriteAsset SearchForSpriteByHashCodeInternal(TMP_SpriteAsset spriteAsset, int hashCode, bool searchFallbacks, out int spriteIndex)
  388. {
  389. // Get the sprite for the given hash code.
  390. spriteIndex = spriteAsset.GetSpriteIndexFromHashcode(hashCode);
  391. if (spriteIndex != -1)
  392. return spriteAsset;
  393. if (searchFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
  394. return SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, true, out spriteIndex);
  395. spriteIndex = -1;
  396. return null;
  397. }
  398. /// <summary>
  399. /// Sort the sprite glyph table by glyph index.
  400. /// </summary>
  401. public void SortGlyphTable()
  402. {
  403. if (m_SpriteGlyphTable == null || m_SpriteGlyphTable.Count == 0) return;
  404. m_SpriteGlyphTable = m_SpriteGlyphTable.OrderBy(item => item.index).ToList();
  405. }
  406. /// <summary>
  407. /// Sort the sprite character table by Unicode values.
  408. /// </summary>
  409. internal void SortCharacterTable()
  410. {
  411. if (m_SpriteCharacterTable != null && m_SpriteCharacterTable.Count > 0)
  412. m_SpriteCharacterTable = m_SpriteCharacterTable.OrderBy(c => c.unicode).ToList();
  413. }
  414. /// <summary>
  415. /// Sort both sprite glyph and character tables.
  416. /// </summary>
  417. internal void SortGlyphAndCharacterTables()
  418. {
  419. SortGlyphTable();
  420. SortCharacterTable();
  421. }
  422. /// <summary>
  423. /// Internal method used to upgrade sprite asset.
  424. /// </summary>
  425. private void UpgradeSpriteAsset()
  426. {
  427. m_Version = "1.1.0";
  428. Debug.Log("Upgrading sprite asset [" + this.name + "] to version " + m_Version + ".", this);
  429. // Convert legacy glyph and character tables to new format
  430. m_SpriteCharacterTable.Clear();
  431. m_SpriteGlyphTable.Clear();
  432. for (int i = 0; i < spriteInfoList.Count; i++)
  433. {
  434. TMP_Sprite oldSprite = spriteInfoList[i];
  435. TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
  436. spriteGlyph.index = (uint)i;
  437. spriteGlyph.sprite = oldSprite.sprite;
  438. spriteGlyph.metrics = new GlyphMetrics(oldSprite.width, oldSprite.height, oldSprite.xOffset, oldSprite.yOffset, oldSprite.xAdvance);
  439. spriteGlyph.glyphRect = new GlyphRect((int)oldSprite.x, (int)oldSprite.y, (int)oldSprite.width, (int)oldSprite.height);
  440. spriteGlyph.scale = 1.0f;
  441. spriteGlyph.atlasIndex = 0;
  442. m_SpriteGlyphTable.Add(spriteGlyph);
  443. TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter();
  444. spriteCharacter.glyph = spriteGlyph;
  445. spriteCharacter.unicode = oldSprite.unicode == 0x0 ? 0xFFFE : (uint)oldSprite.unicode;
  446. spriteCharacter.name = oldSprite.name;
  447. spriteCharacter.scale = oldSprite.scale;
  448. m_SpriteCharacterTable.Add(spriteCharacter);
  449. }
  450. // Clear legacy glyph info list.
  451. //spriteInfoList.Clear();
  452. UpdateLookupTables();
  453. #if UNITY_EDITOR
  454. UnityEditor.EditorUtility.SetDirty(this);
  455. UnityEditor.AssetDatabase.SaveAssets();
  456. #endif
  457. }
  458. }
  459. }