Interpolation anomaly during drawing - c#

I have a problem with AntiAliasing smoothing mode and drawing.
Let say I have a signal with min and max values at the same points.
So I want to display it to see where it "thicker".
So the method I use is to draw vertical lines and use antialiasing.
Here is the problem, the rising edge seems to be antialiased, but the falling not.
If I added some noise to the second signal the same thing observable.
Without noise
With noise
![With noise][2]
Can anyone point out what am I missing? Or this problem comes from somewhere else?
Code (moved from comments):
Bitmap drawBitmap = new Bitmap(pictureBox1.Height, _
pictureBox1.Width, _
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics drawGraph;
Point[] pts = new Point[] { new Point(0, 60), new Point(0, 59), new Point(1, 35), _
new Point(1, 47), new Point(2, 25), new Point(2, 35), _
new Point(3, 17), new Point(3, 25), new Point(4, 12), _
new Point(4, 27), new Point(5, 10), new Point(5, 22), _
new Point(6, 10), new Point(6, 11), new Point(7, 11), _
new Point(7, 16), new Point(8, 16), new Point(8, 24), _
new Point(9, 24), new Point(9, 34), new Point(10, 34), _
new Point(10, 46), new Point(11, 46), new Point(11, 59), _
new Point(12, 59), new Point(12, 72)};
using (drawGraph = Graphics.FromImage(drawBitmap)) {
drawGraph.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic;
drawGraph.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias;
for (int i = 1; i < pts.Length - 1; i += 2) {
drawGraph.DrawLine(new Pen(Color.Black, 1), pts[i], pts[i - 1]);
drawGraph.DrawLine(new Pen(Color.Black, 1), pts[i], pts[i + 1]);
}
}
pictureBox1.Image = drawBitmap;

Apply a pixel offset mode as well:
drawGraph.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality;
The InterpolationMode can be removed as it do nothing with lines (only with images when resized).

Related

Self-intersecting GraphicsPath with z-order

I need to create a framed GraphicsPath that self-intersects giving a z-order feeling:
The code that I used to obtain the image is the following:
private void Example1(PaintEventArgs e) {
Brush brush = new SolidBrush(Color.FromArgb(200, Color.LightBlue));
GraphicsPath path1 = new GraphicsPath(FillMode.Winding);
path1.AddLines(new Point[] {
new Point(400, 200),
new Point(400, 300),
new Point(100, 300),
new Point(100, 400),
new Point(500, 400),
new Point(500, 100)
});
e.Graphics.FillPath(brush, path1);
e.Graphics.DrawPath(Pens.Blue, path1);
GraphicsPath path2 = new GraphicsPath(FillMode.Winding);
path2.AddLines(new Point[] {
new Point(500, 100),
new Point(200, 100),
new Point(200, 500),
new Point(300, 500),
new Point(300, 200),
new Point(400, 200)
});
e.Graphics.FillPath(brush, path2);
e.Graphics.DrawPath(Pens.Blue, path2);
}
in which I draw the two paths independently.
My need is to handle it as a unique graphic object, but if I join the paths I obtain this image:
Example code:
private void Example2(PaintEventArgs e) {
Brush brush = new SolidBrush(Color.FromArgb(200, Color.LightBlue));
GraphicsPath path1 = new GraphicsPath(FillMode.Winding);
path1.AddLines(new Point[] {
new Point(400, 200),
new Point(400, 300),
new Point(100, 300),
new Point(100, 400),
new Point(500, 400),
new Point(500, 100)
});
GraphicsPath path2 = new GraphicsPath(FillMode.Winding);
path2.AddLines(new Point[] {
new Point(500, 100),
new Point(200, 100),
new Point(200, 500),
new Point(300, 500),
new Point(300, 200),
new Point(400, 200)
});
path1.AddPath(path2, true);
e.Graphics.FillPath(brush, path1);
e.Graphics.DrawPath(Pens.Blue, path1);
}
Same problem if I use StartFigure/CloseFigure. Maybe I can solve the problem using the SetMarkers method in conjunction with the GraphicsPathIterator, but it seems overwhelming.
The simplest way that I found is to use the GraphicsPathIterator. In this way I can store more figures on a single path and have the flexibility I need during the painting. The only drawback is that the paint method has to be modified accordingly.
Here is an example in which I define the path and that do also the painting:
private void Example4(PaintEventArgs e) {
Brush brush = new SolidBrush(Color.FromArgb(200, Color.LightBlue));
GraphicsPath path = new GraphicsPath(FillMode.Winding);
path.StartFigure();
path.AddLines(new Point[] {
new Point(400, 200),
new Point(400, 300),
new Point(100, 300),
new Point(100, 400),
new Point(500, 400),
new Point(500, 100)
});
path.StartFigure();
path.AddLines(new Point[] {
new Point(500, 100),
new Point(200, 100),
new Point(200, 500),
new Point(300, 500),
new Point(300, 200),
new Point(400, 200)
});
GraphicsPathIterator pathIterator = new GraphicsPathIterator(path);
GraphicsPath p = new GraphicsPath();
while (pathIterator.NextSubpath(p, out bool isClosed) > 0) {
e.Graphics.FillPath(brush, p);
e.Graphics.DrawPath(Pens.Blue, p);
}
}

Measure width of numbers in Bitmaps

I created a texture (bitmap) that is used for drawing arbitrary numbers
The texture contains the text: 0123456789.-
Now I need the coordinates of each character.
TextRenderer.MeasureText(c.ToString(), m_font, new Size(1, 1), TextFormatFlags.NoPadding);
...returns is 16x17
g.MeasureString(c.ToString(), m_font);
...returns is 11.9x17.7
The actual size is 8x11
My workaround for now is a manual measurement of each character which yields the following table:
coords.Add('0', new RectangleF(1, 3, 8, 11));
coords.Add('1', new RectangleF(9, 3, 7, 11));
coords.Add('2', new RectangleF(15, 3, 8, 11));
coords.Add('3', new RectangleF(22, 3, 8, 11));
coords.Add('4', new RectangleF(29, 3, 8, 11));
coords.Add('5', new RectangleF(36, 3, 8, 11));
coords.Add('6', new RectangleF(43, 3, 8, 11));
coords.Add('7', new RectangleF(50, 3, 8, 11));
coords.Add('8', new RectangleF(57, 3, 8, 11));
coords.Add('9', new RectangleF(64, 3, 8, 11));
coords.Add('.', new RectangleF(72, 3, 3, 11));
coords.Add('-', new RectangleF(75, 3, 6, 11));
I would like to use a dynamic font, so a programmatic way to measure the size is required.
Tell the MeasureString method to work with the generic typographic StringFormat.
static void Main(string[] args)
{
var image = new Bitmap(1000, 500);
var g = Graphics.FromImage(image);
g.FillRectangle(System.Drawing.Brushes.White, 0, 0, 1000, 500);
var stringFormat = new StringFormat(StringFormat.GenericTypographic) {
Alignment = StringAlignment.Near,
FormatFlags = System.Drawing.StringFormatFlags.LineLimit | System.Drawing.StringFormatFlags.NoClip | StringFormatFlags.DirectionRightToLeft
};
var font = new Font(new System.Drawing.FontFamily("Times New Roman"), 72.0f,FontStyle.Regular, GraphicsUnit.Point);
var point = new PointF { X = 10, Y = 10 };
SizeF[] outputs = new SizeF[7];
SizeF[] total = new SizeF[7];
outputs[0] = g.MeasureString("T", font, point, stringFormat);
outputs[1] = g.MeasureString("e", font, point, stringFormat);
outputs[2] = g.MeasureString("s", font, point, stringFormat);
outputs[3] = g.MeasureString("t", font, point, stringFormat);
outputs[4] = g.MeasureString("S", font, point, stringFormat);
outputs[5] = g.MeasureString("t", font, point, stringFormat);
outputs[6] = g.MeasureString("r", font, point, stringFormat);
total[0] = g.MeasureString("T", font, point, stringFormat);
total[1] = g.MeasureString("Te", font, point, stringFormat);
total[2] = g.MeasureString("Tes", font, point, stringFormat);
total[3] = g.MeasureString("Test", font, point, stringFormat);
total[4] = g.MeasureString("TestS", font, point, stringFormat);
total[5] = g.MeasureString("TestSt", font, point, stringFormat);
total[6] = g.MeasureString("TestStr", font, point, stringFormat);
stringFormat.FormatFlags = System.Drawing.StringFormatFlags.LineLimit | System.Drawing.StringFormatFlags.NoClip;
g.DrawString("TestStr", font, System.Drawing.Brushes.Red, new PointF { X = 10, Y = 10 }, stringFormat);
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Blue, 1.0f), GetOutputPositionX(total,outputs, 0), 10.0f, outputs[0].Width, outputs[0].Height);
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Blue, 1.0f), GetOutputPositionX(total,outputs, 1), 10.0f, outputs[1].Width, outputs[1].Height);
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Blue, 1.0f), GetOutputPositionX(total,outputs, 2), 10.0f, outputs[2].Width, outputs[2].Height);
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Blue, 1.0f), GetOutputPositionX(total,outputs, 3), 10.0f, outputs[3].Width, outputs[3].Height);
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Blue, 1.0f), GetOutputPositionX(total,outputs, 4), 10.0f, outputs[4].Width, outputs[4].Height);
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Blue, 1.0f), GetOutputPositionX(total,outputs, 5), 10.0f, outputs[5].Width, outputs[5].Height);
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Brushes.Blue, 1.0f), GetOutputPositionX(total,outputs, 6), 10.0f, outputs[6].Width, outputs[6].Height);
image.Save(#"c:\Temp\bla.png");
}
private static float GetOutputPositionX(SizeF[] total, SizeF[] outputs, int p)
{
return 10.0f + total[p].Width - outputs[p].Width;
}
To get the Position exactly, because of these overlapping characters you have to calculate the width of "e" and the total width of "Te".
Plus there is obviously a bug in the MeasureString function. If you call MeasureString without the StringFormatFlags.DirectionRightToLeft the overlap of the characters "Te" is ignored.

