Skip to content

[BitmapFont] Reconsider what we are going to do about word wrapping #318

Description

I've removed the word wrapping overloads from the DrawString methods in the BitmapFontExtensions class.

There's a few key reasons I decided to remove the word wrapping overloads:

  1. Without word wrapping the BitmapFont class has a very clear scope. Parity with SpriteFont.

  2. The wrapping code is quite complex and as a result caused a number of bugs (e.g. MeasureString wasn't taking into account wrapped lines).

  3. Splitting strings into words and lines over and over again during rendering causes a lot of garbage collection. There are other ways to deal with this problem, but they make it even more clear it shouldn't be the responsibility of the BitmapFont class.

For reference (and for anyone still wanting to use the old code), here's a copy of the method before I started messing with it. Calls to DrawInternal should be replaceable by calls to DrawString with care.

    private static void DrawWrapped(this SpriteBatch spriteBatch, BitmapFont font, string text, Vector2 position,
        Color color, int wrapWidth, float layerDepth)
    {
        HandleNullParameters(font, text);

        if (wrapWidth == int.MaxValue)
        {
            DrawInternal(spriteBatch, font, text, position, color, layerDepth);
            return;
        }

        // parse the text and wrap it at the specified width
        var dx = position.X;
        var dy = position.Y;
        var sentences = text.Split(new[] {'\n'}, StringSplitOptions.None);

        foreach (var sentence in sentences)
        {
            var words = sentence.Split(new[] {' '}, StringSplitOptions.None);

            for (var i = 0; i < words.Length; i++)
            {
                var word = words[i];
                var size = font.GetStringRectangle(word, Vector2.Zero);

                if ((i != 0) && (dx + size.Width >= wrapWidth))
                {
                    dy += font.LineHeight;
                    dx = position.X;
                }

                DrawInternal(spriteBatch, font, word, new Vector2(dx, dy), color, layerDepth);
                dx += size.Width;

                var spaceCharRegion = font.GetCharacterRegion(' ');
                if (i != words.Length - 1)
                    dx += spaceCharRegion.XAdvance + font.LetterSpacing;
                else
                    dx += spaceCharRegion.XOffset + spaceCharRegion.Width;
            }

            dx = position.X;
            dy += font.LineHeight;
        }
    }

Metadata

Metadata

Assignees

Labels

enhancementImprovement to an existing feature: better defaults, expanded support, or quality-of-lifescope: renderingSpriteBatch, PrimitiveBatch/VectorDraw, effects/shaders, BitmapFonts rendering, draw order

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions