Properly Draw Text Using Graphicspath

Properly draw text using GraphicsPath

Seems you are providing wrong measure for font size in the first place and then adding extra thickness to the brush. Try this instead:

using (GraphicsPath path = new GraphicsPath())
{
path.AddString(
text,
_fontStyle.FontFamily,
(int)_fontStyle.Style,
e.Graphics.DpiY * fontSize / 72f, // em size
new Point(0, 0), // location where to draw text
string_format);

e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.CompositingMode = CompositingMode.SourceOver;
e.Graphics.DrawPath(new Pen(Color.Red), path);
}

Get the position where drawn text is truncated by GraphicsPath.DrawString

Given the FillTextSolid() method shown before in:

Graphics DrawPath produces unexpected results when rendering text

Private Sub FillTextSolid(g As Graphics, bounds As RectangleF, text As String, font As Font, fillColor As Color)
Using gp As GraphicsPath = New GraphicsPath(),
brush As New SolidBrush(fillColor),
format = New StringFormat(StringFormat.GenericTypographic)
format.Trimming = StringTrimming.EllipsisWord
gp.AddString(text, font.FontFamily, font.Style, font.Size, bounds, StringFormat.GenericTypographic)
g.FillPath(brush, gp)
Dim lastCharPosition = GetPathLastCharPosition(g, format, gp, bounds, text, font)
End Using
End Sub

you can use the current GraphicsPath, Rectangle bounds, Font size and style used for drawing the the text in a graphics context, to calculate the position of the last character drawn and, as a consequence, the last word, if necessary.

I've added in FillTextSolid() a call to the GetPathLastCharPosition() method, which is responsible for the calculation. Pass to the method the objects described, as they're currently configured (these settings can of course change at any time: see the animation at the bottom).

Dim [Last Char Position] = GetPathLastCharPosition(
[Graphics],
[StringFormat],
[GraphicsPath],
[RectangleF],
[String],
[Font]
)

To determine the current last word printed using a GraphicsPath object, you cannot split the string in parts separated by, e.g., a white space, since each char is part of the rendering.

Also to note: for the measure to work as intended, you cannot set the drawing Font size in Points, the Font size must be expressed in pixels.

You could also use Point units, but the GraphicsPath class, when Points are specified, generates (correctly) a Font measure in EMs - considering the Font Cell Ascent and Descent - which is not the same as the Font.Height.

You can of course convert the measure from Ems to Pixels, but it just adds complexity for no good reason (in the context of this question, at least).

See a description of these details and how to calculate the GraphicsPath EMs in:

Properly draw text using GraphicsPath

GetPathLastCharPosition() uses Graphics.MeasureCharacterRanges to measure the bounding rectangle of each char in the Text string, in chunks of 32 chars per iteration. This is because StringFormat.SetMeasurableCharacterRanges only takes a maximum of 32 CharacterRange elements.

So, we take the Text in chunks of 32 chars, get the bounding Region of each and verify whether the Region contains the last Point in the GraphicsPath.

The last Point generated by a GraphicsPath is returned by the GraphicsPath.GetLastPoint().

  • This procedure only considers text drawn from top to bottom and left to right.

    It can be adapted to handle right to left languages.

Of course, you could also ignore the last point and just consider whether a Region bounds fall outside the bounding rectangle of the canvas.

Anyway, when the a Region that contains the last point is found, the method stops and returns the position of the last character in the string that is part of the drawing.

Private Function GetPathLastCharPosition(g As Graphics, format As StringFormat, path As GraphicsPath, bounds As RectangleF, text As String, font As Font) As Integer
Dim textLength As Integer = text.Length
Dim p = path.GetLastPoint()
bounds.Height += font.Height

For charPos As Integer = 0 To text.Length - 1 Step 32
Dim count As Integer = Math.Min(textLength - charPos, 32)
Dim charRanges = Enumerable.Range(charPos, count).Select(Function(c) New CharacterRange(c, 1)).ToArray()

format.SetMeasurableCharacterRanges(charRanges)
Dim regions As Region() = g.MeasureCharacterRanges(text, font, bounds, format)