C# fill polygon (triangle)

I have problem with draw two polygons.
I want to fill two triangles, but one is greater than the second.
I am using UserControl in winforms.
Code:
Point[] DOWN = new Point[] {new Point(0, 0), new Point(10, 0), new Point(5, 5)};
Point[] UP = new Point[] { new Point(0, 15), new Point(10, 15), new Point(5, 10) };
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
SolidBrush brush = new SolidBrush(Color.FromArgb(253, 198, 19));
e.Graphics.FillPolygon(brush, DOWN);
e.Graphics.FillPolygon(brush, UP);
brush.Dispose();
}
Where is problem?
Try setting the PixelOffsetMode property:
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
using (SolidBrush brush = new SolidBrush(Color.FromArgb(253, 198, 19))) {
e.Graphics.FillPolygon(brush, DOWN);
e.Graphics.FillPolygon(brush, UP);
}
Result:
Try keeping the order counter-clockwise and start from the highest point:
new Point(5, 10), new Point(10, 15), new Point(0, 15)
Tell us if that helped. Sometimes those algorithms don't behave well on border conditions.

Drawing polygon with more than one hole?

I'm trying to draw a polygon with more than one holes. I tried the following code and it does not work correctly. Please advise.
PointF[] mypoly = new PointF[6 + 5 + 5];
mypoly[0] = new PointF(0, 0);
mypoly[1] = new PointF(100, 0);
mypoly[2] = new PointF(100, 100);
mypoly[3] = new PointF(0, 100);
mypoly[4] = new PointF(10, 80);
mypoly[5] = new PointF(0, 0);
mypoly[6] = new PointF(10, 10);
mypoly[7] = new PointF(10, 20);
mypoly[8] = new PointF(20, 20);
mypoly[9] = new PointF(20, 10);
mypoly[10] = new PointF(10, 10);
mypoly[11] = new PointF(40, 10);
mypoly[12] = new PointF(40, 20);
mypoly[13] = new PointF(60, 20);
mypoly[14] = new PointF(60, 10);
mypoly[15] = new PointF(40, 10);
g.FillPolygon(new SolidBrush(Color.Red), mypoly, FillMode.Winding);
The first part is the outer polygon. The second and the third parts are the two holes inside the polygon.
Use a GraphicsPath instead. You can draw it with Graphics.FillPath, like this:
using System.Drawing.Drawing2D;
...
using (var gp = new GraphicsPath()) {
PointF[] outer = new PointF[] { new PointF(0, 0), new PointF(100, 0),
new PointF(100, 100), new PointF(0, 100), new PointF(10, 80),new PointF(0, 0) };
gp.AddPolygon(outer);
PointF[] inner1 = new PointF[] { new PointF(10, 10), new PointF(10, 20),
new PointF(20, 20), new PointF(20, 10), new PointF(10, 10) };
gp.AddPolygon(inner1);
PointF[] inner2 = new PointF[] { new PointF(40, 10), new PointF(40, 20),
new PointF(60, 20), new PointF(60, 10), new PointF(40, 10) };
gp.AddPolygon(inner2);
e.Graphics.FillPath(Brushes.Black, gp);
}

