Example #1
0
        public void TryMeasureCharacterBounds()
        {
            string text = "a b\nc";
            var    expectedGlyphMetrics = new GlyphMetric[] {
                new GlyphMetric('a', new FontRectangle(10, 0, 10, 10), false),
                new GlyphMetric(' ', new FontRectangle(40, 0, 30, 10), false),
                new GlyphMetric('b', new FontRectangle(70, 0, 10, 10), false),
                new GlyphMetric('\n', new FontRectangle(100, 0, 0, 10), true),
                new GlyphMetric('c', new FontRectangle(10, 30, 10, 10), false),
            };
            Font font = CreateFont(text);

            int scaleFactor = 72 * font.EmSize; // 72 * emSize means 1 point = 1px

            Assert.True(TextMeasurer.TryMeasureCharacterBounds(text.AsSpan(), new RendererOptions(font, 72 * font.EmSize), out GlyphMetric[] glyphMetrics));

            Assert.Equal(text.Length, glyphMetrics.Length);
            for (int i = 0; i < glyphMetrics.Length; i++)
            {
                GlyphMetric expected = expectedGlyphMetrics[i];
                GlyphMetric actual   = glyphMetrics[i];
                Assert.Equal(expected.Character, actual.Character);
                Assert.Equal(expected.IsControlCharacter, actual.IsControlCharacter);
                // 4 dp as there is minor offset difference in the float values
                Assert.Equal(expected.Bounds.X, actual.Bounds.X, 4);
                Assert.Equal(expected.Bounds.Y, actual.Bounds.Y, 4);
                Assert.Equal(expected.Bounds.Height, actual.Bounds.Height, 4);
                Assert.Equal(expected.Bounds.Width, actual.Bounds.Width, 4);
            }
        }
Example #2
0
        /// <summary>
        /// Measures the placement of each character in a rendered string of text.
        /// </summary>
        public static GlyphMetric[] MeasureCharacters(string text, Font font, PointF location)
        {
            var rendererOptions = new RendererOptions(font, location);

            TextMeasurer.TryMeasureCharacterBounds(text, rendererOptions, out var characterBounds);
            //font.Instance.
            return(characterBounds);
        }
Example #3
0
        /// <summary>
        /// Create a font atlas with all registered <see cref="FontAtlasEntry"/> instances.
        /// </summary>
        /// <param name="dpi">Dots per inch to use. Usually 96 (Windows) or 72 (MacOS).</param>
        public FontAtlas CreateAtlas(Vector2?dpi = null)
        {
            var dpiVal          = dpi ?? new Vector2(DefaultDpi);
            var characterSizes  = new Size[_atlasEntries.Count][];
            var characterRanges = new CharacterRange[_atlasEntries.Count][];
            var characterBounds = new Rectangle[_atlasEntries.Count][];
            var fonts           = new Font[_atlasEntries.Count];

            var packer = new MaxRectsBin(64, 64);

            // pad 1 pixel at all borders
            packer.PaddingWidth  = 1;
            packer.PaddingHeight = 1;

            var sb = new StringBuilder();

            for (var i = 0; i < _atlasEntries.Count; i++)
            {
                var e = _atlasEntries[i];

                characterSizes[i]  = new Size[e.GetCharacterCount()];
                characterRanges[i] = new CharacterRange[e.CharacterRanges.Count];

                Font font;
                if (e.IsSystemFont)
                {
                    font = SystemFonts.CreateFont(e.Font.FamilyName, e.Font.Size, e.Font.Style);
                }
                else
                {
                    font = _fontCollection.CreateFont(e.Font.FamilyName, e.Font.Size, e.Font.Style);
                }

                fonts[i] = font;

                var renderOptions  = new RendererOptions(font, dpiVal.X, dpiVal.Y);
                var characterIndex = 0;
                for (var characterRangeIndex = 0; characterRangeIndex < e.CharacterRanges.Count; characterRangeIndex++)
                {
                    // TODO reduce allocations
                    var cr = e.CharacterRanges[characterRangeIndex];
                    characterRanges[i][characterRangeIndex] = new CharacterRange(cr, characterIndex);

                    sb.Clear();
                    for (var c = cr.Start; c <= cr.End; c++)
                    {
                        sb.Append(char.ConvertFromUtf32(c));
                    }

                    if (!TextMeasurer.TryMeasureCharacterBounds(sb.ToString().AsSpan(), renderOptions, out var glyphMetrics))
                    {
                        continue;
                    }

                    foreach (var gm in glyphMetrics)
                    {
                        // we should round the size up so nothing gets lost
                        characterSizes[i][characterIndex] = new Size((int)(gm.Bounds.Width + .9999f), (int)(gm.Bounds.Height + .9999f));
                        characterIndex++;
                    }
                }
            }

            for (var i = 0; i < _atlasEntries.Count; i++)
            {
                characterBounds[i] = packer.Insert(characterSizes[i]);
            }

            var uw = (float)packer.UsedWidth;
            var uh = (float)packer.UsedHeight;

            var glyphMaps = new PixelGlyphMap[_atlasEntries.Count];

            for (var i = 0; i < _atlasEntries.Count; i++)
            {
                var glyphData = new GlyphData[characterSizes[i].Length];
                var gi        = 0;
                foreach (var cr in characterRanges[i])
                {
                    foreach (var c in cr)
                    {
                        var rect = characterBounds[i][gi];
                        glyphData[gi] = new GlyphData(c, rect);
                        gi++;
                    }
                }

                glyphMaps[i] = new PixelGlyphMap(fonts[i], characterRanges[i], glyphData);
            }

            return(new FontAtlas(packer.UsedWidth, packer.UsedHeight, dpiVal, glyphMaps));
        }