question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[API Proposal]: Add ReadOnlySpan<char> overloads to System.Drawing.Graphics

See original GitHub issue

Background and motivation

When drawing strings manually in WinForms, new text is frequently created. Since the methods System.Drawing.Graphics and System.Drawing.GraphicsPath only accept strings String allocations occur frequently.

It would be better to add an overload that accepts ReadOnlySpan<char> so that the buffer can be passed directly.

API Proposal

namespace System.Drawing
{
    public sealed partial class Graphics : System.MarshalByRefObject, System.Drawing.IDeviceContext, System.IDisposable
    {
        // Existing:
        public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point) { }
        public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point, System.Drawing.StringFormat? format) { }
        public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle) { }
        public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle, System.Drawing.StringFormat? format) { }
        public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y) { }
        public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y, System.Drawing.StringFormat? format) { }
        public System.Drawing.Region[] MeasureCharacterRanges(string? text, System.Drawing.Font font, System.Drawing.RectangleF layoutRect, System.Drawing.StringFormat? stringFormat) { throw null; }
        public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font) { throw null; }
        public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, System.Drawing.PointF origin, System.Drawing.StringFormat? stringFormat) { throw null; }
        public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, System.Drawing.SizeF layoutArea) { throw null; }
        public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, System.Drawing.SizeF layoutArea, System.Drawing.StringFormat? stringFormat) { throw null; }
        public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, System.Drawing.SizeF layoutArea, System.Drawing.StringFormat? stringFormat, out int charactersFitted, out int linesFilled) { throw null; }
        public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, int width) { throw null; }
        public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, int width, System.Drawing.StringFormat? format) { throw null; }

        // Proposal
        public void DrawString(ReadOnlySpan<char> s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point) { }
        public void DrawString(ReadOnlySpan<char> s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point, System.Drawing.StringFormat? format) { }
        public void DrawString(ReadOnlySpan<char> s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle) { }
        public void DrawString(ReadOnlySpan<char> s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle, System.Drawing.StringFormat? format) { }
        public void DrawString(ReadOnlySpan<char> s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y) { }
        public void DrawString(ReadOnlySpan<char> s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y, System.Drawing.StringFormat? format) { }
        public System.Drawing.Region[] MeasureCharacterRanges(ReadOnlySpan<char> text, System.Drawing.Font font, System.Drawing.RectangleF layoutRect, System.Drawing.StringFormat? stringFormat) { throw null; }
        public System.Drawing.SizeF MeasureString(ReadOnlySpan<char> text, System.Drawing.Font font) { throw null; }
        public System.Drawing.SizeF MeasureString(ReadOnlySpan<char> text, System.Drawing.Font font, System.Drawing.PointF origin, System.Drawing.StringFormat? stringFormat) { throw null; }
        public System.Drawing.SizeF MeasureString(ReadOnlySpan<char> text, System.Drawing.Font font, System.Drawing.SizeF layoutArea) { throw null; }
        public System.Drawing.SizeF MeasureString(ReadOnlySpan<char> text, System.Drawing.Font font, System.Drawing.SizeF layoutArea, System.Drawing.StringFormat? stringFormat) { throw null; }
        public System.Drawing.SizeF MeasureString(ReadOnlySpan<char> text, System.Drawing.Font font, System.Drawing.SizeF layoutArea, System.Drawing.StringFormat? stringFormat, out int charactersFitted, out int linesFilled) { throw null; }
        public System.Drawing.SizeF MeasureString(ReadOnlySpan<char> text, System.Drawing.Font font, int width) { throw null; }
        public System.Drawing.SizeF MeasureString(ReadOnlySpan<char> text, System.Drawing.Font font, int width, System.Drawing.StringFormat? format) { throw null; }
    }

    public sealed partial class GraphicsPath : System.MarshalByRefObject, System.ICloneable, System.IDisposable
    {
        // Existing:
        public void AddString(string s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.Point origin, System.Drawing.StringFormat? format) { }
        public void AddString(string s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.PointF origin, System.Drawing.StringFormat? format) { }
        public void AddString(string s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.Rectangle layoutRect, System.Drawing.StringFormat? format) { }
        public void AddString(string s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.RectangleF layoutRect, System.Drawing.StringFormat? format) { }

        // Proposal
        public void AddString(ReadOnlySpan<char> s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.Point origin, System.Drawing.StringFormat? format) { }
        public void AddString(ReadOnlySpan<char> s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.PointF origin, System.Drawing.StringFormat? format) { }
        public void AddString(ReadOnlySpan<char> s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.Rectangle layoutRect, System.Drawing.StringFormat? format) { }
        public void AddString(ReadOnlySpan<char> s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.RectangleF layoutRect, System.Drawing.StringFormat? format) { }
    }
}

API Usage

It is possible to pass buffers on the stack.

GraphicsPath gp = new GraphicsPath();
...
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    var g = e.Graphics;
    Span<char> buf = stackalloc char[512];
    DateTime.Now.TryFormat(buf, out var writtern);
    g.DrawString(buf, ...);

    gp.AddString(buf, ...);
}

Alternative Designs

I can’t think of any.

Risks

I can’t think of any.

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:4
  • Comments:9 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
JeremyKuhnecommented, Jun 2, 2022

Seems rational. Would just need to double check that the underlying GDI+ APIs all take a length (and not a null-terminated string). I believe that is the case…

0reactions
Philip-Wang01commented, Mar 30, 2023

Verified this on .NET 8.0 latest build: 8.0.100-preview.4.23179.4, has been added ReadOnlySpan<char> overloads to Graphics.DrawString. image

Read more comments on GitHub >

github_iconTop Results From Across the Web

TextRenderer.MeasureText Method (System.Windows. ...
You can manipulate how the text is drawn by using one of the DrawText(IDeviceContext, ReadOnlySpan<Char>, Font, Rectangle, Color, TextFormatFlags) overloads ...
Read more >
CA1846: Prefer AsSpan over Substring - .NET
Many APIs that accept strings also have overloads that accept a ReadOnlySpan<System.Char> argument. When such overloads are available, ...
Read more >
Basics & Requirements
First, add a using directive for the underlying subsystem you want to use. For example, you probably want to use one or more...
Read more >
https://raw.githubusercontent.com/dotnet/samples/m...
Drawing System.Drawing.Common needs netfx and netstandard configurations Right now it has only ... Proposed Api Adds extra .ctor overload + param to ...
Read more >
Is there a way to use the ReadOnlySpan<char> overloads ...
I have a NetStandard2.0 class library. I have imported the System.Memory nuget package, which means I can use Span<T> inside it.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found