Image is flashing when updated new position - c#

I have 2 picture objects.
I want both of them to move from right to left.
If one go out of the visible panel, I replace its position to the start point.
So there are always 2 pictures moving on the screen.
If I don't use timer, 2 pictures are painted. But if I use a timer with tick event updating their positions to make them move, there is only 1 picture is shown and it's keep flashing, lagging...
Below is my code so far. I'm not familiar with C#. Appreciate any help. Thank you.
Timer interval = 30;
Form 1:
public partial class Form1 : Form
{
Background bg1 = new Background();
Background bg2 = new Background(800);
public Form1()
{
InitializeComponent();
}
private void flowLayoutPanel1_Paint(object sender, PaintEventArgs e)
{
bg1.paint(e);
bg2.paint(e);
}
private void Timer_Tick(object sender, EventArgs e)
{
bg1.updatePosition();
bg2.updatePosition();
this.Refresh();
}
}
Background:
class Background
{
int bg_width = 800;
int bg_height = 500;
Image bg;
Rectangle wb;
private static int x = 0;
public Background()
{
bg = Properties.Resources.bg;
wb = new Rectangle(x, 0, bg_width, bg_height);
}
public Background(int custom_x)
{
x = custom_x;
bg = Properties.Resources.bg;
wb = new Rectangle(x, 0, bg_width, bg_height);
}
public void paint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(bg, wb);
}
public void updatePosition()
{
x--;
if (x == -800)
{
x = 801;
}
wb.Location = new Point(x, 0);
}
}

Related

Which part of the code make Drawing on bitmap slow...C# [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Code running slow.. the code generates multiple circles have a center can be specified by first click then change the radius by moving the pointer to desired circle size then click to finish drawing the circle.. and repeat to draw another circle..I solved the problem by very simple step see the comments down
public partial class Form1 : Form
{
public class Seat
{
private string _SeatKey;
private Rectangle _SeatRectangle;
public Seat(string seatKey, Rectangle seatRectangle)
{
_SeatKey = seatKey;
_SeatRectangle = seatRectangle;
}
public string SeatKey
{
get { return _SeatKey; }
}
public Rectangle SeatRectangle
{
get { return _SeatRectangle; }
set { _SeatRectangle = value; }
}
}
List<Seat> _Seats = new List<Seat>();
List<Seat> _center = new List<Seat>();
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
for (int i = 0; i < 30; i = i + 1)
{
string mystring = "regctangle" + i.ToString();
_Seats.Add(new Seat(mystring, new Rectangle(50, 50, 50, 50)));
}
for (int i = 0; i < 30; i = i + 1)
{
string mystring = "center" + i.ToString();
_center.Add(new Seat(mystring, new Rectangle(50 , 50 , 3, 3)));
}
}
Bitmap background;
Graphics scG;
private Point clickCurrent = Point.Empty;
private Point clickPrev = Point.Empty;
int clikno = 1;
int xpos;
int ypos;
int clicknew = 0;
int radius=0;
int recH;
int recW;
int xcen;
int ycen;
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
clikno = clikno + 1;
clicknew = clicknew + 1;
}
private void Form1_Load(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.DrawImage(Draw(), 0, 0);
}
public Bitmap Draw()
{
Graphics scG = Graphics.FromImage(background);
Pen myPen = new Pen(System.Drawing.Color.Red, 1/2);
Pen mPen = new Pen(System.Drawing.Color.Black, 3);
scG.Clear(SystemColors.Control);
_Seats[clikno].SeatRectangle = new Rectangle(xpos, ypos, recH, recW);
_center[clikno].SeatRectangle = new Rectangle(xcen, ycen, 3, 3);
for (int i = 0; i < clikno+1; i = i + 1)
{
scG.DrawEllipse(myPen, _Seats[i].SeatRectangle);
scG.DrawEllipse(mPen, _center[i].SeatRectangle);
}
Refresh();//This what cause the code running slow
return background;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
AutoSize = true;
clickCurrent = this.PointToClient(Cursor.Position);
clickPrev = clickCurrent;
if (clickPrev == Point.Empty) return;
Refresh();
clickCurrent = Point.Empty;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
double oradius = Math.Sqrt((Math.Pow(clickPrev.X - e.X, 2)) + (Math.Pow(clickPrev.Y - e.Y, 2)));
radius = Convert.ToInt32(oradius);
if (clicknew == 1)
{
recH = radius;
recW = radius;
xpos = clickPrev.X - recW / 2;
ypos = clickPrev.Y - recH / 2;
xcen = clickPrev.X - 3 / 2;
ycen = clickPrev.Y - 3 / 2;
Refresh();
}
if (clicknew == 2)
clicknew = 0;
Refresh();
}
}
The part that is so slow is your MouseMove.
Usually one checks for the left mousebutton to be pressed:
private void yourDrawingControl_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left) )
{
// draw stuff
}
}
If that is not a condition that applies you should check for having moved for at least more than one pixel:
Point oldLocation = Point.Empty;
private void yourDrawingControl_MouseMove(object sender, MouseEventArgs e)
{
int minStep = 3;
if ( (Math.Abs(oldLocation.X - e.X) + Math.Abs(oldLocation.Y - e.Y) > minStep) )
{
// draw stuff
}
oldLocation = e.Location;
}
Also: While the graphics is being built up, do not draw into a Bitmap which you then draw with DrawImage in your Paint event. Instead in the Paint event draw directly onto the surfce of the control from a List<yourDrawingshapeClass> !
Drawing hundreds of Rectangles is extremely fast compared to drawing even one of your Bitmaps..
Also: It looks as if you are drawing onto the Form? Better draw onto a dedicated control with just the right size; the natural choice is a PictureBox, which is made for this and is double-buffered out-of-the-box. Instead your enforce the form to refresh all it controls..
Finally: Even if you refuse to change your convoluted code to something well-proven, at least make it not call Refresh in the MouseMove unconditionally (in the last line), even if nothing at all has changed!

