Drawing to a panel using a bitmap as a buffer - c#

I have been learning about drawing to panels using bitmaps. I thought I would run a trial program to simply turn a white panel black. (May seem a complicated way of doing it but this is just to test the basics) My program is as follows:
public partial class Form1 : Form
{
private Bitmap buffer = new Bitmap(100,100);
public Form1()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImageUnscaled(buffer, Point.Empty);
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 100; i++)
{
for (int j = 0; j < 100; j++)
{
buffer.SetPixel(i, j, Color.Black);
}
}
}
}
When I run it and press the button the panel does not seem to change. Any Idea where I am going wrong. Thank you in advance.

You have to invalidate the panel's client area so that Windows will force a repaint. But there are some other issues:
FillRectangle will do a much more efficient job than painting each pixel in a loop, as #Tony suggested.
You might hit concurrency issues if the panel is invalidated before buffer is ready to be displayed. Be sure that the bitmap generation is isolated from its presentation.
These suggestions are summarized (but not tested) as follows:
private void button1_Click(object sender, EventArgs e)
{
Bitmap tempBuffer = new Bitmap(100, 100);
using (Graphics g = Graphics.FromImage(tempBuffer))
using (SolidBrush blackBrush = new SolidBrush(Color.Black))
{
g.FillRectangle(blackBrush, new Rectangle(0, 0, tempBuffer.Width-1, tempBuffer.Height-1);
}
buffer = tempBuffer;
panel1.Invalidate();
}

In addition to invalidating the panel's client area, if you're wanting it to paint when you click the button you'll want to wire up the paint event in the button's click event. Give this a shot:
public partial class Form1 : Form
{
private bool _paintWired;
public Form1()
{
InitializeComponent();
}
private void PanelPaint(object sender, PaintEventArgs e)
{
using (Graphics g = this.panel1.CreateGraphics())
{
g.FillRectangle(Brushes.Black, this.panel1.Bounds);
}
}
private void button1_Click(object sender, EventArgs e)
{
if(!_paintWired)
{
this.panel1.Paint += new PaintEventHandler(PanelPaint);
_paintWired = true;
}
this.panel1.Invalidate();
}
}
UPDATE: Sorry, I missed the point about using a bitmap.

try this example
I use it for something like what you want to do and it worked.
I hope to help you
example_1

Related

Transparent picture box on top of another picture box is not working. How to fix this?

I want to create two picture boxes, overlapping.
The first Picturebox is used as the background, the picture of the screen.
using this method:
public void BckShow()
{
Rectangle rect = Screen.GetBounds(this);
gBackImg = Graphics.FromImage(bBackImg);
gBackImg.CopyFromScreen(0,0,0,0,
Screen.PrimaryScreen.Bounds.Size,
CopyPixelOperation.SourceCopy);
}
The second picturebox is above the first one, a transparent picture box that can be drawn using this mouse event:
public void Draw(bool draw, Point sp, Point ep)
{
if (draw)
{
gCanvas.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
pen = new Pen(new SolidBrush(ColorName), BrushSize);
if (toolPen.Checked)
{
gCanvas.DrawLine(pen, sp, ep);
}
else if (toolEreser.Checked)
{
Rectangle rect = new Rectangle(ep.X, ep.Y, BrushSize*5, BrushSize*5);
gCanvas.DrawEllipse(pen, rect);
gCanvas.FillEllipse(new SolidBrush(ColorName), rect);
}
bCanvas.MakeTransparent(Color.White);
pbxCanvas.Refresh();
dirty = true;
toolSave.Enabled = true;
}
}
private void pbxCanvas_MouseDown(object sender, MouseEventArgs e)
{
sp = e.Location;
if (e.Button == MouseButtons.Left)
{
ActivePaint = true;
}
}
private void pbxCanvas_MouseUp(object sender, MouseEventArgs e)
{
ActivePaint = false;
}
private void pbxCanvas_MouseMove(object sender, MouseEventArgs e)
{
ep = e.Location;
Draw(ActivePaint, sp, ep);
sp = ep;
}
but when i run the program, the second PictureBox does not draw anything when the mouse event was fired. how i can fix this?
I do this because I just want to save the image in the second picture box. Unlike PrintScreen but seemed to make notes on the screen and save the image apart from the screen image.
Is there another way to do this? like using controls other than picture box, or may directly use the screen as a background but still can save the image in the transparent PictureBox separately.
This is the example I want to achieve:
when drawing:
results stored images:
I hope you all will help me to fix this. sorry for poor explanation.
this the document outline window for more detail:
It's likely that your surface being overdrawn by refresh. You should be tracking what you want to draw, and then drawing it in the picture box's Paint event. That way, you get handed a Graphics object and every refresh, you're drawing.
That's assuming, of course, that you have a valid, and correct, Graphics object in the first place.
BTW: passing a form-scope variable to Draw is confusing, just use it.
Check your gCanvas initializer, if it is used from within Paint event (e.Graphics), then your changes are lost when you call the Refresh() method. Refresh() causes a new Paint event to be fired, creating a new Graphics object and therefore invalidating yours.
Create a new graphics object from your PictureBox's Image to persist your changes permanently.
private List<Point> points = new List<Point>();
private void pbxCanvas_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
ActivePaint = true;
}
}
private void pbxCanvas_MouseUp(object sender, MouseEventArgs e) {
ActivePaint = false;
points.Clear();
}
private void pbxCanvas_MouseMove(object sender, MouseEventArgs e) {
if (ActivePaint) {
points.Add(e.Location);
Refresh();
}
}
private void pbxCanvas_Paint(object sender, PaintEventArgs e) {
using (var graphics = Graphics.FromImage(pbxCanvas.Image)) {
for (int i = 0; i < points.Count - 1; i++) {
graphics.DrawLine(Pens.Black, points[i], points[i + 1]);
}
}
}

