Get Bitmap from PictureBox drawn Region

leorob88

Member
Joined
Dec 31, 2020
Messages
23
Programming Experience
1-3
Hello, I'm trying to solve a problem with graphics and drawing. To start with, i define a picturebox region, then with fillrectangle i draw into that region.

What i would do now is creating a Bitmap from that drawn region (because then i need to use bitmap.getpixel to check colors) but I'm kinda stuck on this.

I tried to do:
Bitmap img = (Bitmap)picturebox.image
or even
Bitmap img = new Bitmap((Bitmap)picturebox.image, new size (470,340))
but when i try it or try to operate with it, i get error, saying the parameter cannot be null. It suggests image as an error, like it doesn't properly get an image while converting to bitmap. What i simply need is to convert that drawn region into a Bitmap however, in any possible way.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
4,026
Location
Chesapeake, VA
Programming Experience
10+
Post the exact error you are getting. Don't paraphrase or summarize.

Out of curiosity, how did you get the bitmap into the PictureBox in the first place? Shouldn't you just have held on to a reference of that bitmap? Also what is the significance of the PictureBox here? Your question is centered around actual image manipulation/processing. That can be done without any UI at all. Why even involve a picturebox?
 

leorob88

Member
Joined
Dec 31, 2020
Messages
23
Programming Experience
1-3
Well, on your second question, i have two picture boxes: one has an image, another is above that to draw some shapes (which i have to set visible true or false, so i can just set that to the picturebox.visible), to draw those shapes in the second picturebox i first set the region and then go with graphics.fillrectangle (so only the region gets filled and the rest doesn't, so the image below is still visible where the region doesn't get filled).
The error i get is
System.ArgumentNullException: "The value cannot be null. Parameter name: image".

The value cannot be null. Parameter name: ---> sorry, this i try to translate, i'm italian and so is my software, so i tried to translate in english the error message i get.
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,190
Location
Sydney, Australia
Programming Experience
10+
Nothing you draw on a PictureBox in its Paint event handler has anything at all to do with its Image property. That property only has a value if you assign something to it. Drawing on a control doesn't create an Image object. It's just pixels on the screen.

If you want an Image then you have to create one. You can do that before or after the fact. Before would be creating an Image object, drawing on that and then either drawing that onto the PictureBox or assigning it to the Image property. After would be putting your drawing code in its own method with a Graphics parameter, then using that same method to draw on a new Image object after using it to draw on the PictureBox.
 

leorob88

Member
Joined
Dec 31, 2020
Messages
23
Programming Experience
1-3
@jmcilhinney I think I understand. I'm after the "after" hypothesis, or rather what i need is just creating an Image to convert it then into a Bitmap. Now my question is: can I cyclically draw on the same Image (is it so obvious i'm new to graphics?)? I.e. like creating an Image drawing on a "canvas" in a specific point and then drawing somewhere else but always on the same canvas, the same Image.
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,190
Location
Sydney, Australia
Programming Experience
10+
You can draw on the same Image object as often as you like but, unlike drawing on a control, previous drawing doesn't get erased as a matter of course. That also means that there's no need to draw the same thing multiple times. If that's what you want, by all means use the same Image object and draw multiple times.
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,190
Location
Sydney, Australia
Programming Experience
10+
If you had this code to draw on a PictureBox:
C#:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.DrawString("Hello World", Font, Brushes.Black, 10.0F, 10.0F);
}
then you could replace it with this:
C#:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
    DoDrawing(e.Graphics);
}

private void DoDrawing(Graphics g)
{
    g.DrawString("Hello World", Font, Brushes.Black, 10.0F, 10.0F);
}
and then do this:
C#:
var img = new Bitmap(100, 100);

using (var g = Graphics.FromImage(img))
{
    DoDrawing(g);
}
The drawing code can be as complex as you like. One thing I've done in a demo before is create a Bitmap and assign it to the Image property, store drawing data in fields and draw on the PictureBox until the user clicks a Button, then draw the same data on the Bitmap to make it permanent, then clear the fields. This would enable you to discard all changes since the last "save" while everything looks the same to the user whether the drawing is on the control or the Image.
 

leorob88

Member
Joined
Dec 31, 2020
Messages
23
Programming Experience
1-3
If you had this code to draw on a PictureBox:
C#:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.DrawString("Hello World", Font, Brushes.Black, 10.0F, 10.0F);
}
then you could replace it with this:
C#:
private void PictureBox1_Paint(object sender, PaintEventArgs e)
{
    DoDrawing(e.Graphics);
}