For r As Integer = 0 To regions.Length - 1
If regions(r).IsVisible(p.X, p.Y) Then
Return charRanges(r).First
End If
Next
Next
Return -1
End Function

This is how it works:

GraphicsPath last draw char

C# version of the method:

private int GetPathLastCharPosition(Graphics g, StringFormat format, GraphicsPath path, RectangleF bounds, string text, Font font)
{
int textLength = text.Length;
var p = path.GetLastPoint();
bounds.Height += font.Height;

for (int charPos = 0; charPos < text.Length; charPos += 32) {
int count = Math.Min(textLength - charPos, 32);
var charRanges = Enumerable.Range(charPos, count).Select(c => new CharacterRange(c, 1)).ToArray();

format.SetMeasurableCharacterRanges(charRanges);
Region[] regions = g.MeasureCharacterRanges(text, font, bounds, format);

for (int r = 0; r < regions.Length; r++) {
if (regions[r].IsVisible(p.X, p.Y)) {
return charRanges[r].First;
}
}
}
return -1;
}

Graphics DrawPath produces unexpected results when rendering text

A few notes on the matter:

When you draw the outline of a shape, GraphicsPath adds the outline, using the width of the Pen specified, to the outside of the shape.

The outline begins at the external margin of the shape and proceeds outwards.

This applies while the Pen width specified is withing twice the minimum dimension of the shape.

When the Pen is wider than this measure, the Pen width is clipped and moved towards the outside by a measure equal to the difference between the width specified and the shape min dimension x2.

E.g., if you have a shape (say, an ellipse) with a size of 20x30, the Pen width can reach 40.0f before it's moved to the outside of the shorter dimension.

The size of the ellipse in the image below is (20x30).

The outline Pen is set, respectively, to 40.0f, 46.0f, 52.0f and 65.0f:

GraphicsPath Outline Pen

This is what happens when you add your text to a GraphicsPath and it's converted to curves. The shapes parts that have a size smaller than the Pen width, when outlined, cause the outline to move to the outside. You can see it first in all dots in the text.

You can solve the problem (just a perceived problem, what you see is actually what you asked to draw), you can widen the Path, so it will expand in-wards, not just out-wards.

You can widen the Path using its Widen() method, specifying a Pen used to expand the Path (a Pen width of 1.0f is enough).

Define the drawing properties:

Imports System.Drawing
Imports System.Drawing.Drawing2D

Private drawText As String = "Lorem Ipsum is simply dummy text of the printing and typesetting industry"
Private drawFont As New Font("Times New Roman", 60, FontStyle.Regular)
Private textBounds As RectangleF = RectangleF.Empty
Private outlineWidth As Single = 20.0F
Private outlineColor As Color = Color.Black
Private fillColor As Color = Color.Yellow

Draw the outline first, inside the specified bounds, then fill the Path using the same exact bounds and StringFormat properties.

Here, I'm using StringFormat.GenericTypographic: it's a pre-set of some standard settings that is often used as base for drawing. You can initialize this pre-set, then add/change specific properties when needed (e.g, you need to center the text vertically or horizontally or both).

See also the notes in:

Properly draw text using GraphicsPath

Flip the GraphicsPath that draws the text/string

