Search code examples
c#drawing

Circle Cut Off in PictureBox C#


i created a function which is drawing a circle with a letter inside.

i write it into a stream and set it to picture box in Zoom Mode.

It looks nice but some pieces of the circle are cut off.

Here is the Code for the Circle:

public MemoryStream GenerateCircle(string name)
    {


        var avatarString = string.Format("{0}", name[0]).ToUpper();

        var bgColour = ColorTranslator.FromHtml("#007FBC");
        var bmp = new Bitmap(70, 70);
        var sf = new StringFormat();
        sf.Alignment = StringAlignment.Center;
        sf.LineAlignment = StringAlignment.Center;
        var font = new Font("Arial", 34, FontStyle.Bold, GraphicsUnit.Pixel);
        var graphics = Graphics.FromImage(bmp);
        graphics.Clear(Color.White);
        graphics.SmoothingMode = SmoothingMode.AntiAlias;
        graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

        Rectangle rect = new Rectangle(0, 0, 70, 70);

        Brush b = new SolidBrush(bgColour);
        Pen pen = new Pen(bgColour);

        graphics.DrawEllipse(pen, rect);
        graphics.FillEllipse(b, rect);

        graphics.DrawString(avatarString, font, new SolidBrush(Color.WhiteSmoke), 35, 35, sf);
        graphics.Flush();
        var ms = new MemoryStream();
        bmp.Save(ms, ImageFormat.Png);
        return ms;
    }

i set it like that:

 avatarpicturebox.Image = Image.FromStream(GenerateCircle("Test"));

That is what it looks like:

Circle Cuts Off

Can someone help here please?


Solution

  • Just reduce the circle's width and height 1 pixel each to avoid clipping the right and bottom sides due to the pixels offset. Use Rectangle.Inflate method and pass negative width and height values to shrink the destination rectangle to fit.

    Modifying your function to return a Bitmap instead of MemoryStream.

    public Bitmap GenerateCircle(string name)
    {
        var bgColour = ColorTranslator.FromHtml("#007FBC");
        var bmp = new Bitmap(70, 70, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
    
        using (var graphics = Graphics.FromImage(bmp))
        using (var sf = new StringFormat())
        using (var font = new Font("Arial", 34, FontStyle.Bold, GraphicsUnit.Pixel))
        using (var b = new SolidBrush(bgColour))
        using (var pen = new Pen(bgColour))
        {
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;
    
            graphics.Clear(Color.White);
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
            graphics.PixelOffsetMode = PixelOffsetMode.Half;
    
            Rectangle rect = new Rectangle(Point.Empty, bmp.Size);
            rect.Inflate(-2, -2);
    
            graphics.DrawEllipse(pen, rect);
            graphics.FillEllipse(b, rect);                
            graphics.DrawString(name.ToUpper(), font, Brushes.WhiteSmoke, rect, sf);
        }
    
        return bmp;
    }
    

    And the caller:

    private void SomeButton_Click(object sender, EventArgs e)
    {
        avatarpicturebox.Image?.Dispose();
        avatarpicturebox.Image = GenerateCircle("T");
    }
    

    Side Notes

    • Always dispose of the graphics objects you create either by calling explicitly the .Dispose method or by creating them with using keyword.
    • Use the Graphics.DrawString overloads that take a Rectangle and StringFormat params to draw the strings. Use the later to dictate how and where the string should be drawn in the given Rectangle. See also StringFormatFlags enum.