Graphics.DrawHalfCirle & Graphics.DrawPartialCircle - c#

I'm trying to draw half and partial circles (all BLACK lines) on a bitmap.
My INTENDED result looks like this:
My CURRENT result looks like this:
I've tried so many different alternatives but it nevers looks right.
using (var b = new Bitmap(200, 100, PixelFormat.Format24bppRgb))
{
using (var g = Graphics.FromImage(b))
{
g.FillRectangle(new SolidBrush(Color.LightGray), 0, 0, 200, 100);
// RED COLOR
Rectangle rec = new Rectangle(-15, 50, 70, 100);
g.DrawRectangle(new Pen(Color.Red, 1f), rec);
g.DrawArc(new Pen(Color.Red, 3f), rec, 50, 100);
// WHITE COLOR
Rectangle rec = new Rectangle(10, 50, 70, 70);
g.DrawRectangle(new Pen(Color.White, 1f), rec);
g.DrawEllipse(new Pen(Color.White, 3f), rec);
}
}
But it always look totally wrong and after hours of playing with the numbers, I could not find a way to control the output.
Question:
Is there a simple way to design the 3 black lines on my INTENDED image in a graphic object using C# ??

The easy way to achieve this is to draw three concentric circles and let clipping take care of the fact that two of them fall outside the drawing region.
The way to achieve the arc-based drawing you want is probably to start with the concentric circles (so you know you have the rects in the right places), and then change the DrawEllipse to DrawArc, setting the start and sweep angles to the right values.
Start angle is measured in degrees from the x axis (horizontal line towards the right of the circle's centre), so for the smaller arc you will need an angle approximately 305 degrees. From there you need it to draw for about 90 degrees. The outer arc will be similar, but a smaller arc, so it might go from about 330 degrees for a sweep of about 60 degrees.

It seems the solution is to draw a rectangle outside the boundaries of the bitmap and use the graphic.DrawEllipse method to draw the curve line.
Here is a snippet of the working code:
Pen pen = new Pen(Color.White);
Rectangle rec = new Rectangle(-30, 50, 100, 100);
g.DrawEllipse(pen, rec);
rec = new Rectangle(-30, 10, 150, 150);
g.DrawEllipse(pen, rec);
rec = new Rectangle(-30, -30, 200, 200);
g.DrawEllipse(pen, rec);
Many thanks to Hans Passant to point me to this line of thinking.

Related

C# Rounded Rectangle Corner Inconsistencies

Using C# WinForms, I have a normal label which I fill with rounded corners. The issue I am having is that the resulting corners are not consistently rounded.
For ease of understanding, I have created 4 labels with rounded corners, ranging from a radius of 1 to 5. I have added a single outline border to the label for ease of visibility, but even without the single outline the results are the same. I have also zoomed the label images so we can see the corners more clearly.
Using a radius of 1, you can clearly see that the top corners are rounded accordingly, but the bottom borders remain square.
Using a radius of 2 and 3 respectively, produces the same output at the bottom.
Using a radius of 4 and also 5 respectively, you can clearly see the inconsistencies on all the corners.
Generally it wouldn't be too much of a problem if I had a label of a small size, but the user needs to be able to zoom, and then it just looks plain ugly as the radius increases, as the inconsistencies are clearly visible.
The code I am using to create the filled rectangle is supposedly very basic as per example, taken from How To Draw a Rounded Rectangle
public static GraphicsPath draw_rectangle(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
I have studied and understand what the code does and how it works, I get the angles and the sweep, but I am by no means a seasoned graphics manipulator so I don't know if I am doing something wrong or if there is a way to better the code for all corner consistency.
Your help and\or explanations are appreciated.

Adding antialiasing

I am trying to using antialiasing but I don't why it isn't working:
{
Pen pen = new Pen(Color.Black, 3);
Pen r = new Pen(Color.YellowGreen, 3);
Graphics b = panel2.CreateGraphics();
b.DrawEllipse(pen, 6, 0, 90, 90);
b.SmoothingMode = SmoothingMode.AntiAlias;
b.DrawLine(r, new Point(50, 90), new Point(50, 0));
}
First it should be noted that the Graphics object does not contain any graphics; it is a tool that lets you draw onto a related bitmap, including a control's surface. Therefore changing any of its properties, like the SmoothingMode only influences graphics you draw from then on, not anything you have drawn before..
The circle certainly would have antialised pixels if you would draw it after setting the SmoothingMode from its default None to AntiAlias.
The Line is vertical, so it doesn't need antialiasing except at its ends, where there is some. But if you tilt it or move it to a non-integer position anti-aliasing will show!
Let's modify your code a little and look closely at the result:
Pen pen = new Pen(Color.Black, 3);
Pen r = new Pen(Color.YellowGreen, 3);
Graphics b = panel2.CreateGraphics();
b.DrawEllipse(pen, 6, 6, 90, 90);
b.SmoothingMode = SmoothingMode.AntiAlias;
b.DrawLine(r, new Point(50, 90), new Point(50, 0));
b.DrawLine(r, new Point(60, 90), new Point(70, 0));
b.DrawLine(r, new PointF(40.5f, 90), new PointF(40.5f, 0));
b.DrawEllipse(pen, 6, 6, 30, 30);
The smaller circle has many gray pixels and even the original green line has a lighter top end. The two new lines are fully anti-aliased now, one because it is tilted, the other because it sits 'between' pixels.
Btw: If it is turned on you will also see anti-alising when your Pen.Width is even or when it is a non-integer number. The reason for the latter should be obvious; the former comes from the PenAlignment property. Its default Center tries to center the pen, but not at the pixel boundary but at the center of the coordinate pixels. Therefore only an uneven width will completely fill the pixels and not cause anti-aliasing. For closed shapes you can change this behaviour by changing the Pen.Alignment to Inset:
This property determines how the Pen draws closed curves and
polygons. The PenAlignment enumeration specifies five values;
however, only two values—Center and Inset—will change the appearance
of a drawn line. Center is the default value for this property and
specifies that the width of the pen is centered on the outline of the
curve or polygon. A value of Inset for this property specifies that the
width of the pen is inside the outline of the curve or polygon. The
other three values, Right, Left, and Outset, will result in a pen that
is centered.
A Pen that has its alignment set to Inset will yield unreliable
results, sometimes drawing in the inset position and sometimes in the
centered position.Also, an inset pen cannot be used to draw compound
lines and cannot draw dashed lines with Triangle dash caps.
PS: The question was not about how to draw properly, so let me just note that you never ought to do it using control.CreateGraphics as this will always only result in non-persistent graphics. Instead you need to use the Paint event and its e.Graphics object..

GDI+ shows artifact when drawing rectangles side-by-side with scale factor

Using GDI+ to draw two rectangles side-by-side, if I apply a scale factor to have a zoom effect, an artifact appears between the rectangles in some scale factors.
I have the following code to show the problem:
var scale = (float)(numericUpDown1.Value)/100.0f;
var g = e.Graphics;
var b = new SolidBrush(Color.Red);
var rect1 = new Rectangle(10, 40, 100, 100);
var rect2 = new Rectangle(110, 40, 100, 100);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.ScaleTransform(scale, scale);
g.FillRectangle(b, rect1);
g.FillRectangle(b, rect2);
Using this code and setting the "numericUpDown1" to 100, I get the following:
Setting the scale to 125, I get a line between the rectangles:
Scaling to pretty much any value that is not a multiple of 10 gives this problem. Looking close on the line, it seems to be some anti-aliasing effect. I can't remove the anti-aliasing because in my real scenario the rectangles might be polygons with non-straight lines.
I've tried many things, like toggling the PixelOffsetMode, CompositingQuality, Antialiasing, but nothing works. My goal is to have something in the same level of quality of what MS PowerPoint does.

Draw image with rounded corners, border and gradient fill in C#

I've looked everywhere and googled everything and couldn't find anything good.
What I need is a class that is able to draw an image (graphics) with rounded corners (different on each corner is a plus) with a border and gradient fill.
All the examples I find have some flaws (like bad quality, missing functionality etc).
I will use this with a ashx that will draw the image and then show it to the user.
Thanks!
The GraphicsPath allows you to draw relatively free form shapes which you can then fill with a gradient brush. The below example code will create a rectangle with two differntly rounded corners and a gradient fill.
GraphicsPath gp = new GraphicsPath();
gp.AddLine(new Point(10, 10), new Point(75, 10));
gp.AddArc(50, 10, 50, 50, 270, 90);
gp.AddLine(new Point(100, 35), new Point(100, 100));
gp.AddArc(80, 90, 20, 20, 0, 90);
gp.AddLine(new Point(90, 110), new Point(10, 110));
gp.AddLine(new Point(10, 110), new Point(10, 10));
Bitmap bm = new Bitmap(110, 120);
LinearGradientBrush brush = new LinearGradientBrush(new Point(0, 0), new Point(100, 110), Color.Red, Color.Yellow);
using (Graphics g = Graphics.FromImage(bm))
{
g.FillPath(brush, gp);
g.DrawPath(new Pen(Color.Black, 1), gp);
g.Save();
}
bm.Save(#"c:\bitmap.bmp");
This result in the following image:
I think you'll need to create your own method, using a graphics object and "manually" (read "with code") create the image. Easiest way would be to create a single graphics object, add a circle, then in each quadrant of the image add the extras you need, then split the object into fourths. Or return the whole thing as one image then use CSS sprites to place the image in the right spots with the right coordinates (probably the better solution as it uses less calls to the graphics library and returns just one file, so less calls to the web server).

Graphics transparency on PictureBox

First of all, this is not about making the PictureBox control transparent. It's about bitmap transparency on the fully opaque "canvas".
The PictureBox will always have the size of 300*300 with white background. No transparency is needed for the control.
What I need is the way to draw the transparent rectangle (or whatever else) onto the pictureBox, so anything that was already there will be seen "through" the rectangle.
Say I have a following code
Bitmap bmp = new Bitmap(300, 300);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, 300, 300);
g.FillRectangle(new SolidBrush(Color.Red), 100, 100, 100, 100);
pictureBox.Image = bmp;
This will draw a red rectangle in the middle of the white canvas. Now, I need another (transparent) "layer" on the picture containing another rectangle, but one that is transparent.
I can try
Brush brush = new SolidBrush(Color.FromArgb(128, 0, 80, 0));
g.FillRectangle(brush, 50, 50, 200, 200);
Since I am using a color by specifying its alpha = 128, the resulting rectangle should be transparent so the first red rectangle should be seen through this other green one.
However, this does not happen correctly. I can see the red rectangle behind the new green one, but the part of the green rectangle that does not overlap the red one will remain completely opaque. However, if I set the alpha value of the color to some extremely small value (say 1-5), the whole rectangle will look transparent. This is not normal in my opinion - that 5/255 is only half transparent and that 128/255 is not transparent at all... And if there was a string drawed previously with g.DrawString(), the string is either displayed behind the green rectangle or it is not, depending on the level of transparency. For example if the Alpha is greater than or equals (around) 40, the string is not visible at all, and if it is less than 40, then it will show, more visible for smaller alpha values, down to alpha = 0.
How is this brush (when created from Argb color) applied? Am I missing something? To me it seems that setting a transparent brush makes the background "more visible" instead of setting the object "less visible".
Thanks for any replies with suggestions.
[EDIT] It seems I had a nasty bug in application logic, so the drawing routine happened in a loop, so when I accumulated certain number of transparent rectangles, they became more and more thick.
The code, when taken out of the loop, works correctly.
My bad.
alt text http://lh4.ggpht.com/_1TPOP7DzY1E/S02ivAoGgTI/AAAAAAAAC6s/ZQvZQ5GdwSU/s800/Capture4.png
is done by this code:
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Bitmap bmp = new Bitmap(300, 300);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(Color.White), 0, 0, 300, 300);
g.FillEllipse(new SolidBrush(Color.Blue), 25, 25, 100, 200);
g.FillRectangle(new SolidBrush(Color.Red), 100, 100, 300, 100);
g.DrawString("this is a STRING", SystemFonts.DefaultFont,
Brushes.Black, new Point(150, 150));
pictureBox1.Image = bmp;
Brush brush = new SolidBrush(Color.FromArgb(40, 0, 80, 0));
g.DrawRectangle(Pens.Black, 50, 50, 200, 200);
g.FillRectangle(brush, 50, 50, 200, 200);
}
The green part is not opaque as you can see... The string is perfectly visible.
To me it seems that setting a transparent brush makes the background "more visible" instead of setting the object "less visible".
background "more visible" and object "less visible" are the same thing...

Categories

Resources