C# GDI Drawing2D help

What GDI methods can I use to draw the blue shape shown in the image below? The center must be transparent.
There are a number of ways but you'll probably want to use the following:
FillRectangle
FillPolygon
DrawLine
since it looks like your shape can be reduced to a rectangle and two polygons and then outlined by a few lines.
Here is a really simple and hard-coded example of what i was thinking:
Private Sub Control_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) _
Handles MyBase.Paint
Dim g As Graphics = e.Graphics
g.FillRectangle(Brushes.Aqua, New Rectangle(10, 10, 10, 90))
g.FillPolygon(Brushes.Aqua, New Point() { _
New Point(10, 10), _
New Point(20, 10), _
New Point(40, 50), _
New Point(30, 50)})
g.FillPolygon(Brushes.Aqua, New Point() { _
New Point(10, 100), _
New Point(20, 100), _
New Point(40, 50), _
New Point(30, 50)})
g.DrawLine(Pens.Black, New Point(10, 10), New Point(10, 100))
g.DrawLine(Pens.Black, New Point(10, 100), New Point(20, 100))
g.DrawLine(Pens.Black, New Point(20, 100), New Point(40, 50))
g.DrawLine(Pens.Black, New Point(40, 50), New Point(20, 10))
g.DrawLine(Pens.Black, New Point(20, 10), New Point(10, 10))
...
Im assuming GDI+ here aka System.Drawing namespace.
The best thing to do is to look at System.Drawing.Drawing2d.GraphicsPath class :
http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.graphicspath.aspx
You need to make sure you close the path to get the hollow effect.
Wouldn't it just be easier to draw it using a bitmap? That's what they're for anyway :).

Categories