private void DoDrawing(Graphics g)
{
    g.DrawString("Hello World", Font, Brushes.Black, 10.0F, 10.0F);
}
and then do this:
C#:
var img = new Bitmap(100, 100);

using (var g = Graphics.FromImage(img))
{
    DoDrawing(g);
}
The drawing code can be as complex as you like. One thing I've done in a demo before is create a Bitmap and assign it to the Image property, store drawing data in fields and draw on the PictureBox until the user clicks a Button, then draw the same data on the Bitmap to make it permanent, then clear the fields. This would enable you to discard all changes since the last "save" while everything looks the same to the user whether the drawing is on the control or the Image.
I'll try this and let you know! It would keep me from building an entire dictionary to define paint exceptions!
 

leorob88

Member
Joined
Dec 31, 2020
Messages
23
Programming Experience
1-3
Sorry but maybe it's me not understanding. I tried using e.Graphics.DrawRectangle in the Paint event handler, supposing that would properly create an Image, but when i go storing into a Bitmap the picturebox.image it sends me an error. My code is:

"for" cycle //to cyclically draw to picturebox
{
Rectangle pun = new Rectangle(new Point(x, y), new Size(wid, hei));
e.Graphics.DrawRectangle(new Pen(Color.Black, 1), pun);
}
Bitmap img = (Bitmap)picturebox.Image; //***
GraphicsPath map = new GraphicsPath();
//cycle to add Rectangles to map
map.CloseFigure();
picturebox.Region = new Region(map);

It says System.NullReferenceException (i'm italian, sorry, but it says like "Reference to an unset object/element". If i change the line signed as //*** with
Bitmap img = new Bitmap((Bitmap)picturebox.Image, 480, 340)
it says instead System.ArgumentNullException: The value cannot be null. Parameter name: Image.
You say in your demo you store the image into the Bitmap so you can clear the picturebox and still retain that image. That's exactly what i'm missing, HOW do you store that into the Bitmap?
 
Last edited:

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
4,190
Location
Sydney, Australia
Programming Experience
10+
I told you exactly what to do and you simply haven't done it. If you don't follow the instructions provided then why would you expect to get the desired result?

Step 1: Create a Paint event handler for your PictureBox and use e.Graphics to draw as desired.

Do that first and ONLY when that works as desired, move on to the next step.

Step 2: Create a method with a parameter of type Graphics. Copy the code from your Paint event handler into that method and replace all references to e.Graphics with the parameter. Replace the code in the Paint event handler with a call to that method and pass e.Graphics as an argument.

Do that and ONLY when that works as desired, move on to the next step.

Step 3: Create a Graphics object by calling Graphics.FromImage and passing the appropriate Bitmap as an argument, then call the method created in step 2 and pass that Graphics object as an argument.

You have now drawn the same thing on both your PictureBox and your Bitmap.

By the way, please don't post unformatted code snippets. You can see the difference between the code I posted and what you posted. Please do us the courtesy of making the code you post easy to read.
 

leorob88

Member
Joined
Dec 31, 2020
Messages
23
Programming Experience
1-3
Basically, it works. It draws Rectangles in the right places. Now, my problem comes to the point when i need to refer to that Bitmap to create a Region to re-draw on the Picturebox.
I literally used the img name like in your code. Now i need to create a new Region for the picture, based on that Bitmap. First i create a GraphicsPath to store data for the Region.
My goal, precisely, is to check the Bitmap every 10x10 area. If the first pixel is Black (which it should be), it adds a Rectangle in the same "pixel" position in the GraphicsPath.
Suppose you check every 10x10 area in a cycle, it comes to the point when it's checking an area into the Bitmap corresponding to the pixel 10;0: my code now is going to check if that pixel in the Bitmap is black (i need to check 10x10 areas, but for this case if the first pixel is Black it's just enough, it means in the Bitmap i drawed a 10x10 Rectangle there); if it is, then i add in the GraphicsPath a Rectangle 10x10 in that position, 10;0.
This, as i said, is cyclically made and handled, inside the same Paint event. Problem is, i don't get any error message, but the problem seems to be when i check the pixel color. My condition to check is

C#:
if (img.GetPixel(x * 10, y * 10).Equals(Color.Black))

but debugging this it seems it doesn't see that as a black pixel. Just for the sake of comprehension, x and y are int to check the picture every 10x10 area (as i said). Meaning, if i remove this condition it keeps as Region the full Picturebox, if i use this condition instead the Region becomes empty, like it doesn't see Color.Black in the checked pixels. So, i suppose i'm wrongly referring to the Bitmap to draw the region?
 
Last edited:
Top Bottom