I'm currently trying to draw some lines in C# with the Graphics class.
My problem is, that sometimes (mostly on the repainting on resizing the form) some parts of the lines are missing.
This is how it looks like then:
This is my code where I draw the lines:
Graphics g = pnlGraph.CreateGraphics();
g.Clear(pnlGraph.BackColor);
Point p1 = new Point((mainNode.Left + (mainNode.Width / 2)), (mainNode.Top + (mainNode.Height / 2)));
Point p2 = new Point((pic.Left + (pic.Width / 2)), (pic.Top + (pic.Height / 2)));
g.DrawLine(new Pen(new SolidBrush(Color.Black), 2), p1, p2);
This code draws some lines from a mainNode in the middle of my panel to some nodes around it.
I'm calling the function to paint the lines on:
Load,
Resize,
Visible state changed
I also tried it in Paint of the form and the panel which didn't work.
Is there any way to fix it or another way of painting these lines?
Thanks for any answer!
Since the answer of #HansPassant has also made some problems we fixed the problem in another way:
We created an Image and filled it with an rectangle of the size of the panel.
After that we draw the lines in the image and draw the image on the panel.
Graphics g = pnlGraph.CreateGraphics();
Image img = new Bitmap(pnlGraph.Width, pnlGraph.Height);
Graphics gi = Graphics.FromImage(img);
gi.DrawRectangle(new Pen(new SolidBrush(pnlGraph.BackColor)), new Rectangle(0, 0, pnlGraph.Width, pnlGraph.Height));
// For every line:
gi.DrawLine(new Pen(new SolidBrush(Color.Black), 2), p1, p2);
// At the end:
g.DrawImage(img, 0, 0, img.Width, img.Height);
Related
I need to rotate an image by a certain angle before further operations. After rotating, im left with black areas on my bitmap.
Image after rotating by 30 degrees
I've managed to find similar issue in java right here.
Is there something like that for c#?
Im using the following code, that i found earlier on stack:
Bitmap Rotate_Image(Bitmap bmp, float angle)
{
Bitmap rotatedImage = new Bitmap(bmp.Width, bmp.Height);
rotatedImage.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
using (Graphics g = Graphics.FromImage(rotatedImage))
{
// Set the rotation point to the center in the matrix
g.TranslateTransform(bmp.Width / 2, bmp.Height / 2);
// Rotate
g.RotateTransform(angle);
// Restore rotation point in the matrix
g.TranslateTransform(-bmp.Width / 2, -bmp.Height / 2);
// Draw the image on the bitmap
g.DrawImage(bmp, new Point(0, 0));
}
return rotatedImage;
}
Since your output is *.png I assume you want to use a transparent background. Just call g.Clear(Color.FromArgb(0, default)); before applying TranslateTransform. Or just set any other background color if you like.
You also can set g.SmoothingMode = SmoothingMode.AntiAlias; to smoothen the sides of the rotated image.
i'm trying to put a red border around another application. I am trying to by using WinApi :
User32Methods.GetWindowRect(windowHandle, out var rectangle);
var g = System.Drawing.Graphics.FromHdc(User32Methods.GetDesktopWindow());
using (g)
{
//drawing borders
g.TranslateTransform(rectangle.Left, rectangle.Top);
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Color.Red, 10),
new System.Drawing.Rectangle(0, 0, rectangle.Width, rectangle.Height));
}
But if i put negative X/Y because the app is on left screen it didn't draw anything. I've try many methods but i'm blocked.
Thanks
I'm having some issues with drawing ellipses to a bitmap in a .NET 4.0 C# WinForms application running on mono 4.6.2 on Raspbian 9 (stretch).
The method in question generates a bitmap of 5 white circles of a specified diameter on a black screen. One circle per corner of the screen, and one in the center. The bitmap is meant to encompass the entire screen, so I'm getting the size for the bitmap as a rectangle via Screen.Bounds(), which in turn gives me Res.Width and Res.Height.
This code works as expected in Windows in .NET (which is what it was originally written for and tested on) but running the exe through mono on Raspbian, simply fills the bitmap with white. I have also found that running in mono for Windows (v5.10.0.160), the circles generated end up being smaller than when run in .NET.
private Bitmap GenerateCirclesBitmap(int diam)
{
Bitmap bmp = GenerateColorBitmap(Color.Black);
Graphics g = Graphics.FromImage(bmp);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
Brush whiteBrush = new SolidBrush(Color.White);
g.FillEllipse(whiteBrush, diam/7, diam/7, diam, diam); //Top Left Corner
g.FillEllipse(whiteBrush, diam/7, (Res.Height - diam)-(diam/7), diam, diam); //Bottom Left Corner
g.FillEllipse(whiteBrush, (Res.Width - diam) - (diam / 7), (Res.Height - diam) - (diam / 7), diam, diam); //Bottom Right Corner
g.FillEllipse(whiteBrush, (Res.Width - diam) - (diam / 7), diam/7, diam, diam); //Top Right Corner
g.FillEllipse(whiteBrush, (Res.Width / 2) - (diam / 2), (Res.Height / 2) - (diam / 2), diam, diam); //Center
g.DrawImage(bmp, Res.Width, Res.Height);
return bmp;
}
I was thinking maybe it was an issue with the dimensions of the screen, Res.Width and/or Res.Height, but if that were the case I would expect my method to generate color bars would have problems as well. However this code works as expected (8 color bars each 1/8 of total screen width) on both versions of mono as well as in .NET, which leads me to think that Res.Width is OK:
private Bitmap GenerateBarsBitmap(Color bar1, Color bar2, Color bar3, Color bar4, Color bar5, Color bar6)
{
Bitmap bmp = GenerateColorBitmap(Color.Black);
int barwidth = Res.Width / 8;
Color[] colors = new Color[8];
Brush b = new SolidBrush(Color.White);
Graphics g = Graphics.FromImage(bmp);
colors[0] = Color.White;
colors[1] = bar1;
colors[2] = bar2;
colors[3] = bar3;
colors[4] = bar4;
colors[5] = bar5;
colors[6] = bar6;
colors[7] = Color.Black;
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
for (int colornum = 0; colornum < colors.Length; colornum++)
{
b = new SolidBrush(colors[colornum]);
g.FillRectangle(b, barwidth * colornum, 0, barwidth, Res.Height);
b.Dispose();
}
return bmp;
}
Any ideas or insight would be appreciated. Thanks!
One difference between your two routines is the line
g.DrawImage(bmp, Res.Width, Res.Height);
It comes after you draw the circles. So in a fully compatible world the Mono framework should do the same as .NET does, i.e. draw the bitmap with all the new content over itself.
But fully compatible is not just hard. Given all the weirdness in the long history of GDI, .NET etc, it is in fact out of the question..
Mono seems to use the original version of the bitmap without the newly drawn circles and by drawing it over itself it deletes them.
So remove the unnessesary line and all should be well..
I have following code to draw my border2.bmp in 4 direction
private void Form1_Paint(object sender, PaintEventArgs e)
{
Bitmap border = new Bitmap("border2.bmp");
int borderThick = border.Height;
Graphics g = e.Graphics;
Size region = g.VisibleClipBounds.Size.ToSize();
Rectangle desRectW = new Rectangle(0, 0, region.Width - borderThick, borderThick);
// 1. LEFT - RIGHT border
g.TranslateTransform(30, 30);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 2. UP - BUTTOM border
g.ResetTransform();
g.TranslateTransform(50, 50);
g.RotateTransform(90);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 3. RIGHT-LEFT border
g.ResetTransform();
g.TranslateTransform(100, 100);
g.RotateTransform(180);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
// 4. BOTTOM - UP border
g.ResetTransform();
g.TranslateTransform(150, 150);
g.RotateTransform(270);
g.DrawImage(border, desRectW, desRectW, GraphicsUnit.Pixel);
}
My original image is:
But the result of rotations are not exactly as I expected. 90 degrees is missing the first red line, 270 degrees is missing first black column, and 180 degrees is missing both.
Like image I attached:
PS: you can get border2.bmp at: http://i.imgur.com/pzonx3i.png
Edit:
I tried g.PixelOffsetMode = PixelOffsetMode.HighQuality; as #Peter Duniho comment, but I found it also does't draw correctly.
Example: 4 line is not starting at same position as we expect.
g.TranslateTransform(50, 50);
// LEFT - RIGHT border
g.DrawLine(Pens.Red, 0, 0, 100, 0);
// UP - BOTTOM border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Blue)), 0, 0, 100, 0);
// RIGHT-LEFT border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Green)), 0, 0, 100, 0);
// BOTTOM - UP border
g.RotateTransform(90);
g.DrawLine(new Pen(Color.FromArgb(128, Color.Gray)), 0, 0, 100, 0);
I can't really explain why this happens, except that any graphics API is necessarily going to include optimizations which may lead to imprecise behaviors at times and it seems that you are running into such a situation here.
In your particular example, the problem can be corrected by adding the following statement to the code, before you draw the images:
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
Setting it to Half will also work. It is equivalent to HighQuality (or technically, HighQuality is equivalent to it…but I find HighQuality more descriptive in the code :) ).
This will slow the rendering of the bitmap down somewhat, but probably not in a way that is perceptible to your users.
While the .NET documentation isn't very helpful in terms of describing this setting, the native Win32 docs for the same feature has slightly more detail:
PixelOffsetMode enumeration. From the description, one can infer that with the logical center of the pixel at (0,0), it's possible to lose a pixel on one edge when rotating (and/or gain a pixel on another edge). Switching to Half fixes this.
You need to account some things.
(1) RotateTransforms applies rotation at the current origin (as explained in the Matrix.Rotate documentation
(2) The default MatrixOrder (when not specified by using the overrides with the additional argument) is Prepend. Which means in your case the resulting transformation is rotate, then translate.
For instance, if you put this code inside the paint event:
var g = e.Graphics;
var loc = new Point(128, 128);
var rect = new Rectangle(0, 0, 64, 16);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.FillRectangle(Brushes.Blue, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(90);
g.FillRectangle(Brushes.Red, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(180);
g.FillRectangle(Brushes.Green, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.RotateTransform(270);
g.FillRectangle(Brushes.Magenta, rect);
you'll get this
The blue rectangle is not rotated. Now pin virtually the origin (the upper left point) and start rotating clockwise. You'll see that it will exactly match the Red (90), Green (180) and Magenta (270) rectangles.
What all that means is that if you want to form a rectangle, you need to apply additional offset (translation) to the rotated rectangles. It depends how you want to handle the overlapping areas, but for the sample if we want to concat the Red rectangle right to the Blue one, we need to add the Blue rectangle Width + the original rectangle Height in the X direction. For other rotated rectangles you can apply similar additional offset to X, Y or both.
To complete the sample, if we modify the code like this
var g = e.Graphics;
var loc = new Point(128, 128);
var rect = new Rectangle(0, 0, 64, 16);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y);
g.FillRectangle(Brushes.Blue, rect);
g.ResetTransform();
g.TranslateTransform(loc.X + rect.Width + rect.Height, loc.Y);
g.RotateTransform(90);
g.FillRectangle(Brushes.Red, rect);
g.ResetTransform();
g.TranslateTransform(loc.X + rect.Width + rect.Height, loc.Y + rect.Width + rect.Height);
g.RotateTransform(180);
g.FillRectangle(Brushes.Green, rect);
g.ResetTransform();
g.TranslateTransform(loc.X, loc.Y + rect.Width + rect.Height);
g.RotateTransform(270);
g.FillRectangle(Brushes.Magenta, rect);
the new result will be
Once you understand all that, hope you can apply the required corrections to your concrete code.
Can anybody tell me how to get a rectangle back from GetBounds in any units OTHER than pixels? The following code - lifted directly off the MSDN documentation for this function - returns a rectangle that is pretty obviously in pixels rather than points (1/72 of an inch). (Unless icons come in a size of 32/72"x32/72" rather than 32x32 pixels like I think). I am most interested in working with a rectangle in inches, but I would settle for simply seeing the GetBounds pageUnit parameter cause a change in the returned rectangle.
Bitmap bitmap1 = Bitmap.FromHicon(SystemIcons.Hand.Handle);
Graphics formGraphics = this.CreateGraphics();
GraphicsUnit units = GraphicsUnit.Point;
RectangleF bmpRectangleF = bitmap1.GetBounds(ref units);
Rectangle bmpRectangle = Rectangle.Round(bmpRectangleF);
formGraphics.DrawRectangle(Pens.Blue, bmpRectangle);
formGraphics.Dispose();
The Information is a little sparse on this, I was able to find this MSDN Forum posting that suggests since the Bitmap is already created the units have already been set and are not changable. Since the GraphicsUnit is being passed by a reference, it you look at it after the call you will find it set back to Pixel from Inch. If you actually want to change the size that the rectangle is drawn at set the Graphics.PageUnit Property on formGraphics to the GraphicsUnit you want to draw the Rectangle at.
From above Link:
In this sample, the parameters of Image.GetBounds method don’t change the result, because the bound of Bitmap has been decided. The parameters only determine the unit length to deal with the range, inch by inch or point by point. But the parameters will not influence the result.
emphasis mine
A bit late answering this one, but I thought I would do so because I found it in Google when trying to answer the question "how many mm can I fit in my picture box?", it would have saved me a lot of time not having to work out how to do it!. GetBounds is useless (if you wanted it in pixels...) but it is possible to find the relation between drawing units and display pixels using the Graphics.TransformPoints method:
private void Form1_Load(object sender, EventArgs e)
{
Bitmap b;
Graphics g;
Size s = pictureBox1.Size;
b = new Bitmap(s.Width, s.Height);
g = Graphics.FromImage(b);
PointF[] points = new PointF[2];
g.PageUnit = GraphicsUnit.Millimeter;
g.PageScale = 1.0f;
g.ScaleTransform(1.0f, 1.0f);
points[0] = new PointF(0, 0);
points[1] = new PointF(1, 1);
g.TransformPoints(CoordinateSpace.Device, CoordinateSpace.Page, points);
MessageBox.Show(String.Format("1 page unit in {0} is {1} pixels",g.PageUnit.ToString(),points[1].X));
points[0] = new PointF(0, 0);
points[1] = new PointF(1, 1);
g.TransformPoints(CoordinateSpace.Page, CoordinateSpace.World, points);
MessageBox.Show(String.Format("1 page unit in {0} is {1} pixels",g.PageUnit.ToString(),points[1].X));
g.ResetTransform();
pictureBox1.Image = b;
SolidBrush brush = new SolidBrush(Color.FromArgb(120, Color.Azure));
Rectangle rectangle = new Rectangle(10, 10, 50, 50);
// Fill in the rectangle with a semi-transparent color.
g.FillRectangle(brush, rectangle);
pictureBox1.Invalidate();
}
This will display the basic mm to display pixels (3.779527 in my case) - the world coordinates are 1 mm per pixel, this would change if you applied graphics.ScaleTransform.
Edit: Of course, it helps if you assign the bitmap to the pictureBox image property (and keep the Graphics object to allow changes as required).
Add label
In class Form1 Add field
PointF[] cooridates;
Form1.cs [design] look for lighting bolt in properties double click Paint create handler
Form1_Paint(object sender,PaintEventArgs)
{
e.Graphics.PageUnit = GraphicsUnit.Inch;
if (cooridates != null)
e.Graphics.TransformPoints(CoorinateSpace.World,
CoorinateSpace.Device,cooridates);
}
Create handler again for Form1.MouseMove
Form1_MouseMove(object sender,MouseEventArgs e
{
cooridates[0].X = e.Location.X;
cooridates[0].Y = e.Location.Y;
this.Refresh();
label1.Text = $"X = {cooridates[0].X} Y = {
{ cooridates[0].Y } ";
}
Form1_Load(object sender,MouseEventArgs)
{
cooridates = new PointF[1] { new PointF(0f,0f) };
}
Move mouse to get cooridates in Inches