public DrawableFontAtlas GetAtlas(int fontSize, bool pixelFont = false) { int hash = HashCode.Combine(fontSize, GlyphRasterizer); // Check if the atlas is already loaded. bool found = _loadedAtlases.TryGetValue(hash, out DrawableFontAtlas atlas); if (found) { return(atlas); } lock (_loadedAtlases) { // Recheck as another thread could have built the atlas while waiting on lock. found = _loadedAtlases.TryGetValue(hash, out atlas); if (found) { return(atlas); } // Get atlas of specified type. switch (GlyphRasterizer) { case GlyphRasterizer.StbTrueType: atlas = new StbDrawableFontAtlas(Font, fontSize, pixelFont); break; case GlyphRasterizer.Emotion: atlas = new EmotionRendererDrawableFontAtlas(Font, fontSize, pixelFont); break; case GlyphRasterizer.EmotionSDFVer3: atlas = new EmotionSDF3DrawableFontAtlas(Font, fontSize, pixelFont); break; case GlyphRasterizer.EmotionSDFVer4: atlas = new EmotionSDFDrawableFontAtlas(Font, fontSize, pixelFont); break; default: return(null); } // Cache default ascii set atlas.CacheGlyphs(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"); _loadedAtlases.Add(hash, atlas); } return(atlas); }
public void VerifyFontRendering() { var fonts = new[] { "Junction-Bold.otf", // Cff "CaslonOS.otf", // Cff 2 (covers other cases) "1980XX.ttf", // Ttf "LatoWeb-Regular.ttf", // Composite "Junction-Bold.otf", // 14 font size "Junction-Bold.otf" // 11 font size }; var names = new[] { "Junction-Bold", "CaslonOS-Regular", "1980XX", "Lato Regular", "Junction-Bold", "Junction-Bold" }; var unitsPerEm = new[] { 1000, 1000, 1024, 2000, 1000, 1000 }; int[] descender = { -250, -360, -128, -426, -250, -250 }; var ascender = new[] { 750, 840, 682, 1974, 750, 750 }; var glyphs = new[] { 270, 279, 141, 2164, 270, 270 }; string[] cachedRender = { ResultDb.EmotionCffAtlas, "", ResultDb.EmotionTtAtlas, ResultDb.EmotionCompositeAtlas, "", "" }; int[] fontSizes = { 17, 17, 17, 17, 14, 11 }; FrameBuffer b = null; for (var i = 0; i < fonts.Length; i++) { Engine.Log.Info($"Running font {fonts[i]} ({i})...", TestRunnerLogger.TestRunnerSrc); ReadOnlyMemory <byte> data = Engine.AssetLoader.Get <OtherAsset>($"Fonts/{fonts[i]}")?.Content ?? ReadOnlyMemory <byte> .Empty; var f = new Font(data); // Verify basic font data. Assert.True(f.Valid); Assert.True(f.FullName == names[i]); Assert.True(f.UnitsPerEm == unitsPerEm[i]); Assert.True(f.Descender == descender[i]); Assert.True(f.Ascender == ascender[i]); Assert.True(f.CharToGlyph.Count == glyphs[i]); // Get atlases. int fontSize = fontSizes[i]; var emotionAtlas = new StbDrawableFontAtlas(f, fontSize, false); Runner.ExecuteAsLoop(_ => { var str = ""; for (uint j = emotionAtlas.Font.FirstCharIndex; j < emotionAtlas.Font.LastCharIndex; j++) { str += (char)j; } emotionAtlas.CacheGlyphs(str); }).WaitOne(); DrawableFontAtlas packedStbAtlas = RenderFontStbPacked(data.ToArray(), fontSize, emotionAtlas.Texture.Size * 3, (int)f.LastCharIndex + 1, f, out StbTrueType.stbtt_fontinfo stbFont); // Compare glyph parsing. CompareMetricsWithStb(f, emotionAtlas, stbFont); // Compare render metrics. foreach (KeyValuePair <char, DrawableGlyph> g in emotionAtlas.Glyphs) { DrawableGlyph glyph = packedStbAtlas.Glyphs[g.Key]; Assert.Equal(glyph.XAdvance, g.Value.XAdvance); var fontGlyph = g.Value.FontGlyph; int width = (int)(MathF.Ceiling(fontGlyph.Max.X * emotionAtlas.RenderScale) - MathF.Floor(fontGlyph.Min.X * emotionAtlas.RenderScale)); int height = (int)(MathF.Ceiling(-fontGlyph.Min.Y * emotionAtlas.RenderScale) - MathF.Floor(-fontGlyph.Max.Y * emotionAtlas.RenderScale)); Assert.Equal(glyph.Width, width); Assert.Equal(glyph.Height, height); } // Check if there's a verified render. if (string.IsNullOrEmpty(cachedRender[i])) { continue; } // Compare with cached render. // ReSharper disable AccessToModifiedClosure Runner.ExecuteAsLoop(_ => { if (b == null) { b = new FrameBuffer(emotionAtlas.Texture.Size).WithColor(); } else { b.Resize(emotionAtlas.Texture.Size, true); } RenderComposer composer = Engine.Renderer.StartFrame(); composer.RenderToAndClear(b); composer.RenderSprite(Vector3.Zero, emotionAtlas.Texture.Size, Color.White, emotionAtlas.Texture); composer.RenderTo(null); Engine.Renderer.EndFrame(); Runner.VerifyScreenshot(cachedRender[i], b); }).WaitOne(); } }