panel1.DrawToBitmap doesn't draw lines from DrawLine - c#

I have a panel on which I'm drawing lines using:
Point PreviousPoint = new Point (0,0);
Point NewPoint = new Point (10,10);
Pen MyPen = new Pen(Color.Black, 2);
Graphics MyGraphics = panel1.CreateGraphics();
MyGraphics.DrawLine(MyPen, PreviousPoint, NewPoint);
This all works fine. I obviously change the points to draw more lines, but that doesn't matter for this question. I want to export that panel as a jpg file. I'm using this code:
Bitmap bmp = new Bitmap(panel1.Width, panel1.Height);
panel1.DrawToBitmap(bmp, new Rectangle(0, 0, panel1.Width, panel1.Height));
bmp.Save("C:\\panel.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
This outputs a blank jpg. The background of my panel is gray, and the background of the jpg is the same gray, so I know it's actually exporting the panel. Also, I added a button into the panel just to see if it would get saved, and it did. So for some reason the jpg isn't saving the lines being drawn.

So I made a workaround that solves the core problem. I made an array of the points I plotted to draw the lines, then I did this:
// make sure you actually drew something
if (MyLines.Length > 0)
{
// instantiate the stuff you need
Image img = new Bitmap(panel1.Width, panel1.Height);
Graphics g = Graphics.FromImage(img);
Pen pen = new Pen(Color.Black, 2);
// draw every line (from every even index to the one after it)
for (int i = 0; i < MyLines.Length; i++)
{
if (i % 2 == 0)
{
g.DrawLine(pen, MyLines[i], MyLines[i + 1]);
}
}
img.Save("C:\\panel.png", System.Drawing.Imaging.ImageFormat.Png);
}
}

Related

Is it possible to rotate a drawing half-way its definition?