How to animate a PictureBox in a Win Forms application?

I have a windows form application with a PictureBox control containing an image. I want to move the PictureBox control to the right in a slow movement. Here is my code:
Point currentPoint = pictureBox_Logo.Location;
for (int i = 0; i < 25; i++)
{
pictureBox_Logo.Location = new Point(pictureBox_Logo.Location.X + 1, pictureBox_Logo.Location.Y);
Thread.Sleep(30);
}
The problem here is that when the code executes instead of seeing the picture move, I see a white picture move and the moving stops until the picture appears.
What am I missing and what can I do about it?
Code:
public partial class Form1 : Form
{
void timer_Tick(object sender, EventArgs e)
{
int x = pictureBox1.Location.X;
int y = pictureBox1.Location.Y;
pictureBox1.Location = new Point(x+25, y);
if (x > this.Width)
timer1.Stop();
}
public Form1()
{
InitializeComponent();
timer1.Interval = 10;
timer1.Tick += new EventHandler(timer_Tick);
}
private void button1_Click(object sender, EventArgs e)
{
pictureBox1.Show();
timer1.Start();
}
}
original thread is here Move images in C#
Try to use pictureBox_Logo.Refresh() after Thread.Sleep(30);
Or look for standard Timer control.
My code is good written, but what I did wrong was putting the code in an event:
private void Form1_Shown(object sender, EventArgs e);
But when I put my code in a button it works without any problems.

erasing parts of the loaded image by mouse