I'm using a PictureBox as canvas here. It's the same thing (but it's double-buffered by default).

Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
' Add composition if you have a composited background
'e.Graphics.CompositingQuality = CompositingQuality.HighQuality

textBounds = RectangleF.Inflate(PictureBox1.ClientRectangle, -5, -5)
textBounds.Offset(10, 10)

DrawTextOutline(e.Graphics, textBounds, drawText, drawFont, outlineColor, outlineWidth, LineCap.Round)
FillTextSolid(e.Graphics, textBounds, drawText, drawFont, fillColor)
End Sub

Private Sub DrawTextOutline(g As Graphics, bounds As RectangleF, text As String, font As Font, penColor As Color, penSize As Single, cap As LineCap)
Dim lJoin As LineJoin = If(cap = LineCap.Round, LineJoin.Round, LineJoin.Bevel)
Using gp As GraphicsPath = New GraphicsPath(FillMode.Winding),
pen As New Pen(penColor, penSize) With {.LineJoin = lJoin, .StartCap = cap, .EndCap = cap},
widenPen As New Pen(Color.Black, 1.0F)
gp.AddString(text, font.FontFamily, font.Style, font.Size, bounds, StringFormat.GenericTypographic)
gp.Widen(widenPen)
g.DrawPath(pen, gp)
End Using
End Sub

Private Sub FillTextSolid(g As Graphics, bounds As RectangleF, text As String, font As Font, fillColor As Color)
Using gp As GraphicsPath = New GraphicsPath(),
brush As New SolidBrush(fillColor)
gp.AddString(text, font.FontFamily, font.Style, font.Size, bounds, StringFormat.GenericTypographic)
g.FillPath(brush, gp)
End Using
End Sub

Sample results:

GraphicsPath Outline results


C# Version:

General settings:

using System.Drawing;
using System.Drawing.Drawing2D;

private static string drawText = "Lorem Ipsum is simply dummy text of the printing and typesetting industry";
private static Font drawFont = new Font("Times New Roman", 60, FontStyle.Regular);
private static RectangleF textBounds = RectangleF.Empty;
private static float outlineWidth = 20f;
private static Color outlineColor = Color.Black;
private static Color fillColor = Color.Yellow;

Drawing methods:

private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
textBounds = RectangleF.Inflate(pictureBox1.ClientRectangle, -5, -5);
textBounds.Offset(10, 10);

DrawTextOutline(e.Graphics, textBounds, drawText, drawFont, outlineColor, outlineWidth, LineCap.Round);
FillTextSolid(e.Graphics, textBounds, drawText, drawFont, fillColor);
}

private void DrawTextOutline(Graphics g, RectangleF bounds, string text, Font font, Color penColor, float penSize, LineCap cap)
{
LineJoin lJoin = cap == LineCap.Round ? LineJoin.Round : LineJoin.Bevel;
using (var gp = new GraphicsPath(FillMode.Winding))
using (Pen pen = new Pen(penColor, penSize) { LineJoin = lJoin, StartCap = cap, EndCap = cap })
using (Pen widenPen = new Pen(Color.Black, 1.0f)) {
gp.AddString(text, font.FontFamily, (int)font.Style, font.Size, bounds, StringFormat.GenericTypographic);
gp.Widen(widenPen);
g.DrawPath(pen, gp);
};
}

private void FillTextSolid(Graphics g, RectangleF bounds, string text, Font font, Color fillColor)
{
using (var gp = new GraphicsPath())
using (var brush = new SolidBrush(fillColor)) {
gp.AddString(text, font.FontFamily, (int)font.Style, font.Size, bounds, StringFormat.GenericTypographic);
g.FillPath(brush, gp);
}
}

How to draw a shape using GraphicsPath to create the Region of a Custom Control?

A few pointers and an example on the use of Regions to define the visible area of a Custom Control that represents a non rectangular shape.

  • You're converting most floating point values to integer values: don't do that when drawing, unless you have no other immediate choice. Drawing requires floating point measures (float) most of the time. Always to produce correct calculations.

    For example: Rotate Point around pivot Point repeatedly, to see the difference.

  • You're using what appear to be fixed measures of some type, not defined in the OP and apparently never modified: DEFAULT_WIDTH and DEFAULT_HEIGHT. Since Controls can be resized at any time, both at Design-Time and Run-Time, using fixed measures is not really useful (assuming this is what these values represent). In any case, you need to use the current client area of your Control as the main reference: this value is returned by the Control.ClientRectangle property.

  • The Region of a Control is not set in the OnPaint() override, but either in the OnResize() or OnLayout() override, depending on the functionality of the Control you're building.

    Setting properties, as FlatStyle = FlatStyle.Flat; that you have there (are you deriving your Control from a Label?), also does not belong in the drawing procedures: you may generate cascading events, causing the Control to constantly repaint itself (until it crashes, that is).

  • Using a GraphicsPath, the Pen.Alignment property is not exactly useful. See also the Remarks section in the documentation.


