I have C# winForm application which holds a pictureBox control. This control has a Paint event. Every time paint event is fired , a Bitmap is dynamically created and I perform some drawing on it. And when user clicks "save" button , edited image is saved as jpg file.
It's OK until now. When I load a new image in pictureBox control remains of previous edits are still alive.
How can I erase the bitmap and start fresh each time I load a new image:
private void pb_Resim_Paint(object sender, PaintEventArgs e)
{
List<eVucutParcalari> list = new List<eVucutParcalari>(pointList.Keys);
// Loop through list
foreach (eVucutParcalari k in list)
{
Dictionary<Point, Color> dicItem = pointList[k];
foreach (KeyValuePair<Point, Color> pair in dicItem)
{
Point p = pair.Key;
Color c = pair.Value;
SolidBrush brush = new SolidBrush(c);
if (pb_Resim.Image == null)
return;
Bitmap bmp = new Bitmap(pb_Resim.Image);
Graphics gr = Graphics.FromImage(bmp);
gr.FillRectangle(brush, p.X, p.Y, 5, 5);
pb_Resim.Image = bmp;
}
}
}
Why not create a brand new bitmap when you load a new file, and replace the bitmap currently assigned to pb_Resim.Image on load? This would allow the old bitmap to be collected by the GC without requiring you to expend any effort "clearing" the previous bitmap, and ensure that you have a brand new, fresh bitmap without any residual junk of any kind for the newly loaded file.
Christian Graus wrote several articles on GDI+, which you can find on CodeProject. (Scroll down to the GDI+ articles.) Since they directly relate to the type of activity you are doing, I suggest looking through them.
I think you meant clear the picture box of the previous content which happens to be the bitmap object of an image file.
You have already done
new brush, new bitmap, getting new graphics canvas from the new bitmap.
Any chance of doing a new PictureBox?
Because that's what I did when my app had to quick-and-dirty repeatedly display a set of images. I'm sure there's a better way to clear the picturebox, like using refresh().
foreach ( ... )
{
pb_Resim = new PictureBox();
configPictureBoxDimensions();
...
}
Why don't you try refresh() first? It should work.
foreach ( ... )
{
pb_Resim = bmp;
pb_Resim.refresh();
...
}
Related
I do have following issue with my graphics object.
EDIT:
I do have a picturebox_image (imageRxTx) which is a live stream from a camera. What I do in the paint event is to draw some lines on top of the image imageRxTx (not shown in the code below). This works so far without problem.
Now I need to check for circles in imageRxTx and therefore I have to use the method ProcessImage() which needs a Bitmap as parameter. Unfortunately I do not have the Bitmap image but rather the handle (hDC) to my imageRxTx.
Question: How can I get the imageRxTx from my graphics-object and "convert" it to a bitmap-image which I need to use in the method ProcessImage(Bitmap bitmap)? This method needs to be called continously in the paint-event in order to check the live-stream of my camera (imageRxTx).
Here my code:
private void imageRxTx_paint(object sender, PaintEventArgs e)
{
var settings = new Settings();
// Create a local version of the graphics object for the PictureBox.
Graphics Draw = e.Graphics;
IntPtr hDC = Draw.GetHdc(); // Get a handle to image_RxTx.
Draw.ReleaseHdc(hDC); // Release image_RxTx handle.
//Here I need to send the picturebox_image 'image_RxTx' to ProcessImage as Bitmap
AForge.Point center = ProcessImage( ?....? );
}
// Check for circles in the bitmap-image
private AForge.Point ProcessImage(Bitmap bitmap)
{
//at this point I should read the picturebox_image 'image_RxTx'
...
The video image is updated here:
private void timer1_Elapsed(object sender, EventArgs e)
{
// If Live and captured image has changed then update the window
if (PIXCI_LIVE && LastCapturedField != pxd_capturedFieldCount(1))
{
LastCapturedField = pxd_capturedFieldCount(1);
image_RxTx.Invalidate();
}
}
As the title suggests, your main problem is a (common) misconception about what a Graphics object is.
So far I can draw to my graphics object without problem
No! A 'Graphics' object does not contain any graphics. It is only the tool used to draw graphics onto a related Bitmap. So you don't draw onto the Graphics object at all; you use it to draw onto imageRxTx, whatever that is, probably the surface of some Control or Form..
This line is using an often confusing rather useless format of the Bitmap constructor:
Bitmap bmp = new Bitmap(image_RxTx.Width, image_RxTx.Height, Draw);
The last parameter is doing next to nothing; its only function is to copy the Dpi setting. In particular it does not clone or copy any content from 'Draw', which, as you know now, a Graphics object doesn't have anyway, nor any of its other settings. So yes, the bmp Bitmap is still empty after that.
If you want to draw into bmp you need to use a Graphics object that is actually bound to it:
using (Graphics G = Graphics.FromImage(bmp)
{
// draw now..
// to draw an Image img onto the Bitmap use
G.DrawImage(img, ...);
// with the right params for source and destination!
}
None of this should probably happen in the Paint event! But all the preparation code is unclear as to what you really want to do. You should explain just what is the source of the drawing and what is the target!
If instead you want to get the stuff you draw onto image_RxTx into a Bitmap you can use this method somwhere outside (!) the Paint event:
Bitmap bmp = new Bitmap(image_RxTx.Width, image_RxTx.Height);
image_RxTx.DrawToBitmap(bmp, image_RxTx.ClientRectangle);
This will use the Paint event to draw the control into a Bitmap. Not that the result includes the whole PictureBox: The BackgroundImage, the Image and the surface drawing!
Update: To get the combined content of the PictureBox, that is both its Image and what you have drawn onto the surface, you should use the code above (the last 2 lines) in the Tick event of a Timer or right after the line that triggers the Paint event. (You didn't show us how that happens.) You can't acutally put it in the Paint event itself, as it will use the Paint event and therefore would cause an infinite loop!
The method Graphics.CopyFromScreen is probably what you're looking for.
var rect = myControl.DisplayRectangle;
var destBitmap = new Bitmap(rect.Width, rect.Height, PixelFormat.Format24bppRgb);
using (var gr = Graphics.FromImage(destBitmap))
{
gr.CopyFromScreen(myControl.PointToScreen(new Point(0, 0)), new Point(0, 0), rect.Size);
}
I want to finish my paint program for my Daughter and am having issues with the panel keeping drawings rendered on the panel when the application is minimized. I would also like for the program to automatically save to a file what she drew on the panel when she clicks on the "CLEAR" and/or "CLOSE" button.
If you want to keep your drawings you could instead of drawing in the graphics of the panel draw inside a Image of that panel nonetheless you can use Panel.DrawToImage() it should be like this
Panel.DrawToBitmap(Bitmap, Panel.ClientRectangle);
Bitmap bmp; //this will be the image where you would draw to
Graphics g; // the graphics
public ALANA_PAINT()
{
//do your stuff
bmp = new Bitmap(Width,Height)// Initialize the bitmap
Panel.BackgroundImage = bmp;
g = Graphics.FromImage(bmp);
}
//your normal drawing methods
public void Save()
{
bmp.Save(path,imageFormat);
}
For the close function the forms have a FormsClosing event that you could call you save from there
You need to not put the drawing code in pnl1_MouseMove against a new Graphics you get from pnl1.CreateGraphics();. You need to do one of two things.
1) Keep a list of all the "shapes" that need to be drawn and redraw, then subscribe to the event pn1.Paint += pn1_Paint and have that paint method go through the list of all the shapes you have recorded and re draw them
or
2) Don't paint to the Graphics object for the panel, call CreateGraphics on a Bitmap you have in the class then have that bitmap drawn as the background of pn1.
The benefit of method 2 is it will allow most of your code to be the same, you are now just drawing against a bitmap instead of directly against the panel.
you are going to need a method like this one for the 2nd part of your question :
public void SaveBitmap(string location)
{
Bitmap bmp = new Bitmap((int)myPanel.Width, (int)myPanel.Height);
DrawToBitmap(bmp, new Rectangle(0, 0, myPanel.Width, myPanel.Height));
using (FileStream saveStream = new FileStream(location + ".bmp", FileMode.OpenOrCreate))
{
bmp.Save(saveStream, ImageFormat.Bmp);
}
}
If you want to handle it when she closes the window, use the Closing event in the Window.
I have a timer tick event set to 250ms:
private void timer4_Tick(object sender, EventArgs e)
{
if (pictureboximagestosavecount == 72)
{
timer4.Enabled = false;
}
else
{
Bitmap bmp = new Bitmap(this.Width, this.Height);
Rectangle rect = new Rectangle(this.Location.X, this.Location.Y, this.Width, this.Height);
pictureboximagestosavecount++;
savePictureBox(pictureBox1, #"c:\temp\pboximages\" + pictureboximagestosavecount.ToString("D6") + "pbimg.gif");
this.DrawToBitmap(bmp, rect);
bmp.Save(#"c:\temp\pboximages\" + pictureboximagestosavecount.ToString("D6") + "form.gif");
}
}
First I'm saving the pictureBox as gif using the method savePictureBox.
Second I save the form1:
bmp.Save(#"c:\temp\pboximages\" + pictureboximagestosavecount.ToString("D6") + "form.gif");
After timer4 is stopped I have abutton click event where I create animated gif from the saved files:
private void animatedgifbutton_Click(object sender, EventArgs e)
{
DirectoryInfo di1;
FileInfo[] fi1;
di1 = new DirectoryInfo(#"c:\temp\pboximages\");
fi1 = di1.GetFiles("*form.gif");
List<string> newImages = new List<string>();
for (int i = 0; i < fi1.Length; i++)
{
newImages.Add(fi1[i].FullName);
}
animatedgif.MakeGIF(newImages, #"c:\temp\pboximages\animated1.gif", 6, true);
}
When I'm doing *.form.gif and I see in the List newImages only the form gifs files the animatedgif.MakeGIF throw error since it need to get List of gifs files but I guess that when I save the form it's saving it as bitmap and not real gif.
How can I take a screenshot of form1 and save it as real gif ?
When I save the pictureBox1 to the hard disk it is GIF and there are no problems.
The problem is with saving the form.
EDIT:
I also saw now that the way I'm saving a screenshot of the form1 is not good it's not saving the whole form area only part of it. I gues using this bmp and rect is not good.
This is what I get for example when I'm saving a screenshot of the form:
In other words I need in timer4 tick event to save the form screen the whole form to the hard disk as a gif file real gif file not bmp.
It doesn't save as a proper GIF because you don't tell it to. The extension is not enough, you need to add the ImageFormat to the Save call!
And it doesn't save the whole area of the Form because you don't use the right Rectangle to control it.
Both issues go away very simply if you use the modified savePictueBox function:
void saveControl(Control Ctl, string fileName)
{
Rectangle Rect = Ctl.ClientRectangle;
// if (Ctl is Form) Rect = Ctl.Bounds; // (*)
using (Bitmap bmp = new Bitmap(Rect.Width, Rect.Height))
{
Ctl.DrawToBitmap(bmp, Rect );
bmp.Save(fileName, System.Drawing.Imaging.ImageFormat.Gif);
}
}
Note that this uses the ClientRectangle, that is, it leaves out the Form border. If you want if to include the Border you can uncomment the line (*)
Now you can call it as
saveControl(pictureBox1, yourFileName);
and as
saveControl(this, yourOtherFileName);
Two notes on Animated Gifs:
Since you are recording the Gifs in real-time don't try to create the animation at the same time!
I found this post interesting. fireydude's answer does work, once you have included all those references from the WPF world..Not sure how to get the best quality and unfortunately there is no control over the timing, so far..
I have a List<> object, "imagelist", that contains the paths of many images such as .png's. Now, with the following code:
private void paint_picture(PictureBox picture, string pathofpic)
{
Graphics g = picture.CreateGraphics();
Bitmap drawnpic = null;
if (imagelist.ContainsKey(picture.Name))
{
drawnpic = new Bitmap(pathofpic);
g.DrawImage(drawnpic, 0, 0, picture.Size.Width, picture.Size.Height);
imagelist[picture.Name] = pathofpic;
}
drawnpic.Dispose();
g.Dispose();
}
I call this every time the card's image is changed, but I can't seem to make the image persist on the picturebox, when I drag the picturebox across the form (for example, over other pictureboxes). The click and drag code is just moving the picturebox with the mouse, not really relevant.
I've tried invalidating the form when I de-select the image, but it doesn't do anything.
Is there something I'm missing? Screenshot below, I dragged one image around the form and it overwrote the other images it moved across:
That’s how painting works – you have to handle its Paint event and keep painting the same thing each time it needs repainting.
What you can do is draw on top of your original image:
Bitmap b = new Bitmap(picture.Image);
using (Graphics g = Graphics.FromImage(b)) {
using (Bitmap drawnpic = new Bitmap(pathofpic)) {
g.DrawImage(drawnpic, 0, 0, b.Width, b.Height);
}
}
picture.Image = b;
Then you’d save the original image somewhere and probably use it instead of picture.Image in the new Bitmap line.
And PascalCase for method names, please. ;)
I have a pictureBox on a Windows Form.
I do the following to load a PNG file into it.
Bitmap bm = (Bitmap)Image.FromFile("Image.PNG", true);
Bitmap tmp;
public Form1() {
InitializeComponent();
this.tmp = new Bitmap(bm.Width, bm.Height);
}
private void pictureBox1_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawImage(this.bm, new Rectangle(0, 0, tmp.Width, tmp.Height), 0, 0, tmp.Width, tmp.Height, GraphicsUnit.Pixel);
}
However, I need to draw things on the image and then have the result displayed again. Drawing rectangles can only be done via the Graphics class.
I'd need to draw the needed rectangles on the image, make it an instance of the Image class again and save that to this.bm
I can add a button that executes this.pictureBox1.Refresh();, forcing the pictureBox to be painted again, but I can't cast Graphics to Image. Because of that, I can't save the edits to the this.bm bitmap.
That's my problem, and I see no way out.
What you need to do is use the Graphics.FromImage method, which will allow you to draw directly on the image instead of the temporary Graphics object create from within the Paint method:
using (Graphics g = Graphics.FromImage(this.bm))
{
g.DrawRectangle(...);
}
Do this instead of (or in addition to) hooking the Paint method of the PictureBox. This way, you won't need to use a temporary image or Graphics object at all, and when you're finished modifying the original bitmap (this.bm) then you can invoke pictureBox1.Refresh to force the image to be re-displayed.