I'm doing some draws that are repetitive, and each of them gives lot of work.
What i need to do is rotate the drawing half-way its definition, something like this:
using (Graphics g = Graphics.FromImage(bmp))
{
//define area do pictureBox e preenche a branco
Brush brush = new SolidBrush(Color.White);
Rectangle area = new Rectangle(0, 0, 520, 520);
g.FillRectangle(brush, area);
//rotate
g.RotateTransform(some angle, some reference point)
//draw some more lines on the top of the rotated previous ones.
}
I tried using g.RotateTransform(90) as there is that function in Winforms, but it didn't change anything. Why??
Any tip?
Edit: if it helps, i only need to rotate fixed angles, 180ยบ
RotateTransform certainly does change the subsequent drawing.
Note that you usually need a TranslateTransform before to set the rotation point. But it 'it didn't change anything' is certainly wrong. Try again! And yes you can rotate (or scale or move) at any point and move/turn it back or completely reset the Graphics object.
And yes, learning about Matrix and MultiplyTransform is also very helpful..
But: You need to understand the Graphics object does not contain any graphic, a common misconception! It is the tool which does the drawing on a Bitmap, most often the surface of a Control. So the rotation will happen but only for the things you draw after:
private void panel1_Paint(object sender, PaintEventArgs e)
{
Rectangle rect = new Rectangle(25, 25, 25, 25);
e.Graphics.TranslateTransform(25, 25);
e.Graphics.FillRectangle(Brushes.Red, rect);
for (int i = 0; i < 15; i++)
{
rect.Inflate(2, 2);
e.Graphics.TranslateTransform(5, 2);
e.Graphics.RotateTransform(2.5f);
e.Graphics.DrawRectangle(Pens.Blue, rect);
}
}
Try this:
use these references:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
I made windows application and put on form1 picturebox then this is the code in form_load:
//Load an image in from a file
Bitmap pImage = new Bitmap(Environment.CurrentDirectory + #"\Image.bmp", true);
//Set our picture box to that image
pictureBox1.Image = (Bitmap)pImage.Clone();
//Store our old image so we can delete it
Image oldImage = pictureBox1.Image;
//Pass in our original image and return a new image rotated 35 degrees right
pictureBox1.Image = RotateImage(pImage, 270);
if (oldImage != null)
{
oldImage.Dispose();
}
Then make static function with parameters of image and rotation angle return the rotated image and call it from form_load as mentioned before :
if (image == null)
{
throw new ArgumentNullException("image");
}
else
{
//create a new empty bitmap to hold rotated image
Bitmap rotatedBmp = new Bitmap(image.Width, image.Height);
rotatedBmp.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(rotatedBmp);
//move rotation point to center of image
g.TranslateTransform((float)image.Width / 2, (float)image.Height / 2);
//rotate
g.RotateTransform(angle);
//move image back
g.TranslateTransform(-(float)image.Width / 2, -(float)image.Height / 2);
//draw passed in image onto graphics object
g.DrawImage(image, new PointF(0, 0));
return rotatedBmp;
}
You can also use directly the code in form_load by using ready RotateFlipType (Enumeration type) but this with fixed angles like 90,270,.... but the previous method you can use any integer values to rotate the image :
private void Form1_Load(object sender, EventArgs e)
{
//Load an image in from a file
Bitmap pImage = new Bitmap(Environment.CurrentDirectory + #"\Image.bmp", true);
pImage.RotateFlip(RotateFlipType.Rotate90FlipXY);
pictureBox1.Image = pImage;
}

drawing on image of a picturebox c#

Hello i am in problem with drawing in picturebox. I am trying to draw over a picturebox. picturebox contains an image.I draw a sine wave using drawline method. when the wave reaches the end of the width of picturebox then i use
g.Clear(pictureBox1.BackColor);
this clears the wave over the picturebox.But the problem is it also clears the image of the picturebox.I want draw a wave over a image and then clears it when it reaches the picturebox.width and again starts from the initial position. please help!
Graphics g;
g = pictureBox1.CreateGraphics();
g.DrawLine(System.Drawing.Pens.Crimson, ti, old_gval1, ti + trackBar1.Value, gval1);
usb.SpecifiedDevice.SendData(OUTBuffer);
old_gval1 = gval1;
ti = ti + trackBar1.Value;
if (ti > pictureBox1.Width) {
ti = 0;
g.Clear(pictureBox1.BackColor);
g.DrawLine(System.Drawing.Pens.Gray, 0, ((pictureBox1.Height - 1) - (gnd_val) * ((pictureBox1.Height - 10) / 1023f)), pictureBox1.Width, ((pictureBox1.Height - 1) - (gnd_val) * ((pictureBox1.Height - 10) / 1023f)));
g.DrawLine(System.Drawing.Pens.Gray, pictureBox1.Width / 2, 0, pictureBox1.Width/ 2,pictureBox1.Height);
}
You can use a special feature of the PictureBox:
It has not only the Image everybody is using but also a BackgroundImage usually overlooked.
You can paint freely on the Image and still keep a BackgroundImage untouched.
Obviosly you need to paint on a transparent Bitmap.
Here is some code:
// load the background image:
this.pictureBox1.BackgroundImage = new Bitmap(yourImageFileName);
// prepare the image:
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp) )
{
g.FillRectangle(Brushes.Transparent, new Rectangle(Point.Empty, bmp.Size) );
}
pictureBox1.Image = bmp;
Now paint stuff:
Random R = new Random();
private void button1_Click(object sender, EventArgs e)
{
Image bmp = pictureBox2.Image;
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawEllipse(Pens.Blue, R.Next(33), R.Next(33), R.Next(500), R.Next(500));
g.DrawEllipse(Pens.Red, R.Next(33), R.Next(33), R.Next(500), R.Next(500));
g.DrawEllipse(Pens.White, R.Next(33), R.Next(33), R.Next(500), R.Next(500));
}
pictureBox2.Image = bmp;
}
When your plotting has reached the right edge you would use a call to FillRectangle(Brushes.Transparent,.. to clear the foreground image and reset your x value.
Sounds like the cheapest solution to your problem.
You have two options here.
You could create your graphics from the image using Graphics.FromImage, then assign the image from that to the picture box.
Draw the image into your new Graphics object using Graphics.DrawImage.
It sounds like the latter might be a bit more appropriate for you, since you aren't actually altering the in-memory image. So every time you clear the Graphics instance, just draw in the Image.

Graphics.DrawString doesn't work

I want to draw a text over a PictureBox in a foreach loop. This is the code that is responsible for rendering(GG is a PictureBox that is currently in the loop)
if (GG != null)
{
((PictureBox)GG).Image = (Image)obj;
using (Graphics g = ((PictureBox)GG).CreateGraphics()) {
g.DrawString(i["amount"].ToString(), kryptonRichTextBox1.Font,
new SolidBrush(Color.Gold), new Point(16, 18));
}
}
But sadly, the text is not rendered. If I comment out the
//((PictureBox)GG).Image = (Image)obj;
line, it does work! I have no idea how to get it to work.
I wanted to use the TextRenderer, but I don't know how to get an IDeviceContext of a control(and all examples I see on the internet use PaintEventArgs.Graphics in Paint event).
Also, if this is relevant, the GG PictureBox is a child of another picturebox, and has a transparent background.
I tried to refresh the box after invalidating, the working code:
if (GG != null)
{
((PictureBox)GG).Image = (Image)obj;
((PictureBox)GG).Invalidate();
((PictureBox)GG).Refresh();
using (Graphics g = ((PictureBox)GG).CreateGraphics()) {
g.DrawString(i["amount"].ToString(), kryptonRichTextBox1.Font,
new SolidBrush(Color.Gold), new Point(16, 18));
}
}
You modified the image content but the PictureBox is completely unaware of that. You didn't reassign its Image property. You will need to tell it that it needs to redraw the image as displayed on the screen. Add this line of code:
GG.Invalidate();
Just draw on a Bitmap and show it in the PictureBox:
// A new bitmap with the same size as the PictureBox
var bitmap = new Bitmap(pictureBox.Width, pictureBox.Height);
//Get the graphics objectm which we can use to draw
var graphics = Graphics.FromImage(bitmap);
//Draw stuff
graphics.DrawString(i["amount"].ToString(), kryptonRichTextBox1.Font,
new SolidBrush(Color.Gold), new Point(16, 18));
//Show the bitmap with graphics image in the PictureBox
pictureBox.Image = bitmap;
Image digidashboard = new Bitmap(Properties.Resources.digidashboard);
//using (Graphics g = ((PictureBox)pictureBoxDashboard).CreateGraphics())
//{
// g.DrawString("80.00", this.Font, new SolidBrush(Color.Red), 3, 6);
// pictureBoxUnlock.Image = digidashboard;
// pictureBoxDashboard.Invalidate();
//}
Graphics g = Graphics.FromImage(digidashboard);
g.DrawString("80.00", this.Font, new SolidBrush(Color.Red), 3, 6);
pictureBoxDashboard.Image = digidashboard;
According to StevenHouben's answer, I paste my C# version. It works fine. Thanks #StevenHouben.

How do i save the current frame/image in pictureBox1 to a bitmap file?

In the top of Form1 I did: Bitmap bmp; In the paint event I'm drawing to the pictureBox and also to the bmp file:
private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
SolidBrush brush;
Pen p = null;
Point connectionPointStart;
Point connectionPointEnd;
Graphics g = e.Graphics;
bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics bitmapGraphics = Graphics.FromImage(bmp);
//g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
moveCounter++;
label6.Text = moveCounter.ToString();
brush = new SolidBrush(Color.Red);
p = new Pen(brush);
for (int idx = 0; idx < wireObject1._point_X.Count; ++idx)
{
Point dPoint = new Point((int)wireObject1._point_X[idx], (int)wireObject1._point_Y[idx]);
dPoint.X = dPoint.X - 5; // was - 2
dPoint.Y = dPoint.Y - 5; // was - 2
Rectangle rect = new Rectangle(dPoint, new Size(10, 10));
g.FillEllipse(brush, rect);
bitmapGraphics.FillEllipse(brush, rect);
// g.FillEllipse(brush, rect);
}
for (int i = 0; i < wireObject1._connectionstart.Count; i++)
{
int startIndex = wireObject1._connectionstart[i];
int endIndex = wireObject1._connectionend[i];
connectionPointStart = new Point((int)wireObject1._point_X[startIndex], (int)wireObject1._point_Y[startIndex]);
connectionPointEnd = new Point((int)wireObject1._point_X[endIndex], (int)wireObject1._point_Y[endIndex]);
p.Width = 2;
g.DrawLine(p, connectionPointStart, connectionPointEnd);
bitmapGraphics.DrawLine(p, connectionPointStart, connectionPointEnd);
}
}
I did a new instance in the paint event for the bmp file. I also make a new graphics variable for the bmp file. And every place i draw or fill ellipse to the pictureBox I also draw it to the bmp file.
Now in the save function I did:
private void SavePictureBoxToBitmap()
{
//PbToBitmap++;
//String tempFile = #"d:\PictureBoxToBitmap\" + PbToBitmap.ToString("D6") + bmp;
bmp.Save(#"d:\PictureBoxToBitmap\bit.bmp");
}
If im doing bmp.Save it will save to the hard disk a white bmp file with the drawings inside only. If im doing pictureBox1.Image.Save it will save to the hard disk a file with the picture in the pictureBox only without the drawings.
How can i combine it so i will make one save line/command and it will create one bmp file on the hard disk with the pictureBox image and the drawings together?
When you want to draw something onto an Image, create a Graphics element using the static function FromBitmap:
Graphics gfx = Graphics.FromImage(pictureBox1.Image)
Then you draw things on the picture box by using the gfx instance, e.g. gfx.DrawLine.
Then to save it, use the pictureBox1.Image.Save function.
UPDATE
Your code is a bit too cryptic for me, but I made a simple example which might answer your question. I created a newq empty form, added a picture box, and then implemented the Paint function.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics gfx = Graphics.FromImage(pictureBox1.Image);
gfx.DrawLine(new Pen(Color.Red, 5), new Point(10, 10), new Point(20, 20));
gfx.DrawLine(new Pen(Color.Red, 5), new Point(20, 10), new Point(10, 20));
pictureBox1.Image.Save("test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
pictureBox1.Refresh(); // The file will be correct without this, but the update will not be shown
}
using (var bmp = new Bitmap(panel1.Width, panel1.Height))
{
pictureBox1.DrawToBitmap(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
bmp.Save("output.png", System.Drawing.Imaging.ImageFormat.Jpeg);
}
Should work

C# redaction drawing on Image

Whats going on is i need to draw a black rectangle over the image. I have to load a tif and then show a blackbox over it. I was helped with some code but i continously got the error: A Graphics object cannot be created from an image that has an indexed pixel format.
So i had to read it in to bit format, but when i display the box it resizes the box wierd. And completly displays the the picture box in all black nothing of the original image. if someone could help me where i'm going wrong that would be awesome.
Bitmap original = (Bitmap)System.Drawing.Image.FromFile(coveted, true);
Bitmap newImage = new Bitmap(original.Width, original.Height);
pictureBox1.Image = newImage;
using (Graphics g = Graphics.FromImage(pictureBox1.Image))
{
using (SolidBrush brush = new SolidBrush(Color.Black))
{
g.FillRectangle(brush, new Rectangle(x1value, y1value, x3value, y3value));
}
}
I'm not sure how I can make this clearer. Whats happening is I have a tif in a unsupported format. So I have to change it to a Bitmap so I can actually draw a rectangle on it. Then I need to display this redacted image (the original with the redaction) in a picturebox. Whats going on with the code above, is once it's completed, all it displays is a blackbox with no original image.
I believe i ran something about using a Bitmap from stream and then closing the stream. Anybody familiar with this?
Thanks to all the help from STO members!! heres the correct code for redacting images if you encounter the error "A Graphics object cannot be created from an image that has an indexed pixel format.".
if you're given the redacted starting points (obviously you have to make the Regex work to your situation):
//Regex for pulling points from a file
string x1 = x1 = Regex.Match(l, #"\r\n(\d+)\r\n(\d+)").Groups[2].Value;
string y1 = y1 = Regex.Match(l, #"\r\n(\d+)\r\n(\d+)\r\n(\d+)").Groups[3].Value;
string x2 = x2 = Regex.Match(l, #"\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)").Groups[4].Value;
string y2 = y2 = Regex.Match(l, #"\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)").Groups[5].Value;
string x3 = x3 = Regex.Match(l, #"\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)").Groups[6].Value;
string y3 = y3 = Regex.Match(l, #"\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)\r\n(\d+)").Groups[7].Value;
{
//convert string to int for redacted points
int x1value = Convert.ToInt32(x1);
int y1value = Convert.ToInt32(y1);
int x3value = Convert.ToInt32(x3);
int y3value = Convert.ToInt32(y3);
{
//BEGIN Workaround for indexed pixels
Bitmap original = (Bitmap)System.Drawing.Image.FromFile(YOURFILE, true);
Bitmap newImage = new Bitmap(original);
pictureBox1.Image = newImage; //END Workaround for indexed pixels
using (Graphics g = Graphics.FromImage(pictureBox1.Image)) //start redaction
{
using (SolidBrush brush = new SolidBrush(Color.Black))
{
g.DrawImageUnscaled(newImage, 0,0);
g.FillRectangle(brush, new Rectangle(x1value, y1value, x3value, y3value));
}
} //End Redaction
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage; //Resized to fit into a static picturebox
}
}
Instead of
Bitmap newImage = new Bitmap(original.Width, original.Height);
you want
Bitmap newImage = new Bitmap(original);
This will make your newImage start with the contents of original.
The difference will be that you will end up with newImage.PixelFormat == PixelFormat.Format32bppArgb, while I'm assuming original.PixelFormat == PixelFormat.Format1bppIndexed.
With PixelFormat.Format32bppArgb, you can create a Graphics object; you cannot with PixelFormat.Format1bppIndexed.
This should work for you:
original = (Bitmap)System.Drawing.Image.FromFile(coveted, true);
using (Graphics g = Graphics.FromImage(original))
{
using (SolidBrush brush = new SolidBrush(Color.Black))
{
g.FillRectangle(brush, new Rectangle(x1value, y1value, x3value, y3value));
}
}
pictureBox1.Image = original;
You aren't drawing the original image on the newImage.
To do so :
g.DrawImageUnscaled(original, 0, 0);
Also, I would do it as follows, I think it works better drawing in RAM, and then showing it to screen. Perhaps I'm wrong though.
using (Graphics g = Graphics.FromImage(newImage))
{
g.DrawImageUnscaled(original, 0, 0);
using (SolidBrush brush = new SolidBrush(Color.Black))
{
g.FillRectangle(brush, new Rectangle(x1value, y1value, x3value, y3value));
}
}
pictureBox1.Image = newImage;
I haven't tested the code above.. good luck though.

Categories

Resources