How to save to a bitmap?

L

Lee

I have the following code


using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
namespace FINN
{

public class flickerFreePictureBox : PictureBox
{
public ArrayList Lines = new ArrayList();
public flickerFreePictureBox()
{

this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle (ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
//this.MouseMove += new MouseEventHandler(_MouseMove);
}
protected override void OnPaint(PaintEventArgs e)
{
bool error = false;
//e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
try
{
foreach(scribbleObject al in Lines)
{
bool Drawdot = false;
try
{
Point[] linelist = new Point[
al.linePositions.Count];

al.linePositions.CopyTo(0,linelist,0,al.linePositions.Count);
e.Graphics.DrawLines(al.pen(), linelist);
}
catch
{
Drawdot = true; //we want to draw a dot
}
if(Drawdot)
{
//Draw a single dot as the user has only
clicked, rather than clicked and dragged
//TODO: Draw a dot
}

}
}
catch
{
error = true;
}
if(error)
{
//this.OnPaint(e);
}


}
}

}

And I would like to be able to create a bitmap of what gets drawn in
OnPaint so that I can save it to file, I've tried a few things, but so
far just ended up with black bitmaps the same size as the control that
code creates, any suggestions or code snippets would be a great help.

Cheers

Lee
 
M

Morten Wennevik

Hi Lee

Before Bob Powell notices you are using a PictureBox for drawing, you
should read the GDI+ FAQ. Specifically the article regarding PictureBox

http://www.bobpowell.net/pictureboxhowto.htm

As for saving the Bitmap you could inside your Paint procedure separate
actual drawing to a method that you pass either the Paint graphics object,
or a Bitmap graphics object, otherwise refer to the FAQ
 
L

Lee

I've done the following

In OnPaint added this

Bitmap bmp = new Bitmap(this.Width, this.Height);
Graphics g = Graphics.FromImage(bmp);

Then for my painting method, simply duplicated it so that it paints to
g as well.
Now I get a nice black bitmap and not a lot else :(

Thanks for the link to the FAQ but I'm not sure how I can refactor my
current code to make use of it, due to deadlines I think it might have
to wait for the next project.

Cheers
 
L

Lee

Yep that is exactly what I'm doing, but all I get back is a black
bitmap.

I've tried also: Graphics g = Graphics.FromImage(this.Image);

which yeilds the same results

Simon said:
Graphics g = Graphics.FromImage(bmp);

The above line is good.
Therefore do you then do all of the drawing with THAT graphics object?
Then after the drawing has finished save the bitmap (bmp) to disk?

That (from what I recall) should do the trick.

Simon Tamman
 
S

Simon Tamman

Graphics g = Graphics.FromImage(bmp);

The above line is good.
Therefore do you then do all of the drawing with THAT graphics object?
Then after the drawing has finished save the bitmap (bmp) to disk?

That (from what I recall) should do the trick.

Simon Tamman

Lee said:
I've done the following

In OnPaint added this

Bitmap bmp = new Bitmap(this.Width, this.Height);
Graphics g = Graphics.FromImage(bmp);

Then for my painting method, simply duplicated it so that it paints to
g as well.
Now I get a nice black bitmap and not a lot else :(

Thanks for the link to the FAQ but I'm not sure how I can refactor my
current code to make use of it, due to deadlines I think it might have
to wait for the next project.

Cheers
 
L

Lee

Got it working, here is the entire code


using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
namespace FINN
{

public class flickerFreePictureBox : PictureBox
{
public ArrayList Lines = new ArrayList();
public Bitmap bmp;
private Graphics g;
public flickerFreePictureBox()
{
bmp = new Bitmap(800, 310);
g = Graphics.FromImage(bmp);
g.FillRectangle(System.Drawing.Brushes.White, 0,0, 800, 310);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
//this.MouseMove += new MouseEventHandler(_MouseMove);

}
protected override void OnPaint(PaintEventArgs e)
{
bool error = false;

//e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
try
{


foreach(scribbleObject al in Lines)
{
bool Drawdot = false;
try
{
Point[] linelist = new Point[al.linePositions.Count];
al.linePositions.CopyTo(0,linelist,0,al.linePositions.Count);

e.Graphics.DrawLines(al.pen(), linelist);
g.DrawLines(al.pen(), linelist);

}
catch
{
Drawdot = true; //we want to draw a dot
}
if(Drawdot)
{
//Draw a single dot as the user has only clicked, rather than
clicked and dragged
//TODO: Draw a dot
}
}
}
catch
{
error = true;
}
if(error)
{
//this.OnPaint(e);
}
bmp.Save("Test.bmp");

}
}

}


I know there are magic numbers in there, but for some reason
'this.Width' etc wasn't working when I tried it on the setup, got too
many other things to do to refine it, but if I get some time I'll make
it neater, cheers for your help all
 
S

Simon Tamman

Eh?

Open a new windows project, drop a picture box onto the form and paste the
following code in.
That should work.
This code not only saves the file to disk (c:\test.bmp) but also reloads it
from disk to the picturebox and works fine for me.

Then track back from this code onto your code and see where you're going
wrong.

private string path = "c:\\test.bmp";

public Form1()
{
InitializeComponent();
this.Load += new EventHandler(Form1_Load);
}

void Form1_Load(object sender, EventArgs e)
{
using (Bitmap b = new Bitmap(100, 100))
{
using (Graphics g = Graphics.FromImage(b))
{
using (SolidBrush sb = new SolidBrush(Color.Red))
{
g.FillRectangle(sb, 0, 0, 50, 50);
}
using (SolidBrush sb = new SolidBrush(Color.Green))
{
g.FillRectangle(sb, 50, 50, 50, 50);
}
b.Save(path, System.Drawing.Imaging.ImageFormat.Bmp);
}
}
LoadBitmap();
}

private void LoadBitmap()
{
Bitmap b = (Bitmap)Bitmap.FromFile(path);
this.pictureBox1.Image = b;
}
 
S

Simon Tamman

Glad to hear you got it working.
I'd re-iterate Morten's advice, Bob Powells GDI+ FAQ is a great FAQ to read
on this kind of thing.

Additionally i'd watch out for the way you're performing the painting the
segment, performing a try catch to determine the user's action is a BAD way
to work things out you'd be better off working out what the user did without
using exceptions.
Secondly I know you currently have it commented out but calling OnPaint
within OnPaint is scary recursion which will lead you into the badness of
StackOverflowExceptions.

I'd recommend reading:
http://www.amazon.co.uk/Framework-Design-Guidelines-Conventions-Libraries/dp/0321246756
for a better understanding of exception handling (it has an excellent
chapter on it), it's also a pretty good book for plenty of other reasons.

HTH

Simon


Lee said:
Got it working, here is the entire code


using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
namespace FINN
{

public class flickerFreePictureBox : PictureBox
{
public ArrayList Lines = new ArrayList();
public Bitmap bmp;
private Graphics g;
public flickerFreePictureBox()
{
bmp = new Bitmap(800, 310);
g = Graphics.FromImage(bmp);
g.FillRectangle(System.Drawing.Brushes.White, 0,0, 800, 310);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
//this.MouseMove += new MouseEventHandler(_MouseMove);

}
protected override void OnPaint(PaintEventArgs e)
{
bool error = false;

//e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
try
{


foreach(scribbleObject al in Lines)
{
bool Drawdot = false;
try
{
Point[] linelist = new Point[al.linePositions.Count];
al.linePositions.CopyTo(0,linelist,0,al.linePositions.Count);

e.Graphics.DrawLines(al.pen(), linelist);
g.DrawLines(al.pen(), linelist);

}
catch
{
Drawdot = true; //we want to draw a dot
}
if(Drawdot)
{
//Draw a single dot as the user has only clicked, rather than
clicked and dragged
//TODO: Draw a dot
}
}
}
catch
{
error = true;
}
if(error)
{
//this.OnPaint(e);
}
bmp.Save("Test.bmp");

}
}

}


I know there are magic numbers in there, but for some reason
'this.Width' etc wasn't working when I tried it on the setup, got too
many other things to do to refine it, but if I get some time I'll make
it neater, cheers for your help all
Lee said:
Yep that is exactly what I'm doing, but all I get back is a black
bitmap.

I've tried also: Graphics g = Graphics.FromImage(this.Image);

which yeilds the same results
 
L

Lee

OnPaint is commented out for exactly that reason! :)

I agree with you on the try/catch.

I'm fully aware that the code is a horrible hack, it was written months
ago and was my first real attempt and dealing with System.Drawing et
al. Unfortunately deadlines loom and I don't have time to refactor
it...

The moral of the story: get it right the first time.

Simon said:
Glad to hear you got it working.
I'd re-iterate Morten's advice, Bob Powells GDI+ FAQ is a great FAQ to read
on this kind of thing.

Additionally i'd watch out for the way you're performing the painting the
segment, performing a try catch to determine the user's action is a BAD way
to work things out you'd be better off working out what the user did without
using exceptions.
Secondly I know you currently have it commented out but calling OnPaint
within OnPaint is scary recursion which will lead you into the badness of
StackOverflowExceptions.

I'd recommend reading:
http://www.amazon.co.uk/Framework-Design-Guidelines-Conventions-Libraries/dp/0321246756
for a better understanding of exception handling (it has an excellent
chapter on it), it's also a pretty good book for plenty of other reasons.

HTH

Simon


Lee said:
Got it working, here is the entire code


using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
namespace FINN
{

public class flickerFreePictureBox : PictureBox
{
public ArrayList Lines = new ArrayList();
public Bitmap bmp;
private Graphics g;
public flickerFreePictureBox()
{
bmp = new Bitmap(800, 310);
g = Graphics.FromImage(bmp);
g.FillRectangle(System.Drawing.Brushes.White, 0,0, 800, 310);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
//this.MouseMove += new MouseEventHandler(_MouseMove);

}
protected override void OnPaint(PaintEventArgs e)
{
bool error = false;

//e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
try
{


foreach(scribbleObject al in Lines)
{
bool Drawdot = false;
try
{
Point[] linelist = new Point[al.linePositions.Count];
al.linePositions.CopyTo(0,linelist,0,al.linePositions.Count);

e.Graphics.DrawLines(al.pen(), linelist);
g.DrawLines(al.pen(), linelist);

}
catch
{
Drawdot = true; //we want to draw a dot
}
if(Drawdot)
{
//Draw a single dot as the user has only clicked, rather than
clicked and dragged
//TODO: Draw a dot
}
}
}
catch
{
error = true;
}
if(error)
{
//this.OnPaint(e);
}
bmp.Save("Test.bmp");

}
}

}


I know there are magic numbers in there, but for some reason
'this.Width' etc wasn't working when I tried it on the setup, got too
many other things to do to refine it, but if I get some time I'll make
it neater, cheers for your help all
Lee said:
Yep that is exactly what I'm doing, but all I get back is a black
bitmap.

I've tried also: Graphics g = Graphics.FromImage(this.Image);

which yeilds the same results

Simon Tamman wrote:
Graphics g = Graphics.FromImage(bmp);

The above line is good.
Therefore do you then do all of the drawing with THAT graphics object?
Then after the drawing has finished save the bitmap (bmp) to disk?

That (from what I recall) should do the trick.

Simon Tamman

I've done the following

In OnPaint added this

Bitmap bmp = new Bitmap(this.Width, this.Height);
Graphics g = Graphics.FromImage(bmp);

Then for my painting method, simply duplicated it so that it paints to
g as well.
Now I get a nice black bitmap and not a lot else :(

Thanks for the link to the FAQ but I'm not sure how I can refactor my
current code to make use of it, due to deadlines I think it might have
to wait for the next project.

Cheers

Morten Wennevik wrote:
Hi Lee

Before Bob Powell notices you are using a PictureBox for drawing, you
should read the GDI+ FAQ. Specifically the article regarding PictureBox

http://www.bobpowell.net/pictureboxhowto.htm

As for saving the Bitmap you could inside your Paint procedure separate
actual drawing to a method that you pass either the Paint graphics
object,
or a Bitmap graphics object, otherwise refer to the FAQ
 
T

Tim Van Wassenhove

public class flickerFreePictureBox : PictureBox


Assuming you have an instance named myFlickerFreePictureBox:

int width = myFlickerFreePictureBox.Width;
int height = myFlickerFreePictureBox.Height;

Bitmap bitmap = new Bitmap(width, height);
myFlickerFreePictureBox.DrawToBitmap(bitmap, new Rectangle(0, 0, width, height));

bitmap.Save(@"C:\somewhere.gif", ImageFormat.Gif);
 
B

Bob Powell [MVP]

Despite Morten's fears for your health, Congratulations, you've done it in
more or less the right way by overriding the control and staying on top of
the functionality using "OOP" rather than "POO" which is, of course, the
opposite of OOP.

I assume that somewhere you loaded an image and put it into the Image
property of the flickerFreePictureBox so...

All you need to do is:

#1 refactor your onpaint so that you can call a PaintObjects method handing
in a Graphics object.

#2 In OnPaint, call PaintObjects(e.Graphics) which will iterate your list
and draw your lines and dots.

#3In the Save method do this...

// this code may not compile but you'll see the sense of it...

Bitmap bm=new Bitmap(orixinal.Width, original.Height, PixelFormat.24BppRGB);
Graphics g=Graphics.FromImage(bm);
g.DrawImageUnscaled(original);
PaintObjects(g);
g.Dispose();
bm.Save(<some filename>,<Some format>);

Hope this helps.

--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.



Lee said:
I have the following code


using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
namespace FINN
{

public class flickerFreePictureBox : PictureBox
{
public ArrayList Lines = new ArrayList();
public flickerFreePictureBox()
{

this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle (ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
//this.MouseMove += new MouseEventHandler(_MouseMove);
}
protected override void OnPaint(PaintEventArgs e)
{
bool error = false;
//e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
try
{
foreach(scribbleObject al in Lines)
{
bool Drawdot = false;
try
{
Point[] linelist = new Point[
al.linePositions.Count];

al.linePositions.CopyTo(0,linelist,0,al.linePositions.Count);
e.Graphics.DrawLines(al.pen(), linelist);
}
catch
{
Drawdot = true; //we want to draw a dot
}
if(Drawdot)
{
//Draw a single dot as the user has only
clicked, rather than clicked and dragged
//TODO: Draw a dot
}

}
}
catch
{
error = true;
}
if(error)
{
//this.OnPaint(e);
}


}
}

}

And I would like to be able to create a bitmap of what gets drawn in
OnPaint so that I can save it to file, I've tried a few things, but so
far just ended up with black bitmaps the same size as the control that
code creates, any suggestions or code snippets would be a great help.

Cheers

Lee
 
M

Morten Wennevik

Hi Simon,

As an alternative to copy the code in OnPaint for your Bitmap drawing, you
can create a PaintEventArgs and call OnPaint directly.

void SaveBitmap()
{
using (Graphics g = Graphics.FromImage(bmp))
{
PaintEventArgs pea = new PaintEventArgs(g, new
Rectangle(0, 0, b.Width, b.Height));
OnPaint(pea);
}
bmp.Save(...);
}
 

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

Similar Threads

Flickering 3
How do you copy a Graphic to a Bitmap Image? 1
A Little Help?? 2

Top