Why I cannot create more PictureBox in my array?

First of all I want to thank you for your time :) So there is my problem.. I am trying to make a little game where I spawn PictureBox and send it from right to left and my player which will be a PictureBox will try to avoid them jumping. So the first thing I did is , I created my PictureBox spawner with a class .. But the problem is , I can only spawn block[0] .. when I try to create block[1], nothing happens !! Please help me.. There is my code :
Form1.cs
namespace Game_0._1
{
public partial class Form1 : Form
{
Block[] block = new Block[50];
public Form1()
{
InitializeComponent();
int _Length = block.Length;
for(int i=0; i < 50; i++)
{
block[i] = new Block();
this.Controls.Add(block[i]._ScreenPanel());
block[i].Screen.Controls.Add(block[i].Box);
label1.Text = ("Generating block [" + i + "]");
}
}
private void button1_Click(object sender, EventArgs e)
{
block[0].SpawnBlock();
}
}
}
Block.cs
namespace Game_0._1
{
class Block
{
public Panel Screen;
public PictureBox Box;
Point x = new Point(50, 50);
Size s = new Size(150, 50);
Color c = Color.FromName("black");
public Block()
{
Screen = new Panel();
Screen.Dock = DockStyle.Fill;
Box = new PictureBox();
}
public Panel _ScreenPanel()
{
return Screen;
}
public PictureBox SpawnBlock()
{
Box.Name = "Obstacle";
Box.Location = x;
Box.Size = s;
Box.BorderStyle = BorderStyle.FixedSingle;
Box.BackColor = c;
return Box;
}
public void ChangeXLoc()
{
this.x.X += 50;
}
}
}
Here :
private void button1_Click(object sender, EventArgs e)
{
block[0].SpawnBlock();
}
this spawn a black box successfully , but if I type block[1], nothing ...
The issue is that your Panel (Screen) is overlapping the Panel that box[1] is positioned in, as a result it is not visible unless brought to front - which will in turn make the other box invisible instead.
A solution to this is to use a single panel, and size it accordingly, this allows you to place each box on the same panel and have their position relative to the Panel's 0,0.
Another solution would be to have a panel for each box which is the same size as the box and move the Panel and not the box which will have the same effect, but likely require less code to be edited.
I modified your code a little maybe it helps:
your form :
public partial class Form1 : Form
{
private Block[] _block = new Block[50];
private Point _x = new Point(0, 50);
private Timer _timer=new Timer();
private int _index = 0;
public Form1()
{
InitializeComponent();
_timer.Interval = 1000;
_timer.Tick += _timer_Tick;
for (int i = 0; i < 50; i++)
{
_block[i] = new Block(_x);
//Move the position of the block
ChangeXLoc();
Controls.Add(_block[i]._ScreenPanel());
label1.Text = #"Generating block [" + i + #"]";
}
}
//Timer to spawn the blocks
private void _timer_Tick(object sender, EventArgs e)
{
_block[_index].SpawnBlock();
if (_index < 49)
_index++;
}
private void button1_Click(object sender, EventArgs e)
{
_timer.Start();
}
private void ChangeXLoc()
{
_x.X += 50;
}
}
and your block class :
class Block
{
Panel Screen;
PictureBox Box;
Point _x;
Size s = new Size(50, 50);
Color c = Color.FromName("black");
//Pass the position x to the block
public Block(Point x)
{
_x = x;
Screen = new Panel
{
Size = s
};
}
public Panel _ScreenPanel()
{
Screen.Location = _x;
return Screen;
}
public void SpawnBlock()
{
Box = new PictureBox
{
Dock = DockStyle.Fill,
Name = "Obstacle" + _x,
BorderStyle = BorderStyle.FixedSingle,
BackColor = c
};
Screen.Controls.Add(Box);
}
}

