Saving and loading bmp using graphics object

V

Vin

Hi,

I am using the following code to draw whatever the user draws using
x,y.

// draws lines directly on a winform.
CreateGraphics().DrawLine(APen, x, y, OldX, OldY);

Now how do I save the drawing on to a bmp file on my harddisk? C# code
in this regard would be very helpful. I tried all forums but invain.

I tried with Graphics object and then

objBitmap=new Bitmap(400, 200, objGraphics);
objBitmap.Save(@"c:\test.bmp", ImageFormat.Bmp);

which didn't work, leaving me with a blank test.bmp.

Also help me load a bmp from hard disk to this form? C# code please.

Anyone out there who could help me?

Cheers
Vin
 
M

Morten Wennevik

Hi Vin,

The simplest way might be to create a Bitmap the size of your window and
either draw on both Bitmap and graphics or draw on the bitmap and use
DrawImage to draw the bitmap on the graphics.
This way you can also load the bitmap from a file and use DrawImage to
paste it back to the screen

However, this way won't store any undo information. To do this you would
have to store each line drawn in some array or other, possibly a
GraphicsPath.

Your code won't work because simply using DrawLine doesn't retain
information. If something covers your window or you minimize/maximize it
the drawing will be gone. You might want to read Bob Powell's Faq in this
regard.

http://www.bobpowell.net/faqmain.htm
 
V

Vin

Thanks Morten,

I understand what you want to say.
But in terms of code I have no clue what you are saying.

A c# code snippet would surely do a world of good to me.
GraphicsPath and all, I am still a novice. Even bob powell's FAQ are a
too elaborative and I got lost somewhere.

Help much appreciated

Thanks
Vin
 
I

Ian Griffiths [C# MVP]

First of all, this is not a good way to draw onto the window. You'll find
that if anything causes the window to need repainting (e.g. it is obscured
by some other window, or resized, or minimized, or is moved partially off
the screen and then back on) you'll lose all your drawing.

This is because there is nothing that actually stores what you've drawn onto
the window - you're just drawing onto the screen.

Windows has always relied on the ability of applications to recreate their
appearance on demand. It sends them a message (WM_PAINT) whenever it wants
them to repaint themselves, and in .NET this is manifest through the OnPaint
event in Windows Forms.

So the fact that nothing is actually storing your drawing output should make
it fairly clear that there isn't any method you can call to save the
output...

In fact the output might not even make it onto the screen - if you run the
code below when the window happens to be obscured or minimized, that drawing
won't go anywhere at all, not even onto the screen.

Here's what you should do:

public class MyForm : Form
{
private Bitmap bmp;

... and in whatever function it was that you were going to do your
drawing,
put this code....

bmp = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
using (Graphics g = Graphics.FromImage(bmp)
{
g.DrawLine(APen, x, y, OldX, OldY);
}

...

protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(bmp, 0, 0);

base.OnPaint(e);
}

}


And now you'll be able to save that image with this code:

bmp.Save("MyBitmap.bmp", ImageFormat.Bmp);


What's been done here is that we've created a Bitmap object to hold the
image. If you want to have the image stored somewhere it's your
responsibility to make that happen, so in this example, that's why we create
the Bitmap.

By overriding OnPaint, we make sure that the contents of the Bitmap get
shown on the screen whenever Windows asks us to refresh our application's
appearance.

And because we've stored the drawing inside a Bitmap, we can now save it out
to disk whenever we like using the code shown above.

To load it back from disk, do this:

bmp = new Bitmap("MyBitmap.bmp");

Hope that helps.
 
V

Vin

Thanks Ian, you've been of great help.
I did what you suggested and it seems to work. But I got couple of
problems.

1> I can't see my lines being drawn on mousemove. When I move the
window, resize it the drawing gets shown. but no on mouse move.

2> When I save the bitmap, the bmp file is black, where as my form
suface is white. No clue what's going wrong here.

Here's my code.

Kindly point me where am I making a mistake.

private Pen APen;
private int OldX;
private int OldY;

private void DrawPoint(int x, int y)
{
using (objGraphics = Graphics.FromImage(objBitmap))
{
objGraphics.DrawLine(APen, x, y, OldX, OldY);
}
}

protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(objBitmap, 0, 0);
base.OnPaint(e);
}

private void ResetOffset(int x, int y)
{
ResetOffset(x, y, false);
}

private void ResetOffset(int x, int y, bool Reset)
{
OldX = x + System.Convert.ToInt32(Reset);
OldY = y + System.Convert.ToInt32(Reset);
}

private void Scribbler_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
StatusBar1.Text = string.Format("X:{0:d},Y:{0:d}", e.X, e.Y);
if ((e.Button == MouseButtons.Left))
{

DrawPoint(e.X, e.Y);
}
ResetOffset(e.X, e.Y, e.Button == MouseButtons.None);
}

private void Scribbler_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)
{
DrawPoint(e.X, e.Y);
}

private void Button1_Click(object sender, System.EventArgs e)
{
APen = new Pen(Color.Red);
}

private void Button2_Click(object sender, System.EventArgs e)
{
APen = new Pen(Color.Blue);
}

private void Button3_Click(object sender, System.EventArgs e)
{
APen = new Pen(Color.Green);
}

private void Button4_Click(object sender, System.EventArgs e)
{
APen = new Pen(Color.Black);
}

private void btnSave_Click(object sender, System.EventArgs e)
{
objBitmap.Save(@"c:\Save.bmp", ImageFormat.Bmp);
objBitmap.Dispose();
objGraphics.Dispose();
}

private void Scribbler_Load(object sender, System.EventArgs e)
{
objBitmap = new Bitmap(ClientRectangle.Width,
ClientRectangle.Height);
}

Thanks
Vin
 
I

Ian Griffiths [C# MVP]

Windows needs to know that part of your window needs redrawing. Remember
that you're now drawing into an off-screen bitmap, and Windows doesn't know
that this bitmap is a copy of what you want to appear on screen. (Windows
has no intrinsic support for this idea of a window with some bitmap backing
store, so it just plain doesn't understand the idea that a bitmap might be
associated with what's on screen.)

So you need to tell Windows. The way to do this is to call the Invalidate
method on your control. You could just call it with no parameters - that
will refresh the whole control. However, that can be a little slow - a more
efficient way of doing it would be to call the one that takes a rectangle,
telling it which part just changed. When you do either of these, your
OnPaint method will get called, allowing the results to appear on screen.

So here's the simple but slow version:

private void DrawPoint(int x, int y)
{
using (objGraphics = Graphics.FromImage(objBitmap))
{
objGraphics.DrawLine(APen, x, y, OldX, OldY);
}

Invalidate();
}

I'll leave it as an exercise to work out the faster version. ;-)


Also, you're going to have to think about what you want to do when the
window is resized. Resizing the bitmap is one option, but to do that you'd
have to build a new one, and then draw the existing one into the new one.
Or you could just pick a bitmap size up front and not resize it. (So think
about what Windows PaintBrush does. It doesn't keep resizing the bitmap
every time you resize the window. The bitmap size remains the same until
you change it.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top