When you set the Region of a Control, to modify its shape, you need to consider that the Region doesn't support anti-aliasing, hence you cannot draw along the borders it creates. You need to deflate the drawing area, so you always draw inside the Region you have defined. Or, you can create a completely transparent / translucent Control and draw whatever you need (shapes and/or Bitmaps) inside the transparent area. But this is a different matter, let's stick to the Region and the GraphicsPath that creates it.

In the sample code, the GetRegionPath() method generates a GraphicsPath object, created passing PointF coordinates that defines a shape, then building the shape using the AddLines() method.

The Custom Control show here, uses SetStyle() to set in its Constructor:

SetStyle(ControlStyles.OptimizedDoubleBuffer | 
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw, true);

This enables double-buffering and causes the Control to repaint when it's resized.

The Region is then reset in the OnResize() override.

In OnPaint(), the GetRegionPath() method is called again to get a GraphicsPath object, based on the current client area.

It then scales the GraphicsPath using a simple Matrix:

(see a description of the Matrix functionality in Flip the GraphicsPath that draws the text/string)

float scaleX = 1.0f - ((border * 2.5f) / rect.Width);
float scaleY = 1.0f - ((border * 2.0f) / rect.Height);
var mx = new Matrix(scaleX, 0, 0, scaleY, border, border);
[GraphicsPath].Transform(mx);

This scales the GraphicsPath, based on the size of the Border (values above 1.0f scale up, values below it scale down).

It's then moved (translated) right and down by the measure of the Border.

If the Border is not set, the GraphicsPath is not scaled or moved: e.g.

 1.0f + (((border * 2.5f) / rect.Width)) = 1.0f + 0.0f

This allows to draw the shape and its Border, if any, all inside the Region.

In this case, anti-aliasing can be applied and the borders of the shape appear smooth.

This is how it looks like, at Design-Time:

Region Control Design-time

And at Run-Time:

Region Control Run-time


See also:

How to avoid visual artifacts of colored border of zoomable UserControl with rounded corners?

How can I draw a rounded rectangle as the border for a rounded Form?

A shadow created with PathGradientBrush shows undesired thorn results


Custom Control:

using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

[ToolboxItem(true)]
[DesignerCategory("code")]
public class NavigationShape : Control
{
private Color m_ArrowColor = Color.SteelBlue;
private Color m_BorderColor = Color.Orange;
private float m_BorderSize = 1.5f;
private bool m_BorderVisible = true;

public NavigationShape() {
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw, true);
MinimumSize = new Size(40, 20);
}

protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Region = new Region(GetRegionPath());
}

private GraphicsPath GetRegionPath()
{
// The arrow shape begins at 3/4 or the current width of the container
float arrowSection = ClientSize.Width * .75f;
PointF[] arrowPoints = new PointF[] {
new PointF (0, 0),
new PointF (arrowSection, 0),
new PointF(ClientSize.Width, ClientSize.Height / 2.0f),
new PointF (arrowSection, ClientSize.Height),
new PointF (0, ClientSize.Height),
new PointF (0, 0)
};
var path = new GraphicsPath();
path.AddLines(arrowPoints);
path.CloseFigure();
return path;
}

protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
float border = m_BorderVisible ? m_BorderSize : .5f;

using (var path = GetRegionPath()) {
var rect = path.GetBounds();
float scaleX = 1.0f - ((border * 2.5f) / rect.Width);
float scaleY = 1.0f - ((border * 2.0f) / rect.Height);

using (var mx = new Matrix(scaleX, 0, 0, scaleY, border, border))
using (var brush = new SolidBrush(m_ArrowColor)) {
path.Transform(mx);
e.Graphics.FillPath(brush, path);
if (m_BorderVisible) {
using (Pen pen = new Pen(m_BorderColor, m_BorderSize)) {
e.Graphics.DrawPath(pen, path);
}
}
}
}
base.OnPaint(e);
}