I have loaded an image in my panel.I want to erase the parts of that image by using the mouse(dragging on the panel).Here is my code to load my image:
private void drawP_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(myImage, new Point(0, 0));
}
How can I do it?
Thanks in advance.
Updated:
sorry not to say earlier,I have set another image(image2) as background of the panel and I want it to be seen after erasing myImage(image loaded with the code above).
Hi I'm going to assume that you want this feature to work like the eraser on paint.
there are 3 events you are going to need
1.mousedown - to call the first erase and open up the mousemove event method.
2.mouseup - to stop the mousemove event method
3.mousemove - just to call the erase method
Code: //part pseudo as im not in visual studio right now :(
//global vars
bool enable = false;
void erase(Point mousepoint)
{
Point f = (mousepoint.X - yourpanel.left?, mousepoint.Y - yourpanel.top?);
//gets mouse position on accual picture;
yourImageGraphics.fillreactangle( f.X - 10, f.Y+10, 20,20 ,Color.White)
// int X , int Y, width , height, color
}
void mousedown(?)
{
enable=true;
erase(Cursor.Position //but you get this from e?);
}
void mouseup(?);
{
enable=false;
}
void mousemove(?)
{
if (enable)
erase(e.Position?);
}
Also it looks like you are going to have to make a graphics object for your panel :(
I hope this helps because question was a bit vague.
Here I created simple example. Of course it can be done better, but I just wondering how to do it... so sharing my results.
public partial class mainForm : Form
{
private Bitmap image;
private Rectangle imgRect;
public mainForm()
{
InitializeComponent();
BackColor = Color.Chartreuse;
image = new Bitmap(#"C:\image.jpg");
imgRect = new Rectangle(0,0,image.Width, image.Height);
}
private void main_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(image, 0, 0);
}
private void main_MouseMove(object sender, MouseEventArgs e)
{
if(e.Button == MouseButtons.Left && e.X < image.Width && e.Y < image.Height)
{
image.SetPixel(e.X, e.Y, Color.Magenta);//change pixel color;
image.MakeTransparent(Color.Magenta);//Make image transparent
Invalidate(imgRect);
}
}
}
...and lets test
Ha! scared that I deleted his eye :)
A TextureBrush on a pen can be used for erasing.
Working example (image1 and image2 are the same size images):
Bitmap bmp1;
TextureBrush tb;
Point _LastPoint;
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
bmp1 = new Bitmap(#"c:\image1.png");
tb = new TextureBrush(new Bitmap(#"c:\image2.png"));
}
private void Form1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
if (!_LastPoint.IsEmpty) {
using (Graphics g = Graphics.FromImage(bmp1))
using (Pen p = new Pen(tb, 15)) {
p.StartCap = LineCap.Round;
p.EndCap = LineCap.Round;
g.DrawLine(p, _LastPoint, e.Location);
}
}
_LastPoint = e.Location;
this.Invalidate();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e) {
_LastPoint = Point.Empty;
}
private void Form1_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawImage(bmp1, new Point(0, 0));
}

loading image on a panel

First I load an image in a picturebox. Then I measure the areas in it and create a new picture. Now I want to load the image in a panel and draw a line by mouse.
I added to my form:
private Image imag;
I also added to my project:
private void drawP_Paint(object sender, PaintEventArgs e)
{
Graphics g = drawP.CreateGraphics();
g.DrawImage(imag, new Point(0,0));
}
I set the image in a function:
imag = (Image)bm;
// or
imag = picturebox1.Image; // the made picture
drawP.Invalidate();
But nothing appears when running the project.
You should place your code in panel Paint event.
private void panel1_Paint(object sender, PaintEventArgs e)
{
Image imag = Image.FromFile(filename);
e.Graphics.DrawImage(imag, new Point(0,0));
}
This makes you sure that everytime panel is redrawn (after beeing invalidated for any reason) your image is visible.
Try changing it to this:
private void drawP_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(imag, new Point(0,0));
}
Also, from your comments, it sounds like you may not have the event wired up. Example:
public Form1()
{
InitializeComponent();
drawP.Paint += drawP_Paint;
}
To draw a line on that image:
private void button1_Click(object sender, EventArgs e) {
using (Graphics g = Graphics.FromImage(imag)) {
g.DrawLine(Pens.Red, new Point(0, 0), new Point(32, 32));
}
drawP.Invalidate();
}

Form not showing after Invalidate()

For some reason, this code does not actually draw my bitmap file... or show the form.
namespace GraphicsEngine
{
public partial class Form1 : Form
{
Bitmap[] dude = new Bitmap[3];
Bitmap dude0 = new Bitmap(#"C:\Directory.bmp");
Point renderpoint = new Point(1, 1);
public Form1()
{
dude[0] = new Bitmap(#"C:\Directory.bmp");
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MainLoop();
}
private void MainLoop()
{
double FPS = 30.0;
long ticks1 = 0;
long ticks2 = 0;
double interval = (double)Stopwatch.Frequency / FPS;
while (!this.IsDisposed)
{
ticks2 = Stopwatch.GetTimestamp();
if (ticks2 >= ticks1 + interval)
{
ticks1 = Stopwatch.GetTimestamp();
this.Invalidate();
}
Thread.Sleep(1);
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(dude0, renderpoint);
}
}
}
Any ideas?
Your problem ought to be a bit more obvious than not seeing the bitmap, you should not see the form either. That's because you never complete the Load event. You could use the Shown event instead.
Check this thread for the code for a true game loop.
Try replacing your call to this.Invalidate(); with this.Refresh();.
Looks like your MainLoop() could be an infinite loop. You can put Console.Out.WriteLine(ticks1); in your while loop to validate this.
It gets stuck in the Load event handler and none of the main form validation occurs. It definitely draws the picture if you comment out the call to MainLoop().
Form1_Paint is not called unless if you have UserPaint set to true. Try this as your constructor
public Form1() {
dude[0] = new Bitmap(#"C:\Directory.bmp");
InitializeComponent();
this.SetStyle( System.Windows.Forms.ControlStyles.UserPaint, true );
this.SetStyle( System.Windows.Forms.ControlStyles.AllPaintingInWmPaint, true );
this.SetStyle( System.Windows.Forms.ControlStyles.OptimizedDoubleBuffer, true );
}
Just beware, when you do this, you may be responsible for all the paints on the form.

Categories