Implement DalamudFontAtlas

This commit is contained in:
Soreepeong 2023-11-21 15:09:38 +09:00 committed by Soreepeong
parent 01cde50a46
commit 8bdab4d2c8
41 changed files with 7551 additions and 1428 deletions

View file

@ -1,4 +1,3 @@
using System.IO;
using System.Linq;
using System.Numerics;
@ -7,6 +6,8 @@ using Dalamud.Interface.Animation.EasingFunctions;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Components;
using Dalamud.Interface.GameFonts;
using Dalamud.Interface.ManagedFontAtlas;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
@ -31,8 +32,14 @@ internal sealed class ChangelogWindow : Window, IDisposable
Plugins can now add tooltips and interaction to the server info bar
The Dalamud/plugin installer UI has been refreshed
";
private readonly TitleScreenMenuWindow tsmWindow;
private readonly DisposeSafety.ScopedFinalizer scopedFinalizer = new();
private readonly IFontAtlas privateAtlas;
private readonly Lazy<IFontHandle> bannerFont;
private readonly Lazy<IDalamudTextureWrap> apiBumpExplainerTexture;
private readonly Lazy<IDalamudTextureWrap> logoTexture;
private readonly InOutCubic windowFade = new(TimeSpan.FromSeconds(2.5f))
{
@ -46,27 +53,36 @@ internal sealed class ChangelogWindow : Window, IDisposable
Point2 = Vector2.One,
};
private IDalamudTextureWrap? apiBumpExplainerTexture;
private IDalamudTextureWrap? logoTexture;
private GameFontHandle? bannerFont;
private State state = State.WindowFadeIn;
private bool needFadeRestart = false;
/// <summary>
/// Initializes a new instance of the <see cref="ChangelogWindow"/> class.
/// </summary>
/// <param name="tsmWindow">TSM window.</param>
public ChangelogWindow(TitleScreenMenuWindow tsmWindow)
/// <param name="fontAtlasFactory">An instance of <see cref="FontAtlasFactory"/>.</param>
/// <param name="assets">An instance of <see cref="DalamudAssetManager"/>.</param>
public ChangelogWindow(
TitleScreenMenuWindow tsmWindow,
FontAtlasFactory fontAtlasFactory,
DalamudAssetManager assets)
: base("What's new in Dalamud?##ChangelogWindow", ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse, true)
{
this.tsmWindow = tsmWindow;
this.Namespace = "DalamudChangelogWindow";
this.privateAtlas = this.scopedFinalizer.Add(
fontAtlasFactory.CreateFontAtlas(this.Namespace, FontAtlasAutoRebuildMode.Async));
this.bannerFont = new(
() => this.scopedFinalizer.Add(
this.privateAtlas.NewGameFontHandle(new(GameFontFamilyAndSize.MiedingerMid18))));
this.apiBumpExplainerTexture = new(() => assets.GetDalamudTextureWrap(DalamudAsset.ChangelogApiBumpIcon));
this.logoTexture = new(() => assets.GetDalamudTextureWrap(DalamudAsset.Logo));
// If we are going to show a changelog, make sure we have the font ready, otherwise it will hitch
if (WarrantsChangelog())
Service<GameFontManager>.GetAsync().ContinueWith(t => this.MakeFont(t.Result));
_ = this.bannerFont;
}
private enum State
@ -97,20 +113,12 @@ internal sealed class ChangelogWindow : Window, IDisposable
Service<DalamudInterface>.Get().SetCreditsDarkeningAnimation(true);
this.tsmWindow.AllowDrawing = false;
this.MakeFont(Service<GameFontManager>.Get());
_ = this.bannerFont;
this.state = State.WindowFadeIn;
this.windowFade.Reset();
this.bodyFade.Reset();
this.needFadeRestart = true;
if (this.apiBumpExplainerTexture == null)
{
var dalamud = Service<Dalamud>.Get();
var tm = Service<TextureManager>.Get();
this.apiBumpExplainerTexture = tm.GetTextureFromFile(new FileInfo(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "changelogApiBump.png")))
?? throw new Exception("Could not load api bump explainer.");
}
base.OnOpen();
}
@ -186,10 +194,7 @@ internal sealed class ChangelogWindow : Window, IDisposable
ImGui.SetCursorPos(new Vector2(logoContainerSize.X / 2 - logoSize.X / 2, logoContainerSize.Y / 2 - logoSize.Y / 2));
using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, Math.Clamp(this.windowFade.EasedPoint.X - 0.5f, 0f, 1f)))
{
this.logoTexture ??= Service<DalamudAssetManager>.Get().GetDalamudTextureWrap(DalamudAsset.Logo);
ImGui.Image(this.logoTexture.ImGuiHandle, logoSize);
}
ImGui.Image(this.logoTexture.Value.ImGuiHandle, logoSize);
}
ImGui.SameLine();
@ -205,7 +210,7 @@ internal sealed class ChangelogWindow : Window, IDisposable
using (ImRaii.PushStyle(ImGuiStyleVar.Alpha, Math.Clamp(this.windowFade.EasedPoint.X - 1f, 0f, 1f)))
{
using var font = ImRaii.PushFont(this.bannerFont!.ImFont);
using var font = this.bannerFont.Value.Push();
switch (this.state)
{
@ -275,9 +280,11 @@ internal sealed class ChangelogWindow : Window, IDisposable
ImGui.TextWrapped("If some plugins are displayed with a red cross in the 'Installed Plugins' tab, they may not yet be available.");
ImGuiHelpers.ScaledDummy(15);
ImGuiHelpers.CenterCursorFor(this.apiBumpExplainerTexture!.Width);
ImGui.Image(this.apiBumpExplainerTexture.ImGuiHandle, this.apiBumpExplainerTexture.Size);
ImGuiHelpers.CenterCursorFor(this.apiBumpExplainerTexture.Value.Width);
ImGui.Image(
this.apiBumpExplainerTexture.Value.ImGuiHandle,
this.apiBumpExplainerTexture.Value.Size);
DrawNextButton(State.Links);
break;
@ -377,7 +384,4 @@ internal sealed class ChangelogWindow : Window, IDisposable
public void Dispose()
{
}
private void MakeFont(GameFontManager gfm) =>
this.bannerFont ??= gfm.NewFontRef(new GameFontStyle(GameFontFamilyAndSize.MiedingerMid18));
}

View file

@ -6,6 +6,8 @@ using Dalamud.Interface.Components;
using Dalamud.Interface.Internal.Windows.Data.Widgets;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Windowing;
using Dalamud.Utility;
using ImGuiNET;
using Serilog;
@ -14,7 +16,7 @@ namespace Dalamud.Interface.Internal.Windows.Data;
/// <summary>
/// Class responsible for drawing the data/debug window.
/// </summary>
internal class DataWindow : Window
internal class DataWindow : Window, IDisposable
{
private readonly IDataWindowWidget[] modules =
{
@ -34,6 +36,7 @@ internal class DataWindow : Window
new FlyTextWidget(),
new FontAwesomeTestWidget(),
new GameInventoryTestWidget(),
new GamePrebakedFontsTestWidget(),
new GamepadWidget(),
new GaugeWidget(),
new HookWidget(),
@ -76,6 +79,9 @@ internal class DataWindow : Window
this.Load();
}
/// <inheritdoc/>
public void Dispose() => this.modules.OfType<IDisposable>().AggregateToDisposable().Dispose();
/// <inheritdoc/>
public override void OnOpen()
{

View file

@ -0,0 +1,186 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Dalamud.Interface.GameFonts;
using Dalamud.Interface.ManagedFontAtlas;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Utility;
using Dalamud.Utility;
using ImGuiNET;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// <summary>
/// Widget for testing game prebaked fonts.
/// </summary>
internal class GamePrebakedFontsTestWidget : IDataWindowWidget, IDisposable
{
private ImVectorWrapper<byte> testStringBuffer;
private IFontAtlas? privateAtlas;
private IReadOnlyDictionary<GameFontFamily, (GameFontStyle Size, Lazy<IFontHandle> Handle)[]>? fontHandles;
private bool useGlobalScale;
private bool useWordWrap;
private bool useItalic;
private bool useBold;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; }
/// <inheritdoc/>
public string DisplayName { get; init; } = "Game Prebaked Fonts";
/// <inheritdoc/>
public bool Ready { get; set; }
/// <inheritdoc/>
public void Load() => this.Ready = true;
/// <inheritdoc/>
public unsafe void Draw()
{
ImGui.AlignTextToFramePadding();
fixed (byte* labelPtr = "Global Scale"u8)
{
var v = (byte)(this.useGlobalScale ? 1 : 0);
if (ImGuiNative.igCheckbox(labelPtr, &v) != 0)
{
this.useGlobalScale = v != 0;
this.ClearAtlas();
}
}
ImGui.SameLine();
fixed (byte* labelPtr = "Word Wrap"u8)
{
var v = (byte)(this.useWordWrap ? 1 : 0);
if (ImGuiNative.igCheckbox(labelPtr, &v) != 0)
this.useWordWrap = v != 0;
}
ImGui.SameLine();
fixed (byte* labelPtr = "Italic"u8)
{
var v = (byte)(this.useItalic ? 1 : 0);
if (ImGuiNative.igCheckbox(labelPtr, &v) != 0)
{
this.useItalic = v != 0;
this.ClearAtlas();
}
}
ImGui.SameLine();
fixed (byte* labelPtr = "Bold"u8)
{
var v = (byte)(this.useBold ? 1 : 0);
if (ImGuiNative.igCheckbox(labelPtr, &v) != 0)
{
this.useBold = v != 0;
this.ClearAtlas();
}
}
ImGui.SameLine();
if (ImGui.Button("Reset Text") || this.testStringBuffer.IsDisposed)
{
this.testStringBuffer.Dispose();
this.testStringBuffer = ImVectorWrapper.CreateFromSpan(
"(Game)-[Font] {Test}. 0123456789!! <氣気气きキ기>。"u8,
minCapacity: 1024);
}
this.privateAtlas ??=
Service<FontAtlasFactory>.Get().CreateFontAtlas(
nameof(GamePrebakedFontsTestWidget),
FontAtlasAutoRebuildMode.Async,
this.useGlobalScale);
this.fontHandles ??=
Enum.GetValues<GameFontFamilyAndSize>()
.Where(x => x.GetAttribute<GameFontFamilyAndSizeAttribute>() is not null)
.Select(x => new GameFontStyle(x) { Italic = this.useItalic, Bold = this.useBold })
.GroupBy(x => x.Family)
.ToImmutableDictionary(
x => x.Key,
x => x.Select(y => (y, new Lazy<IFontHandle>(() => this.privateAtlas.NewGameFontHandle(y))))
.ToArray());
fixed (byte* labelPtr = "Test Input"u8)
{
if (ImGuiNative.igInputTextMultiline(
labelPtr,
this.testStringBuffer.Data,
(uint)this.testStringBuffer.Capacity,
new(ImGui.GetContentRegionAvail().X, 32 * ImGuiHelpers.GlobalScale),
0,
null,
null) != 0)
{
var len = this.testStringBuffer.StorageSpan.IndexOf((byte)0);
if (len + 4 >= this.testStringBuffer.Capacity)
this.testStringBuffer.EnsureCapacityExponential(len + 4);
if (len < this.testStringBuffer.Capacity)
{
this.testStringBuffer.LengthUnsafe = len;
this.testStringBuffer.StorageSpan[len] = default;
}
}
}
var offsetX = ImGui.CalcTextSize("99.9pt").X + (ImGui.GetStyle().FramePadding.X * 2);
foreach (var (family, items) in this.fontHandles)
{
if (!ImGui.CollapsingHeader($"{family} Family"))
continue;
foreach (var (gfs, handle) in items)
{
ImGui.TextUnformatted($"{gfs.SizePt}pt");
ImGui.SameLine(offsetX);
ImGuiNative.igPushTextWrapPos(this.useWordWrap ? 0f : -1f);
try
{
if (handle.Value.LoadException is { } exc)
{
ImGui.TextUnformatted(exc.ToString());
}
else if (!handle.Value.Available)
{
fixed (byte* labelPtr = "Loading..."u8)
ImGuiNative.igTextUnformatted(labelPtr, labelPtr + 8 + ((Environment.TickCount / 200) % 3));
}
else
{
if (!this.useGlobalScale)
ImGuiNative.igSetWindowFontScale(1 / ImGuiHelpers.GlobalScale);
using var pushPop = handle.Value.Push();
ImGuiNative.igTextUnformatted(
this.testStringBuffer.Data,
this.testStringBuffer.Data + this.testStringBuffer.Length);
}
}
finally
{
ImGuiNative.igPopTextWrapPos();
ImGuiNative.igSetWindowFontScale(1);
}
}
}
}
/// <inheritdoc/>
public void Dispose()
{
this.ClearAtlas();
this.testStringBuffer.Dispose();
}
private void ClearAtlas()
{
this.fontHandles?.Values.SelectMany(x => x.Where(y => y.Handle.IsValueCreated).Select(y => y.Handle.Value))
.AggregateToDisposable().Dispose();
this.fontHandles = null;
this.privateAtlas?.Dispose();
this.privateAtlas = null;
}
}

View file

@ -8,7 +8,6 @@ using Dalamud.Interface.Internal.Windows.Settings.Tabs;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Internal;
using Dalamud.Utility;
using ImGuiNET;
@ -19,14 +18,7 @@ namespace Dalamud.Interface.Internal.Windows.Settings;
/// </summary>
internal class SettingsWindow : Window
{
private readonly SettingsTab[] tabs =
{
new SettingsTabGeneral(),
new SettingsTabLook(),
new SettingsTabDtr(),
new SettingsTabExperimental(),
new SettingsTabAbout(),
};
private SettingsTab[]? tabs;
private string searchInput = string.Empty;
@ -49,6 +41,15 @@ internal class SettingsWindow : Window
/// <inheritdoc/>
public override void OnOpen()
{
this.tabs ??= new SettingsTab[]
{
new SettingsTabGeneral(),
new SettingsTabLook(),
new SettingsTabDtr(),
new SettingsTabExperimental(),
new SettingsTabAbout(),
};
foreach (var settingsTab in this.tabs)
{
settingsTab.Load();

View file

@ -1,13 +1,13 @@
using System;
using System.Diagnostics;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Numerics;
using CheapLoc;
using Dalamud.Game.Gui;
using Dalamud.Interface.GameFonts;
using Dalamud.Interface.ManagedFontAtlas;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Plugin.Internal;
@ -15,7 +15,6 @@ using Dalamud.Storage.Assets;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using ImGuiNET;
using ImGuiScene;
namespace Dalamud.Interface.Internal.Windows.Settings.Tabs;
@ -173,16 +172,21 @@ Contribute at: https://github.com/goatcorp/Dalamud
";
private readonly Stopwatch creditsThrottler;
private readonly IFontAtlas privateAtlas;
private string creditsText;
private bool resetNow = false;
private IDalamudTextureWrap? logoTexture;
private GameFontHandle? thankYouFont;
private IFontHandle? thankYouFont;
public SettingsTabAbout()
{
this.creditsThrottler = new();
this.privateAtlas = Service<FontAtlasFactory>
.Get()
.CreateFontAtlas(nameof(SettingsTabAbout), FontAtlasAutoRebuildMode.Async);
}
public override SettingsEntry[] Entries { get; } = { };
@ -207,11 +211,7 @@ Contribute at: https://github.com/goatcorp/Dalamud
this.creditsThrottler.Restart();
if (this.thankYouFont == null)
{
var gfm = Service<GameFontManager>.Get();
this.thankYouFont = gfm.NewFontRef(new GameFontStyle(GameFontFamilyAndSize.TrumpGothic34));
}
this.thankYouFont ??= this.privateAtlas.NewGameFontHandle(new(GameFontFamilyAndSize.TrumpGothic34));
this.resetNow = true;
@ -269,14 +269,12 @@ Contribute at: https://github.com/goatcorp/Dalamud
if (this.thankYouFont != null)
{
ImGui.PushFont(this.thankYouFont.ImFont);
using var fontPush = this.thankYouFont.Push();
var thankYouLenX = ImGui.CalcTextSize(ThankYouText).X;
ImGui.Dummy(new Vector2((windowX / 2) - (thankYouLenX / 2), 0f));
ImGui.SameLine();
ImGui.TextUnformatted(ThankYouText);
ImGui.PopFont();
}
ImGuiHelpers.ScaledDummy(0, windowSize.Y + 50f);
@ -305,9 +303,5 @@ Contribute at: https://github.com/goatcorp/Dalamud
/// <summary>
/// Disposes of managed and unmanaged resources.
/// </summary>
public override void Dispose()
{
this.logoTexture?.Dispose();
this.thankYouFont?.Dispose();
}
public override void Dispose() => this.privateAtlas.Dispose();
}

View file

@ -7,11 +7,14 @@ using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.Gui;
using Dalamud.Interface.Animation.EasingFunctions;
using Dalamud.Interface.ManagedFontAtlas;
using Dalamud.Interface.ManagedFontAtlas.Internals;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin.Services;
using Dalamud.Storage.Assets;
using Dalamud.Utility;
using ImGuiNET;
@ -27,16 +30,17 @@ internal class TitleScreenMenuWindow : Window, IDisposable
private readonly ClientState clientState;
private readonly DalamudConfiguration configuration;
private readonly Framework framework;
private readonly GameGui gameGui;
private readonly TitleScreenMenu titleScreenMenu;
private readonly DisposeSafety.ScopedFinalizer scopedFinalizer = new();
private readonly IFontAtlas privateAtlas;
private readonly Lazy<IFontHandle> myFontHandle;
private readonly Lazy<IDalamudTextureWrap> shadeTexture;
private readonly Dictionary<Guid, InOutCubic> shadeEasings = new();
private readonly Dictionary<Guid, InOutQuint> moveEasings = new();
private readonly Dictionary<Guid, InOutCubic> logoEasings = new();
private readonly Dictionary<string, InterfaceManager.SpecialGlyphRequest> specialGlyphRequests = new();
private InOutCubic? fadeOutEasing;
@ -48,6 +52,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
/// <param name="clientState">An instance of <see cref="ClientState"/>.</param>
/// <param name="configuration">An instance of <see cref="DalamudConfiguration"/>.</param>
/// <param name="dalamudAssetManager">An instance of <see cref="DalamudAssetManager"/>.</param>
/// <param name="fontAtlasFactory">An instance of <see cref="FontAtlasFactory"/>.</param>
/// <param name="framework">An instance of <see cref="Framework"/>.</param>
/// <param name="titleScreenMenu">An instance of <see cref="TitleScreenMenu"/>.</param>
/// <param name="gameGui">An instance of <see cref="gameGui"/>.</param>
@ -55,6 +60,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
ClientState clientState,
DalamudConfiguration configuration,
DalamudAssetManager dalamudAssetManager,
FontAtlasFactory fontAtlasFactory,
Framework framework,
GameGui gameGui,
TitleScreenMenu titleScreenMenu)
@ -65,7 +71,6 @@ internal class TitleScreenMenuWindow : Window, IDisposable
{
this.clientState = clientState;
this.configuration = configuration;
this.framework = framework;
this.gameGui = gameGui;
this.titleScreenMenu = titleScreenMenu;
@ -77,9 +82,25 @@ internal class TitleScreenMenuWindow : Window, IDisposable
this.PositionCondition = ImGuiCond.Always;
this.RespectCloseHotkey = false;
this.shadeTexture = new(() => dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.TitleScreenMenuShade));
this.privateAtlas = fontAtlasFactory.CreateFontAtlas(this.WindowName, FontAtlasAutoRebuildMode.Async);
this.scopedFinalizer.Add(this.privateAtlas);
this.myFontHandle = new(
() => this.scopedFinalizer.Add(
this.privateAtlas.NewDelegateFontHandle(
e => e.OnPreBuild(
toolkit => toolkit.AddDalamudDefaultFont(
TargetFontSizePx,
titleScreenMenu.Entries.SelectMany(x => x.Name).ToGlyphRange())))));
titleScreenMenu.EntryListChange += this.TitleScreenMenuEntryListChange;
this.scopedFinalizer.Add(() => titleScreenMenu.EntryListChange -= this.TitleScreenMenuEntryListChange);
this.shadeTexture = new(() => dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.TitleScreenMenuShade));
framework.Update += this.FrameworkOnUpdate;
this.scopedFinalizer.Add(() => framework.Update -= this.FrameworkOnUpdate);
}
private enum State
@ -94,6 +115,9 @@ internal class TitleScreenMenuWindow : Window, IDisposable
/// </summary>
public bool AllowDrawing { get; set; } = true;
/// <inheritdoc/>
public void Dispose() => this.scopedFinalizer.Dispose();
/// <inheritdoc/>
public override void PreDraw()
{
@ -109,12 +133,6 @@ internal class TitleScreenMenuWindow : Window, IDisposable
base.PostDraw();
}
/// <inheritdoc/>
public void Dispose()
{
this.framework.Update -= this.FrameworkOnUpdate;
}
/// <inheritdoc/>
public override void Draw()
{
@ -246,33 +264,12 @@ internal class TitleScreenMenuWindow : Window, IDisposable
break;
}
}
var srcText = entries.Select(e => e.Name).ToHashSet();
var keys = this.specialGlyphRequests.Keys.ToHashSet();
keys.RemoveWhere(x => srcText.Contains(x));
foreach (var key in keys)
{
this.specialGlyphRequests[key].Dispose();
this.specialGlyphRequests.Remove(key);
}
}
private bool DrawEntry(
TitleScreenMenuEntry entry, bool inhibitFadeout, bool showText, bool isFirst, bool overrideAlpha, bool interactable)
{
InterfaceManager.SpecialGlyphRequest fontHandle;
if (this.specialGlyphRequests.TryGetValue(entry.Name, out fontHandle) && fontHandle.Size != TargetFontSizePx)
{
fontHandle.Dispose();
this.specialGlyphRequests.Remove(entry.Name);
fontHandle = null;
}
if (fontHandle == null)
this.specialGlyphRequests[entry.Name] = fontHandle = Service<InterfaceManager>.Get().NewFontSizeRef(TargetFontSizePx, entry.Name);
ImGui.PushFont(fontHandle.Font);
ImGui.SetWindowFontScale(TargetFontSizePx / fontHandle.Size);
using var fontScopeDispose = this.myFontHandle.Value.Push();
var scale = ImGui.GetIO().FontGlobalScale;
@ -383,8 +380,6 @@ internal class TitleScreenMenuWindow : Window, IDisposable
initialCursor.Y += entry.Texture.Height * scale;
ImGui.SetCursorPos(initialCursor);
ImGui.PopFont();
return isHover;
}
@ -401,4 +396,6 @@ internal class TitleScreenMenuWindow : Window, IDisposable
if (charaMake != IntPtr.Zero || charaSelect != IntPtr.Zero || titleDcWorldMap != IntPtr.Zero)
this.IsOpen = false;
}
private void TitleScreenMenuEntryListChange() => this.privateAtlas.BuildFontsAsync();
}