C# How to Move A Circle Along a Rectangle Path

I need to move a point along a rectangle path at the command of a button. I want it to start at the upper right corner of the rectangle path, but I am not sure how to get it to go all the way around the path and stop at the original point. The screen refreshes at the speed provided by the user in an input box. Thank you in advance!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Timers;
namespace Assignment_2
{
public partial class Form1 : Form
{
private const int formwidth = 1280;
private const int formheight = 720;
private const int ball_a_radius = 10;
private const int horizontaladjustment = 8;
private const double ball_a_distance_moved_per_refresh = 1.6;
private double ball_a_real_coord_x = 515;
private double ball_a_real_coord_y = 40;
private int ball_a_int_coord_x;
private int ball_a_int_coord_y;
private const double graphicrefreshrate = 30.0;
private static System.Timers.Timer graphic_area_refresh_clock = new System.Timers.Timer();
private static System.Timers.Timer ball_a_control_clock = new System.Timers.Timer();
private bool ball_a_clock_active = false;
public double speed = 0;
public Form1()
{
InitializeComponent();
ball_a_int_coord_x = (int)(ball_a_real_coord_x);
ball_a_int_coord_y = (int)(ball_a_real_coord_y);
System.Console.WriteLine("Initial coordinates: ball_a_int_coord_x = {0}. ball_a_int_coord_y = {1}.",
ball_a_int_coord_x, ball_a_int_coord_y);
graphic_area_refresh_clock.Enabled = false;
graphic_area_refresh_clock.Elapsed += new ElapsedEventHandler(Updatedisplay);
ball_a_control_clock.Enabled = false;
ball_a_control_clock.Elapsed += new ElapsedEventHandler(Updateballa);
Startgraphicclock(graphicrefreshrate);
Startballaclock(speed);
}
public class NumericTextBox : TextBox
{
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void panel2_Paint(object sender, PaintEventArgs e)
{
}
private void panel2_Paint_1(object sender, PaintEventArgs e)
{
//Create pen
Pen blackPen = new Pen(Color.Black, 1);
//Create rectangle
Rectangle rect = new Rectangle(125, 50, 400, 400);
//Draw rectangle to screen
e.Graphics.DrawRectangle(blackPen, rect);
Graphics graph = e.Graphics;
graph.FillEllipse(Brushes.Green, ball_a_int_coord_x, ball_a_int_coord_y, 2 * ball_a_radius, 2 * ball_a_radius);
base.OnPaint(e);
}
public void button7_Click(object sender, EventArgs e)
{
speed = Convert.ToInt32(textBox3.Text);
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
}
protected void Startgraphicclock(double refreshrate)
{
double elapsedtimebetweentics;
if (refreshrate < 1.0) refreshrate = 1.0;
elapsedtimebetweentics = 1000.0 / refreshrate;
graphic_area_refresh_clock.Interval = (int)System.Math.Round(elapsedtimebetweentics);
graphic_area_refresh_clock.Enabled = true;
}
protected void Startballaclock(double updaterate)
{
double elapsedtimebetweenballmoves;
if (updaterate < 1.0) updaterate = 1.0;
elapsedtimebetweenballmoves = 1000.0 / updaterate;
ball_a_control_clock.Interval = (int)System.Math.Round(elapsedtimebetweenballmoves);
ball_a_control_clock.Enabled = true;
ball_a_clock_active = true;
}
protected void Updatedisplay(System.Object sender, ElapsedEventArgs evt)
{
Invalidate();
if (!(ball_a_clock_active))
{
graphic_area_refresh_clock.Enabled = false;
System.Console.WriteLine("The graphical area is no longer refreshing. You may close the window.");
}
}
protected void Updateballa(System.Object sender, ElapsedEventArgs evt)
{
ball_a_real_coord_x = ball_a_real_coord_x - 5;
ball_a_real_coord_y = ball_a_real_coord_y - 5;
ball_a_int_coord_x = (int)System.Math.Round(ball_a_real_coord_x);
ball_a_int_coord_y = (int)System.Math.Round(ball_a_real_coord_y);
if (ball_a_int_coord_x >= formwidth || ball_a_int_coord_y + 2 * ball_a_radius <= 0 || ball_a_int_coord_y >= formheight)
{
ball_a_clock_active = false;
ball_a_control_clock.Enabled = false;
System.Console.WriteLine("The clock controlling ball a has stopped.");
}
}
private void button4_Click(object sender, EventArgs e)
{
ball_a_control_clock.Enabled = true;
}
}
}
I have a more straightforward way to move a circle. Your code is too long for me to read. See if you like this!
If I were you, I would use a PictureBox. I first create an image of a circle, and then put that image in the PictureBox. Then you can just use a timer to change the position of the PictureBox.
You should set the Interval of the timer to 33 ms, which is roughly 30 fps. This is how you would programme the timer:
Keep a counter to indicate how many pixels the circle has moved. Let's say you want it to move in a 100px x 50px rectangular path.
For every 33ms,
If the counter is less than 100,
increase the X position and the counter by 1,
if the counter is between 101 and 150,
increase the Y position and the counter by 1,
if the counter is between 151 and 250,
decrease the X position by 1 and increment the counter
if the counter is between 251 and 300,
decrease the Y position by 1 and increment the counter
if the counter is greater than 300,
stop the timer
I really don't like drawing stuff on the screen with the OnPaint event. I mean, you are moving a ball! People think of this as changing the x and y positions of a ball, not as deleting the ball at the previous position and drawing it in the new position. Changing the position of the picture box just makes LOTS more sense, don't you think so?

how can i add a mouse click to this program that will increment a score by one each time the ball is clicked in C#?

How can i add a mouse click to this program that will increment a score by one each time the ball is clicked in C#?
namespace Ball_timer_2005
{
public class Form1 : System.Windows.Forms.Form
{
const int radius = 20;
const int velocity = 5;
int xC, yC, xDelta=10, yDelta=10, xSize, ySize; // class level variables
private System.Windows.Forms.Timer timer1;
private System.ComponentModel.IContainer components;
public Form1()
{
InitializeComponent();
// TODO: Add any constructor code after InitializeComponent call
this.ResizeRedraw = true; // Tell form to redraw itself when resized
timer1.Start();
Form1_Resize(this, EventArgs.Empty); // Force a Resize Event as pgm starts
//
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Interval = 25;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Name = "Form1";
this.Text = "Bouncing Ball";
this.Resize += new System.EventHandler(this.Form1_Resize);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Resize(object sender, System.EventArgs e)
{
xSize = this.ClientSize.Width; // Set current window size
ySize = this.ClientSize.Height;
xC = xSize/2; // Place ball in center of window
yC = ySize/2;
DrawBall(); // Draw the ball in the window
}
private void timer1_Tick(object sender, System.EventArgs e)
{
DrawBall(); // Draw ball in next frame of animation
}
private void DrawBall()
{
Graphics g = this.CreateGraphics();
Brush b = new SolidBrush(this.BackColor);
g.FillEllipse(b, xC-radius, yC-radius, 2*radius, 2*radius); //erase old ball
xC += xDelta; //move ball
yC += yDelta;
if ((xC+radius >= ClientSize.Width) || (xC - radius <= 0)) //check for wall hits
xDelta = -xDelta;
if ((yC+radius >= ClientSize.Height) || (yC - radius <= 0))
yDelta = -yDelta;
b = new SolidBrush(Color.GreenYellow); // draw new ball
g.FillEllipse(b, xC-radius, yC-radius, 2*radius, 2*radius);
b.Dispose();
g.Dispose();
}
}
}
1.this is the code i have so far can some one help ???
1.this is the code i have so far can some one help ???
1.this is the code i have so far can some one help ???
You can do this by Handling MouseClick event of the Form.
Step1: Store the Ellipse Co-ordinates into the Region to identify the position of the Mouse Click later.
//Declare it as class members
List<Region> regionList = new List<Region>();
int EclipseX = 10;
int EclipseY = 50;
int BallWidth = 100;
int BallHeight = 100;
//inside paint function
SolidBrush brush = new SolidBrush(Color.Blue);
e.Graphics.FillEllipse(brush, EclipseX,EclipseY, BallWidth, BallHeight);
regionList.Add(new Region(new Rectangle(EclipseX,EclipseY, BallWidth, BallHeight)));
Step2: now declare the MouseClick event of the Form
this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseClick);
Step3: now handle the MouseClick event of the Form.
the MouseClick event provides MouseEventArgs parameter here e.
this MouseEventArgs has two properties.
first property is X gives you the X-axis Location of the
Mouse Click.
second property is Y gives you the Y-axis location of the Mouse Click.
you need to compare these X and Y values with existing Region values using IsVisible Property.
you need to get the each Region from the List (which was added while drawing
Ellipse) and use IsVisible Property of the Region class to
detect whether mouse clicked on the Ellipse or not.
//declare it as class member
int MouseClicksCount = 0;
bool isfound=false;
//event handler
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
isfound=false;
foreach(Region r in regionList)
{
if (r.IsVisible(e.X, e.Y))
{
isfound=true;
break;
}
}
if (isfound)
MouseClicksCount++;
}
Complete Solution(Sample code):
public partial class Form1 : Form
{
int MouseClicksCount = 0;
List<Region> regionList = new List<Region>();
int EclipseX = 10;
int EclipseY = 50;
int BallWidth = 100;
int BallHeight = 100;
bool isfound=false;
public Form1()
{
InitializeComponent();
this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseClick);
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
isfound=false;
foreach(Region r in regionList)
{
if (r.IsVisible(e.X, e.Y))
{
isfound=true;
break;
}
}
if (isfound)
MouseClicksCount++;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
SolidBrush brush = new SolidBrush(Color.Blue);
e.Graphics.FillEllipse(brush, EclipseX,EclipseY, BallWidth, BallHeight);
regionList.Add(new Region(new Rectangle(EclipseX,EclipseY, BallWidth, BallHeight)));
}
}
Does this help at all
http://www.daniweb.com/software-development/csharp/threads/317766/mouse-coordinates-within-a-form
You may be able to us this to detect an overlap of mouse position/ ball width radius