[DefaultValue(typeof(Color), "SteelBlue")]
[Description("Color of the shape")]
public Color ArrowColor {
get => m_ArrowColor;
set {
if (m_ArrowColor != value) {
m_ArrowColor = value;
Invalidate();
}
}
}

[DefaultValue(true)]
[Description("Show or hide the Border")]
public bool BorderVisible {
get => m_BorderVisible;
set {
m_BorderVisible = value;
Invalidate();
}
}

[DefaultValue(typeof(Color), "Orange")]
[Description("Color of the Border")]
public Color BorderColor {
get => m_BorderColor;
set {
if (m_BorderColor != value) {
m_BorderColor = value;
Invalidate();
}
}
}

[DefaultValue(1.5f)]
[Description("Size of the Border [1.0, 6.0]")]
public float BorderSize {
get => m_BorderSize;
set {
if (m_BorderSize != value) {
m_BorderSize = Math.Max(Math.Min(value, 6.0f), 1.0f);
Invalidate();
}
}
}

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public BorderStyle BorderStyle{ get; set; } // Implement if needed
}

Flip the GraphicsPath that draws the text/string

There is a simpler way to flip a Graphics object.

Create a Matrix which is the result of the Matrix multiplication of all the transformations that need to be applied to the specified object.

A Matrix transformation can be applied to either the GraphicsPath object or the Graphics object. Or both, when multiple transformation need to be performed sequentially.

The .Net System.Drawing.Drawing2D Matrix class does not have a pre-built Flip (mirroring) transformation, but this Matrix structure is already well known (I'm not sure this is the reason why there isn't a specific method in the Matrix class):

| 1 | 0 | 0 |       |-1 | 0 | 0 |       | 1 | 0 | 0 |
| 0 | 1 | 0 | | 0 | 1 | 0 | | 0 |-1 | 0 |
| 0 | 0 | 1 | | 0 | 0 | 1 | | 0 | 0 | 1 |

Identity Mirror X-Axis Mirror Y-Axis
Matrix Matrix Matrix

You can notice (it's also reported in the Docs) that the 3rd column is always the same, therefore, when building a new Matrix, the 3rd column values are implied and are provided by the Matrix class initialization, so we specify just the elements in the first 2 columns.

Important note, straight from the Matrix class Docs:

Caution:
The order of a composite transformation is important. In general, rotate, then scale, then translate is not the same as
scale, then rotate, then translate. Similarly, the order of matrix
multiplication is important. In general, ABC is not the same as BAC

An example of a string drawn on a Panel using the GraphicsPath.AddString() method.

Two Matrix transformations are added to the GraphicsPath object:

a Flip-X and a Flip-Y, which are combined using the Matrix.Multiply() method:

The Flip-X and Flip-Y Matrices are built including the X and Y translations, applied to the 3rd row of each Matrix.
The translation values are determined by the Canvas dimensions.

For example, the Flip-X Matrix:

With a [Canvas].Width = 100 =>:

Rotation Element : Rotate the X-Axis 180° (-1 radians) on the origin Point(0, 0).

Translate Element: Translate the X Position 100 Graphics Units to the right (positive value).

| -1  |  0  |  0  |
| 0 | 1 | 0 |
| 100 | 0 | 1 |

Mirror X-Axis
Translate X +100
Matrix

A visual representation of the effect.

The Controls referenced in the code are the same you can see here (if you need to reproduce it).

Matrix Flip

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;

bool flipX = false;
bool flipY = false;
bool outlined = false;
float sampleFontEmSize = 28f;
string sampleText = "Sample Text to Flip";
FontFamily sampleFont = new FontFamily("Segoe UI");

private void panel1_Paint(object sender, PaintEventArgs e)
{
Panel panel = sender as Panel;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

using (var path = new GraphicsPath())
using (var format = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.NoWrap))
{
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
path.AddString(sampleText, sampleFont, (int)FontStyle.Regular, sampleFontEmSize, panel.ClientRectangle, format);

using (var flipXMatrix = new Matrix(-1, 0, 0, 1, panel.Width, 0))
using (var flipYMatrix = new Matrix(1, 0, 0, -1, 0, panel.Height))
using (var transformMatrix = new Matrix())
{
if (flipX) {
transformMatrix.Multiply(flipXMatrix);
}
if (flipY) {
transformMatrix.Multiply(flipYMatrix);
}
path.Transform(transformMatrix);
//Or e.Graphics.Transform = TransformMatrix;

if (outlined) {
e.Graphics.DrawPath(Pens.LawnGreen, path);
}
else {
e.Graphics.FillPath(Brushes.Orange, path);
}
}
}
}

