StealthRT StealthRT - 6 months ago 29
Vb.net Question

Modifying drawn image as a rounded rectangle

Following the code here I am trying to curve the edges of the rectangle so that its not all square.

An example of what it looks like now:

enter image description here

And what I am looking for it to do:

enter image description here

Using this code:

Using br As New SolidBrush(solidBGColor)
Dim r As New RectangleF(0, 0, myPictureBox.Width, myPictureBox.Height)
Dim gp As New System.Drawing.Drawing2D.GraphicsPath()
Dim d As Integer = 5

gp.AddArc(r.X, r.Y, imgSizeWH(0), imgSizeWH(1), 180, 90)
gp.AddArc(r.X + r.Width - d, r.Y, imgSizeWH(0), imgSizeWH(1), 270, 90)
gp.AddArc(r.X + r.Width - d, r.Y + r.Height - d, imgSizeWH(0), imgSizeWH(1), 0, 90)
gp.AddArc(r.X, r.Y + r.Height - d, imgSizeWH(0), imgSizeWH(1), 90, 90)

g.FillPath(br, gp)
End Using


I have an image that just doesn't seem correct:

enter image description here

Full code:

Private Function CreateLabeledAvatar(av As Image, text As String) As Image
Dim imgSizeWH() As Integer = {800, 800}
Dim bmp As New Bitmap(imgSizeWH(0), imgSizeWH(1))
Dim solidBGColor As Color = DirectCast(New ColorConverter().ConvertFromString("#" + _BackgroundColours(New Random().[Next](0, _BackgroundColours.Count - 1))), Color)

Using g As Graphics = Graphics.FromImage(bmp)
Using br As New SolidBrush(solidBGColor)
Dim r As New RectangleF(0, 0, myPictureBox.Width, myPictureBox.Height)
Dim gp As New System.Drawing.Drawing2D.GraphicsPath()
Dim d As Integer = 5

gp.AddArc(r.X, r.Y, imgSizeWH(0), imgSizeWH(1), 180, 90)
gp.AddArc(r.X + r.Width - d, r.Y, imgSizeWH(0), imgSizeWH(1), 270, 90)
gp.AddArc(r.X + r.Width - d, r.Y + r.Height - d, imgSizeWH(0), imgSizeWH(1), 0, 90)
gp.AddArc(r.X, r.Y + r.Height - d, imgSizeWH(0), imgSizeWH(1), 90, 90)

g.FillPath(br, gp)
'g.FillRectangle(br, 0, 0, bmp.Width, bmp.Height)
End Using

g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
g.CompositingQuality = CompositingQuality.HighQuality
g.TextRenderingHint = TextRenderingHint.AntiAlias
g.SmoothingMode = SmoothingMode.HighQuality
g.DrawImage(av, 0, 0, bmp.Width, bmp.Height)

Using fnt As New Font("Arial", 132, FontStyle.Bold, GraphicsUnit.Pixel)
TextRenderer.DrawText(g, text, fnt, New Rectangle(0, 0, imgSizeWH(0), imgSizeWH(1)),
Color.WhiteSmoke, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)
End Using
End Using

Return bmp
End Function

Answer

Your GraphicsPath is just some arcs, no lines to make it an actual rounded rectangle. If the size is 800x800, you might want a larger d (diameter); and since you are working on an image, you should use the image size not the size of a PictureBox (this may be why the circle is larger than the image). Use this for the GP block:

' use the actual size, not those array values
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)

Using gp As New GraphicsPath
    ' arc radius is specified, but we use it as diameter
    Dim d As Int32 = radius * 2

    gp.StartFigure()
    ' top left
    ' LRTB creates a temp rect rather than calculating Size
    '   complete with typos and miscalcs
    gp.AddArc(Rectangle.FromLTRB(rect.Left, rect.Top,
                             rect.Left + d, rect.Top + d), 180, 90)
    gp.AddLine(rect.Left + d, rect.Top, rect.Right - d, rect.Top)

    ' top right
    gp.AddArc(Rectangle.FromLTRB(rect.Right - d, rect.Top,
                            rect.Right, rect.Top + d), -90, 90)
    gp.AddLine(rect.Right, rect.Top + d, rect.Right, rect.Bottom - d)

    ' bottom right
    gp.AddArc(Rectangle.FromLTRB(rect.Right - d, rect.Bottom - d,
                            rect.Right, rect.Bottom), 0, 90)
    gp.AddLine(rect.Right - d, rect.Bottom, rect.Left + d, rect.Bottom)

    ' bottom left
    gp.AddArc(Rectangle.FromLTRB(rect.Left, rect.Bottom - d,
                            rect.Left + d, rect.Bottom), 90, 90)
    gp.AddLine(rect.Left, rect.Bottom - d, rect.Left, rect.Top + d)

    gp.CloseFigure()
    Using p As New Pen(BackColor), br = New SolidBrush(BackColor)
        g.DrawPath(p, gp)
        g.FillPath(br, gp)
    End Using
End Using

Note that when trying this on smaller images, it can help to create a much larger image, say 2, 4 or 8 times the final size, draw the rounded rect (using a radius * {2, 4 or 8}) then shrink down the result. This makes for nicer corners.1

Result using a 250x250 image and radius of 42:

enter image description here

The Picturebox has a fixed border and is on a panel (yellowish), to proof the transparency and edges. Use a smaller diameter for smaller cuts. Also, for max flexibility, mine is in a class which allows all those hardcoded Font, Size and Color params to be specified - the image shows MistyRose used for the text color.

1 The supersizing helps on small images or sharp curves. My 250 image with a very small curve (Radius == 8 ) looks kind of bad.