Manipulating an image and updating the pictureBox has some issues

I could not fit exactly what I wanted to say in the title, it would be too long. Okay this is a multi-threaded app. What my app does is looks at a picture, find the edges of the picture, and finds the shape of that object from the edges. While it finds the shape, it constantly updates the image so we can get some sort of visual representation. I have created a very short (40 seconds) video demonstrating the issue: http://phstudios.com/projects/Programming/MultiThreadIssue/
As you can see, everything is working fine until the minute I move the window. This is always the case. I ran the program several times without moving the window and it ran fine. However, the minute I move the window, it will come up with that error. As you see, I am locking the specific image I would like to work with. Is the forms OnPaint overriding that somehow? Is there any way I can fix that?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Imaging;
namespace LineRecognition
{
public enum Shape
{
Unknown,
Quadrilateral,
Circle,
Triangle
}
public partial class Form1 : Form
{
Bitmap image, original, shapes;
List<Point> outlines;
Shape shape;
ShapeDetection detector;
public Form1()
{
InitializeComponent();
edgeDetection.WorkerReportsProgress = true;
shapeDetection.WorkerReportsProgress = true;
shape = Shape.Unknown;
}
private void Form1_Load(object sender, EventArgs e)
{
original = new Bitmap("photo1.png");
image = new Bitmap("photo1.png");
shapes = new Bitmap(image.Width, image.Height);
pictureBox1.Image = (Image)original;
}
private void findLines_Click(object sender, EventArgs e)
{
if (edgeDetection.IsBusy != true)
{
lblStatus.Text = "Finding Edges";
edgeDetection.RunWorkerAsync();
}
}
private void justTheOutlines(Bitmap image, List<Point> pixels, BackgroundWorker worker)
{
lock (image)
{
for (int i = 0; i < pixels.Count; i++)
{
image.SetPixel(pixels[i].X, pixels[i].Y, Color.Red);
worker.ReportProgress((int)((float)i * 100 / (float)pixels.Count));
}
}
}
private List<Point> outlineLines(Bitmap image, BackgroundWorker worker)
{
int w = image.Width;
int h = image.Height;
int alpha = 800000;
List<Point> changes = new List<Point>();
lock (image)
{
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
Color selected = image.GetPixel(i, j);
Color nextRight = selected;
Color nextDown = selected;
if (i < w - 1)
nextRight = image.GetPixel(i + 1, j);
if (j < h - 1)
nextDown = image.GetPixel(i, j + 1);
int iSelected = selected.ToArgb();
int iNextRight = nextRight.ToArgb();
int iNextDown = nextDown.ToArgb();
if (Math.Abs(iSelected - iNextRight) > alpha)
{
if (iSelected < iNextRight)
{
Point p = new Point(i, j);
if(!ContainsPoint(changes, p)) changes.Add(p);
}
else
{
Point p = new Point(i + 1, j);
if (!ContainsPoint(changes, p)) changes.Add(p);
}
}
if (Math.Abs(iSelected - iNextDown) > alpha)
{
if (iSelected < iNextDown)
{
Point p = new Point(i, j);
if (!ContainsPoint(changes, p)) changes.Add(p);
}
else
{
Point p = new Point(i, j + 1);
if (!ContainsPoint(changes, p)) changes.Add(p);
}
}
image.SetPixel(i, j, Color.White);
}
worker.ReportProgress((int)(((float)i / (float)w) * 100));
}
}
return changes;
}
private bool ContainsPoint(List<Point> changes, Point p)
{
foreach (Point n in changes)
{
if (n.Equals(p)) return true;
}
return false;
}
private void edgeDetection_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
outlines = outlineLines(image, worker);
justTheOutlines(image, outlines, worker);
pictureBox2.Image = (Image)image;
Thread.Sleep(100);
image.Save("photo-lines.jpg");
}
private void edgeDetection_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
algorithmProgress.Value = e.ProgressPercentage;
}
private void edgeDetection_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
algorithmProgress.Value = 0;
findLines.Enabled = false;
determineShape.Enabled = true;
lblStatus.Text = "";
}
private void determineShape_Click(object sender, EventArgs e)
{
if (shapeDetection.IsBusy != true)
{
pictureBox1.Image = (Image)image;
lblStatus.Text = "Running Shape Detection: Circle -> Quadrilateral";
shapeDetection.RunWorkerAsync();
}
}
private void ShapeDetection_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
detector = new ShapeDetection(outlines, 40, 10);
detector.Worker = worker;
detector.circleChange += new ShapeDetection.CircleChangeEventHandler(circleChange);
if (detector.IsCircle())
{
MessageBox.Show("Object is a circle");
shape = Shape.Circle;
}
else if (detector.IsQuadrilateral())
{
MessageBox.Show("Object is a quadrilateral", "Number of edges: " + detector.Summits);
shape = Shape.Quadrilateral;
}
else
{
int sides = detector.Summits.Count;
if (sides == 3)
{
MessageBox.Show("Object is a triangle");
shape = Shape.Triangle;
}
else
{
MessageBox.Show("Number of edges: " + detector.Summits.Count, "Unknown");
}
}
BitmapDrawing.DrawLines(detector.Summits, shapes);
BitmapDrawing.DrawSummits(detector.Summits, shapes);
pictureBox2.Image = (Image)shapes;
Thread.Sleep(100);
}
private void ShapeDetection_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (detector != null)
{
lblSummits.Text += detector.Summits.Count;
lblType.Text += shape.ToString();
determineShape.Enabled = false;
lblStatus.Text = "";
}
}
void circleChange(object sender, CircleChangeEventArgs e)
{
lock (shapes)
{
Point p = detector.visited[detector.visited.Count - 1];
shapes.SetPixel(p.X, p.Y, Color.Blue);
pictureBox2.Image = (Image)shapes;
Thread.Sleep(10);
detector.Worker.ReportProgress((int)(e.percent * 100));
}
}
private void shapeDetection_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
algorithmProgress.Value = e.ProgressPercentage;
}
}
}
Update
What Nick said before worked fine. I added that to my CircleChange event and it works. Can somebody explain why the invoke makes it work instead of setting the picturebox2.Image to the shapes image? I mean I call it after I call setpixel, so I should be done modifying the image right?
void circleChange(object sender, CircleChangeEventArgs e)
{
Point p = detector.visited[detector.visited.Count - 1];
shapes.SetPixel(p.X, p.Y, Color.Blue);
Image copyForPictureBox = shapes.Clone() as Image;
BeginInvoke(new Action(() => pictureBox2.Image = copyForPictureBox));
Thread.Sleep(15);
detector.Worker.ReportProgress((int)(e.percent * 100));
}
It appears that you're operating on the shapes bitmap on a thread separate to the GUI thread. When you move the window the OnPaint routine will run which will also access the image.
What you need to do to solve this is operate on a separate bitmap in your worker thread, and then pass a copy of that to the GUI thread using Invoke on the form. That way you're guaranteed only to have one thread accessing the picture box image at a time.
Edit:
void MyThreadFunction( )
{
Bitmap localThreadImage;
...
Image copyForPictureBox = localThreadImage.Clone( ) as Image;
BeginInvoke( new Action( () => pictureBox.Image = copyForPictureBox ) );
....
}
So the idea is that you create a Bitmap on the thread which is only ever accessed by that thread (i.e. your background worker thread). And when you get to a point when you want to update the image in the PictureBox you invoke onto the the GUI thread using BeginInvoke (which doesn't block your worker thread) passing a copy of the Bitmap to the PictureBox.
Locking the shapes object at only one point in your application does not accomplish anything. You also use this bitmap to draw to the window, and my guess is that you are not locking it for drawing. You can either lock it in OnPaint as well, or use a different bitmap for manipulation and display.

Categories