private void btnFlipX_Click(object sender, EventArgs e)
{
flipX = !flipX;
panel1.Invalidate();
}

private void btnFlipY_Click(object sender, EventArgs e)
{
flipY = !flipY;
panel1.Invalidate();
}

private void chkOutlined_CheckedChanged(object sender, EventArgs e)
{
outlined = chkOutlined.Checked;
panel1.Invalidate();
}

Remove top and bottom padding from Text drawn on an Image

I propose you a slightly different method, using the GraphicsPath class to both measure and draw the text on a Bitmap object.

The advantage is that the GraphicsPath class reports the actual coordinates where the object that it references will be drawn and also the size of the text in relation to a specific Font.

These measures are returned in a RectagleF structure using the GraphicsPath.GetBounds() method.

The base constructor assumes a Pen size of 1 pixel.

There's only one (small) detail to take care of: the GDI+ Bitmap object accepts dimensions expressed in integer values only, while all the other measures are expressed in floating point values.

We need to compensate for the rounding, but it's usually just ± 1 pixel.

Sample of the results:

GraphicsPath GetBounds text measure

A description of the procedure:

  • Define a Font Family and Size
  • Add the Text string to the GraphicsPath object
  • Get the GraphicsPath bounding rectangle of the text object
  • Build a Bitmap object using the bounding rectangle Size
  • Move the World coordinates, with Graphics.TranslateTransform, to the coordinates defined by the bounding rectangle Y position and the Pen Size, using their negative value: we need to move backwards that measure.
  • Draw the Text

See also these notes about GraphicsPath and Fonts:

Properly draw text using Graphics Path

Sample code:

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;

string text = "This is my Text";
Font font = new Font("Arial", 52, FontStyle.Regular, GraphicsUnit.Point);
float penSize = 1f;

using (var path = new GraphicsPath()) {
path.AddString(text, font.FontFamily, (int)font.Style, font.Size, Point.Empty, StringFormat.GenericTypographic);

RectangleF textBounds = path.GetBounds();

using (var bitmap = new Bitmap((int)textBounds.Width, (int)textBounds.Height, PixelFormat.Format32bppArgb))
using (var g = Graphics.FromImage(bitmap))
using (var brush = new SolidBrush(Color.LightGreen)) {
g.SmoothingMode = SmoothingMode.AntiAlias;
// When rendering without a GraphicsPath object
//g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
g.Clear(Color.Black);
g.TranslateTransform(-(textBounds.X + penSize), -(textBounds.Y + penSize));
g.FillPath(brush, path);

bitmap.Save("[Image Path]", ImageFormat.Png);
// Or: return (Bitmap)bitmap.Clone();
// Or: return bitmap; declaring the bitmap without a using statement
}
}

Is it possible to fill a letter with the Graphics Class in c#?

I finally found a solution, thanks to a blogpost found Here!

Instead of creating a bitmap, I create, as suggeste in the blog, a GraphicsPathIterator.

Instead of only creating the path and fill the path (as the code said in the question), I now add the GraphicsPathIterator.

GraphicsPath p = new GraphicsPath();
p.AddString(MarkerSymbol, markerFont.FontFamily, (int)markerFont.Style, symbolSize.Height * 0.9f, CorrectedSymbolLocation, stringFormat);

var iter = new GraphicsPathIterator(p);
while (true)
{
var subPath = new GraphicsPath();
bool isClosed;
if (iter.NextSubpath(subPath, out isClosed) == 0) break;
Region region = new Region(subPath);
graphics.FillRegion(new SolidBrush(Color.White), region);
}
graphics.FillPath(brush, p);


Related Topics



Leave a reply



Submit