152 lines
6.7 KiB
C#
152 lines
6.7 KiB
C#
using Cryville.Common.Font;
|
|
using Cryville.Culture;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Reflection;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.TextCore;
|
|
using UnityEngine.TextCore.LowLevel;
|
|
using AtlasPopulationMode = TMPro.AtlasPopulationMode;
|
|
|
|
namespace Cryville.Common.Unity.UI {
|
|
[RequireComponent(typeof(TMP_Text))]
|
|
public class TMPLocalizedText : MonoBehaviour {
|
|
public static Shader DefaultShader;
|
|
public static FontMatcher FontMatcher;
|
|
public static int MaxFallbackCount = 4;
|
|
|
|
static readonly Dictionary<CultureInfo, TMP_FontAsset> _cachedFonts = new();
|
|
|
|
[SerializeField]
|
|
Shader m_shader;
|
|
|
|
public TMP_Text Text { get; private set; }
|
|
void Awake() {
|
|
Text = GetComponent<TMP_Text>();
|
|
}
|
|
|
|
public void SetText(string text, CultureInfo culture = null) {
|
|
Text.text = text;
|
|
SetCulture(culture ?? CultureInfo.CurrentCulture);
|
|
}
|
|
|
|
void SetCulture(CultureInfo culture) {
|
|
if (FontMatcher == null) return;
|
|
string cultureName = culture.Name;
|
|
if (string.IsNullOrEmpty(cultureName)) cultureName = CultureInfo.CurrentCulture.Name;
|
|
if (!_cachedFonts.TryGetValue(culture, out var font)) {
|
|
foreach (var typeface in FontMatcher.MatchLanguage(new LanguageId(cultureName), true)) {
|
|
try {
|
|
var ifont = CreateFontAsset(typeface.File.FullName, typeface.IndexInFile);
|
|
if (m_shader) ifont.material.shader = m_shader;
|
|
else if (DefaultShader) ifont.material.shader = DefaultShader;
|
|
if (font == null) {
|
|
font = ifont;
|
|
if (MaxFallbackCount <= 0) break;
|
|
}
|
|
else {
|
|
font.fallbackFontAssetTable ??= new List<TMP_FontAsset>();
|
|
font.fallbackFontAssetTable.Add(ifont);
|
|
if (font.fallbackFontAssetTable.Count >= MaxFallbackCount) break;
|
|
}
|
|
}
|
|
catch (Exception) { }
|
|
}
|
|
_cachedFonts.Add(culture, font);
|
|
}
|
|
Text.font = font;
|
|
}
|
|
|
|
static TMP_FontAsset CreateFontAsset(string path, int index) => CreateFontAsset(path, index, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, AtlasPopulationMode.Dynamic);
|
|
|
|
static readonly Lazy<FieldInfo> _f_m_Version = new(() => typeof(TMP_FontAsset).GetField("m_Version", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
static readonly Lazy<PropertyInfo> _p_atlasWidth = new(() => typeof(TMP_FontAsset).GetProperty("atlasWidth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
static readonly Lazy<PropertyInfo> _p_atlasHeight = new(() => typeof(TMP_FontAsset).GetProperty("atlasHeight", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
static readonly Lazy<PropertyInfo> _p_atlasPadding = new(() => typeof(TMP_FontAsset).GetProperty("atlasPadding", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
static readonly Lazy<PropertyInfo> _p_atlasRenderMode = new(() => typeof(TMP_FontAsset).GetProperty("atlasRenderMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
static readonly Lazy<PropertyInfo> _p_freeGlyphRects = new(() => typeof(TMP_FontAsset).GetProperty("freeGlyphRects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
static readonly Lazy<PropertyInfo> _p_usedGlyphRects = new(() => typeof(TMP_FontAsset).GetProperty("usedGlyphRects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
|
|
|
|
static readonly Lazy<PropertyInfo> _p_ShaderRef_MobileBitmap = new(() => typeof(ShaderUtilities).GetProperty("ShaderRef_MobileBitmap", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
|
|
static readonly Lazy<PropertyInfo> _p_ShaderRef_MobileSDF = new(() => typeof(ShaderUtilities).GetProperty("ShaderRef_MobileSDF", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
|
|
|
|
public static TMP_FontAsset CreateFontAsset(string path, int faceIndex, int samplingPointSize, int atlasPadding, GlyphRenderMode renderMode, int atlasWidth, int atlasHeight, AtlasPopulationMode atlasPopulationMode = AtlasPopulationMode.Dynamic, bool enableMultiAtlasSupport = true) {
|
|
// Initialize FontEngine
|
|
FontEngine.InitializeFontEngine();
|
|
|
|
// Load Font Face
|
|
if (FontEngine.LoadFontFace(path, samplingPointSize, faceIndex) != FontEngineError.Success) {
|
|
Debug.LogWarning("Unable to load font face at path " + path);
|
|
return null;
|
|
}
|
|
|
|
// Create new font asset
|
|
TMP_FontAsset fontAsset = ScriptableObject.CreateInstance<TMP_FontAsset>();
|
|
|
|
_f_m_Version.Value.SetValue(fontAsset, "1.1.0");
|
|
fontAsset.faceInfo = FontEngine.GetFaceInfo();
|
|
|
|
fontAsset.atlasPopulationMode = atlasPopulationMode;
|
|
|
|
_p_atlasWidth.Value.SetValue(fontAsset, atlasWidth);
|
|
_p_atlasHeight.Value.SetValue(fontAsset, atlasHeight);
|
|
_p_atlasPadding.Value.SetValue(fontAsset, atlasPadding);
|
|
_p_atlasRenderMode.Value.SetValue(fontAsset, renderMode);
|
|
|
|
// Initialize array for the font atlas textures.
|
|
fontAsset.atlasTextures = new Texture2D[1];
|
|
|
|
// Create and add font atlas texture.
|
|
var texture = new Texture2D(0, 0, TextureFormat.Alpha8, false);
|
|
fontAsset.atlasTextures[0] = texture;
|
|
|
|
fontAsset.isMultiAtlasTexturesEnabled = enableMultiAtlasSupport;
|
|
|
|
// Add free rectangle of the size of the texture.
|
|
int packingModifier;
|
|
if (((int)renderMode & 0x10) != 0) {
|
|
packingModifier = 0;
|
|
|
|
// Optimize by adding static ref to shader.
|
|
var tmp_material = new Material((Shader)_p_ShaderRef_MobileBitmap.Value.GetValue(null));
|
|
|
|
//tmp_material.name = texture.name + " Material";
|
|
tmp_material.SetTexture(ShaderUtilities.ID_MainTex, texture);
|
|
tmp_material.SetFloat(ShaderUtilities.ID_TextureWidth, atlasWidth);
|
|
tmp_material.SetFloat(ShaderUtilities.ID_TextureHeight, atlasHeight);
|
|
|
|
fontAsset.material = tmp_material;
|
|
}
|
|
else {
|
|
packingModifier = 1;
|
|
|
|
// Optimize by adding static ref to shader.
|
|
var tmp_material = new Material((Shader)_p_ShaderRef_MobileSDF.Value.GetValue(null));
|
|
|
|
//tmp_material.name = texture.name + " Material";
|
|
tmp_material.SetTexture(ShaderUtilities.ID_MainTex, texture);
|
|
tmp_material.SetFloat(ShaderUtilities.ID_TextureWidth, atlasWidth);
|
|
tmp_material.SetFloat(ShaderUtilities.ID_TextureHeight, atlasHeight);
|
|
|
|
tmp_material.SetFloat(ShaderUtilities.ID_GradientScale, atlasPadding + packingModifier);
|
|
|
|
tmp_material.SetFloat(ShaderUtilities.ID_WeightNormal, fontAsset.normalStyle);
|
|
tmp_material.SetFloat(ShaderUtilities.ID_WeightBold, fontAsset.boldStyle);
|
|
|
|
fontAsset.material = tmp_material;
|
|
}
|
|
|
|
_p_freeGlyphRects.Value.SetValue(fontAsset, new List<GlyphRect>(8) { new(0, 0, atlasWidth - packingModifier, atlasHeight - packingModifier) });
|
|
_p_usedGlyphRects.Value.SetValue(fontAsset, new List<GlyphRect>(8));
|
|
|
|
// TODO: Consider adding support for extracting glyph positioning data
|
|
|
|
fontAsset.ReadFontAssetDefinition();
|
|
|
|
return fontAsset;
|
|
}
|
|
}
|
|
}
|