drawing Graphics performance issue - c#

i am trying to create slider movement graphics. The code explain better this.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace temp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private System.Drawing.Graphics g;
private System.Drawing.Pen pen1 = new System.Drawing.Pen(Color.Black, 1F);
//timer for animation
private void Form1_Load(object sender, EventArgs e)
{
Timer a = new Timer();
a.Interval = 60;
a.Tick += new EventHandler(a_Tick);
a.Start();
}
void a_Tick(object sender, EventArgs e)
{
button1_Click(this, null);
}
//draws randomly generated point array.
int cnt = 0;
private void button1_Click(object sender, EventArgs e)
{
rAr();
g = pictureBox1.CreateGraphics();
g.Clear(Color.Violet);
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawCurve(pen1, points2);
cnt++;
}
Random r = new Random();
Point[] p = new Point[100];
Point[] points2 = new Point[100];
int c = 4;
//fills new random point array
private void rAr()
{
points2.CopyTo(p, 0);
int cc = 1;
for (int i = points2.Length - 1; i > 0; i--)
{
points2[i - 1] = new Point(100 - cc, p[i].Y);
cc++;
}
points2[99] = new Point(100, r.Next(1, 50));
}
}
}
this code works fine put the problem is that it takes to much cpu and automation is not very smooth. is there some other way to achieve same thing put with much less cpu.
Thanks for replays

(Premature) Optimisations
In addition to the other answers already posted, you are attempting to draw a spline curve through 100 points.
This is probably a bad idea for two reasons: Firstly, fitting a curve to 100 points isn't a particularly fast thing to do under any circumstances. Secondly, as the points are all 1 pixel apart, there is likely to be no visual benefit to using a curve.
Therefore, there are several more things you could do that might improve the speed:
Try using a PolyLine rather than a curve,
If you need to use a curve, then you probably don't need to use 100 points across 100 pixels of the drawing - using 10 or 20 positions across that width may give you better results, and will significantly reduce the curve-fitting work that .net has to do.
You may be able to also remove some intermediate points that lie on the line/curve, so that you have fewer lines to draw. e.g. if you have points at (10,57) (11, 57) (12,57) then you can drop the middle point and just draw a straight line from (10,57) to (12,57).
Wrong Algorithm?
But wait! Before you optimise the code - is it actually the right solution to the problem?
It sounds as if the content is not meant to "change", but simply scroll sideways. In which case, only the new pixels introduced at one side of the image are actually "changing" with each frame - the rest simply move sideways. In that case, you can scroll (move) the region of the graphic that contains the "old" curve image, and then just draw the extra pixel or two that have "scrolled into view". Scrolling in this manner can be achieved in several different ways (e.g. blitting in the graphics, or if the content of the entire window is scrolling, by using windows Form scrolling commands)
In the same way, if the data is not changing, but simply "scrolling", then rebuilding the array for every frame is unnecessarily expensive. Using a circular buffer you could simply add a new point to the "end" of the array and delete one from the beginning without having to reallocate the array or copy all the intervening points at all.
So it's all about using the right tool for the job.

You should move all of the graphics code in Button1 to your picturebox's paint event, something like this:
private void button1_Click(object sender, EventArgs e)
{
rAr();
pictureBox1.Invalidate();
cnt++;
}
private void picturebox1_Paint(object sender PaintEventArgs e)
{
using graphics g = e.Graphics()
{
g.Clear(Color.Violet);
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawCurve(pen1, points2);
}
}

Related

Dynamic drawing ants in winforms during execution of ant colony

After this question (Show trail of moving pixel in C# WinForm project) for my personal ant colony project in c#, I'm trying to apply the solution second suggested solution: the one that combines drawing the trail into a bitmap and the new ants onto the surface.
[...]Application.Run(new ShowAnts());[...]
public partial class ShowAnts : Form
{
Bitmap bmp;
int j = 0;
public ShowAnts()
{
InitializeAntProgram();
InitializeComponent();
bmp = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
pictureBox1.Image = bmp;
}
public void RenderAnts(object sender, PaintEventArgs e)
{
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
while (j < 1000)
{
Map.EvaporatesPheromones();
foreach (Vector2D food in foodSrcs)
{
Map.SetMapPoint(food, 500);
}
foreach (Ant a in ants)
{
Brush c;
c = Brushes.DarkBlue;
if (a.role == AntRole.Scout)
{
a.Move(j);
c = Brushes.Red;
}
e.Graphics.FillRectangle(Brushes.DarkBlue, a.position.x, a.position.y, 1, 1);
G.FillRectangle(Brushes.Gray, a.position.x, a.position.y, 1, 1);
}
j++;
}
}
}
}
The code above shows the graphic attempt to draw the ant movement into a winform.
It works perfectly, but it shows only the final result. I would like to show the step by step evolution keeping the graphical trail information without reparsing my map info.
Please consider that a working console project on which I' developing this "graphic interface" already exists so:
some variables are set elsewhere (i.e.: food) in the project;the `a.Move(j);` refers to the ant logic itself (analysis, decision, new cell movement referring to the map array);the `j` counter is used to count steps and to set an arbitrary stop, but has no real use;I'm already storing into map array and some other variables all informations concerning pheromone, movement, positions etc.
Looking at your code and also the comments of the previous question, it seems that you are missing the part that would animate the movement. Instead you are looping inside what seems to be the Paint event.
Here is a quick fix for that. It adds a Timer that triggers the RenderAnts event, which seems to be hooked up to the pictureBox1.Paint handler..:
A few class level variables:
int counter = 0;
int limit = 1000;
Timer antTimer = new Timer();
Start code:
antTimer.Interval = 50; // <-- pick your speed !!
antTimer.Tick += (ss, ee) =>
{ pictureBox1.Invalidate(); counter++; if (counter > limit) antTimer.Stop(); };
antTimer.Start();
The speed is 50ms, which means 20 Ticks per second.
The Tick event is inlined with a tiny Lambda epression and has only one statement plus the loop logic. By Invalidating the pictureBox1 control its Paint event and thereby the RenderAnts event is triggered.
Also note that I called it a 'quick fix'. Usually you would discern between the rendering and the moving code of an animation; but in this case this fine difference doesn't matter much.
Now we change the RenderAnts method, taking out the loop:
public void RenderAnts(object sender, PaintEventArgs e)
{
using (Graphics G = Graphics.FromImage(pictureBox1.Image))
{
Map.EvaporatesPheromones();
foreach (Vector2D food in foodSrcs)
{
Map.SetMapPoint(food, 500);
}
foreach (Ant a in ants)
{
Brush c = Brushes.DarkBlue;
if (a.role == AntRole.Scout)
{
a.Move(j);
c = Brushes.Red;
}
e.Graphics.FillRectangle(c, a.position.x, a.position.y, 1, 1);
G.FillRectangle(Brushes.Gray, a.position.x, a.position.y, 1, 1);
}
}
}
You also may want to add a Start/Stop Button. Also a TrackBar to change the speed..
Now you should be able to watch the progress of your ants at 20Hz, leaving grey trails.

copying free hand drawing from panel in visual studio 2013

I want to draw free hand in a form (picture box) on visual studio and copy the same figure (that I draw) on another panel/picture box.
Also they should not be dots forming one line but a continuous line. Please help.
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;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Pen p_white;
bool draw = true;
private Graphics objgraphics;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Pen p_black = new Pen(new SolidBrush(Color.Black));
if (draw)
{
objgraphics = panel1.CreateGraphics();
}
}
/*private void panel1_MouseDown(object sender, MouseEventArgs e)
{
bool draw = true;
}*/
private void panel1_MouseMove_1(object sender, MouseEventArgs e)
{
Rectangle rEllipse = new Rectangle();
switch (e.Button)
{
case MouseButtons.Left:
rEllipse.X = e.X;
rEllipse.Y = e.Y;
rEllipse.Width = 5;
rEllipse.Height = 5;
objgraphics.DrawEllipse(System.Drawing.Pens.Black, rEllipse);
break;
case MouseButtons.Right:
rEllipse.X = e.X;
rEllipse.Y = e.Y;
rEllipse.Width = 3;
rEllipse.Height = 3;
objgraphics.DrawEllipse(System.Drawing.Pens.Black, rEllipse);
break;
default:
return;
}
}
/*private void panel1_MouseUp(object sender, MouseEventArgs e)
{
bool draw = false;
} */
private void form_Paint(object sender, EventArgs e)
{
}
private void panel2_Paint(object sender, PaintEventArgs e)
{
Pen p_black = new Pen(new SolidBrush(Color.Black));
if (draw)
{ objgraphics = panel1.CreateGraphics();
}
}
private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
Looking at your code I'm afraid I have to say: This is all wrong.
Sorry to be so blunt, but you must never use control.CreateGraphics!!
The first thing to do is to throw away the Graphics objgraphics object.
It is (almost) always wrong to store a Graphics object!
Instead, you have to use the one you get from the e.Graphics parameter in the Paint events of your controls.
Note that Graphics doesn't contain any graphics, it is a tool used to draw onto an associated Bitmap or a control's surface.
The next thing is to do is to understand about drawing freehand lines. Often one can see the code you have; but it is useless and only an example of how many stupid things you find in introductions. Forget it. It will always look like crap as the circles simply never look smooth and as soon as you move the mouse faster the pseudo-lines completely fall apart.
There is a nice method DrawCurve that will draw smooth lines. You feed it a Pen and an array of Points.
This is what we will use.
Let's return to the basics: How to create a graphic? Now we know that you need to call DrawCurve in the Paint event:
e.Graphics.DrawCurve(somePen, somePointsArray);
This brings up the next questions:
what's somePen
what's somePointsArray
There is a hidden third question:
what about drawing more lines?
The first one is simple; you create a Pen with a stroke width of 5.5 pixels as
Pen somePen = new Pen(Color.Blue, 5.5f);
If you want to you can give it a linestyle (dashes), too.
Now for the array: In its simplest form this is easy as well.
In the MouseMove event you store the current Location in a list of points. First, we declare it at class level:
List<Point> currentLine = new List<Point>();
Then we start filling it as long as the left button is pressed:
private void panel1_MouseMove_1(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
currentLine.Add(e.Location);
panel1.Invalidate();
}
}
Note the last line: Calling Invalidate on a control triggers the system to invoke the Paint event. It may look complicated but this is the only correct way as it guarantees that the very same drawing will also happen when some other reason makes it necessary.
We need to draw because we have changes the data that should be drawn. But there are many outside reasons, most notoriously the Minimize/maximize sequence that will clear the drawing and trigger the Paint event, too. So we need to cooperate with the way windows draws its controls! Only this way the graphics will persist.
Also, note we don't use an array as we don't know how many Points we will need. Instead, we use a List and later cast it to Array.
Let's code the Paint event for our simple case:
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen somePen = new Pen(Color.Blue, 5.5f) )
if (currentLine.Count > 1) e.Graphics.DrawCurve(yourPen , currentLine.ToArray());
}
Note that I have created the Pen in a using clause. This is a cheap and safe way to ensure that the Pen is disposed of properly.
Also note how we cast the List to an Array!
The above code alone would work and allow you free-hand drawing a line.
But what about the next line? It should not connect to the first one so we can't just add more points!
So we need not just one list of points but more than that, in fact, a list of lists of points is called for:
List<List<Point>> curves = new List<List<Point>>();
And we add the current curve to it whenever the mouse is released:
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
if (currentLine.Count > 1) curves.Add(currentLine.ToList()); // copy!!
currentLine.Clear();
panel1.Invalidate();
}
Note how I use a cast from List to List to enforce a copy or else only the reference would be assigned and then, in the next line cleared..
Again we trigger the Paint event as the final thing to do.
We now should change the Paint event to display all the lines, both the one currently being drawn and all the earlier ones..:
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen somePen = new Pen(Color.Blue, 5.5f) )
{
if (currentLine.Count > 1) e.Graphics.DrawCurve(somePen, currentLine.ToArray());
foreach (List<Point> lp in curves)
if (lp.Count > 1) e.Graphics.DrawCurve(somePen, lp.ToArray());
}
}
Now we are basically done with the free-hand drawing part.
So we return to your original question: How can you copy the drawing to a second Panel?
Well, you have stored everything in the curves data structure.
So you have two options: Either simply use the same data in the panel2_Paint event or if you need to copy and change the data to, maybe adapt to a different size.
Here are the things still missing:
Saving the data, saving the drawing (Serialize, DrawToBitMap)
Drawing with varying pens & colors (create a drawAction class to store all you need)
Using other tools like Line or Rectangle (create a drawAction class)
Clearing the drawing (see below)
Undo and Redo (look into Stacks an Queues)
Caching when there are a great number of lines ( draw the first portion into a BackgroundImage Bitmap)
Here is a clearing code:
curves.Clear(); currentLine .Clear(); panel1.Invalidate();
I noted that your original code lets you draw with two different stroke widths using the left and right button. This alone shows that this code is not very good. Who would a) think of that and b) be satisfied with only two stroke widths.
Please read this post where I explain a little about creating a class that can store a pen width, a color etc so that you can change then between lines you draw.

Moving backwards through a gif much slower than moving forwards

I would like to be able to move through a gif frame by frame. In the example below, I use a trackbar to select which frame of a gif I want to see. For the designer I just dropped a PictureBox in the centre of the screen and stuck a TrackBar at the bottom of the screen.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
namespace TestGifProject {
public partial class Form1 : Form {
private Image gif;
private FrameDimension fd;
public Form1() {
InitializeComponent();
gif = Image.FromFile("PATH\\TO\\SOME\\GIF.gif");
// just for this quick example...
this.Width = gif.Width + 20;
this.Height = gif.Height + 53;
pictureBox1.Width = gif.Width;
pictureBox1.Height = gif.Height;
pictureBox1.Image = gif;
fd = new FrameDimension(gif.FrameDimensionsList[0]);
trackBar1.SetRange(0, gif.GetFrameCount(fd) - 1);
}
private void trackBar1_MouseUp(object sender, MouseEventArgs e) {
gif.SelectActiveFrame(fd, trackBar1.Value);
pictureBox1.Image = gif;
}
}
}
When you move and release the trackbar, the frame is shown correctly however it is much slower when you scroll backwards rather than forwards (probably 10x faster forwards), often long enough to make it seem like the app has crashed. Is there anything I can do to speed up backwards scrolling through a gif?
One option is to create a list where you add each frame. SelectActiveFrame() could be resolving the correct image with a more complex logic, which could be the reason for slow action.
Create a new list to your class' root:
private List<Image> frames = new List<Image>();
Fill the list with the frame images:
for(int i = 0; i < gif.GetFrameCount(fd); i++)
{
gif.SelectActiveFrame(fd, i);
frames.Add((Image)gif.Clone());
}
Then use the list to set images to the PictureBox:
pictureBox1.Image = frames[trackBar1.Value];
I haven't tested this, but I think it should work.

C# Tetris game slow performance

I'm programming a tetris clone for my C# school project. I am using Microsoft Visual Studio 2012. The game itself is implemented as a two dimensional array of blocks(List of Lists of blocks) and every block has its own texture (bmp image). I am drawing the whole array onto a PictureBox control and this is where the problem starts. When updating the image on the PictureBox (moving/rotating the active shape) the game slightly lags. I tried to draw on a Panel control instead but the result was the same. I have a rough idea what might cause the lag but I don't know exactly how to get rid of it.
This is the draw method of the game "grid":
public void Draw(Graphics g)
{
Brush brush;
Font font = new System.Drawing.Font( "Arial", 5);
for (int i = 0; i < Width; i++)
for (int j = 0; j < Height; j++)
{
brush = new TextureBrush(Blocks[i][j].Texture);
if (Blocks[i][j].Occupied==true)
g.FillRectangle(brush, i * 20, j * 20, i * 20 + Blocks[i][j].Texture.Width, j * 20 + Blocks[i][j].Texture.Height);
}
}
This is the draw method of the active tetromino:
public void Draw(Graphics g)
{
Brush brush = new TextureBrush(Blocks[0].Texture);
foreach (FullBlock b in Blocks)
g.FillRectangle(brush, b.x * 20, b.y * 20,b.Texture.Width, b.Texture.Height);
}
The game itself then use both of them (double buffering attempt):
public void GameDraw(PictureBox p)
{
Graphics g = Graphics.FromImage(gb);
gameGrid.Draw(g);
PlayingShape.Draw(g);
p.Image = gb;
p.Refresh();
}
where "gb" is a private Bitmap variable I create just once in the class constructor (to reduce (unsuccessfully) the lag).
The GameDraw method is called whenever the state of the game is changed (e.g. moving/rotating the active tetromino and every "gravity" tick)
You need Double buffering, which you did not set. Quoting MSDN:
Double buffering uses a memory buffer to address the flicker problems
associated with multiple paint operations. When double buffering is
enabled, all paint operations are first rendered to a memory buffer
instead of the drawing surface on the screen
You can enable it using Control.DoubleBuffered property
No need for picture box, add your own control:
using System.Drawing;
using System.Windows.Forms;
namespace TetrisGame
{
public sealed class TetrisControl : Control
{
private TheBlockType[][] blocks = ...;
protected override void OnPaint(PaintEventArgs e)
{
//draw your stuff here direct to the control, no buffers in the middle
//if that causes flickering, turn on double buffering, but don't bother doing it yourself
//this is your existing draw method:
Draw(e.Graphics);
}
}
}
Then on every tick or movement, do not call paint, just invalidate the control:
tetris.Invalidate();
Also, think out of the box... Rather than doing a full grid scan, you could make each of your shapes part of a linked-list and redraw them based on their position in the grid... Until your grid completely fills, you'd be doing less scanning.
Or, consider only redrawing what you need to redraw, i.e. a block drops near the top, no need to completely redraw the full grid.
Optimisation is what separates us from the animals. Apart from the platypus, who is an optimal creature.

Are vector based generators the best way to create barcodes?

Is vector based generators the best way to generate barcodes? If yes, what are the namespaces that it will make use of? How is it used? Can anyone share some knowledge on this?
Assuming that we are talking about UPC like barcodes, vector based generation is not a must. It's the matter of representing some bits as vertical lines. So, you can easily do this using any graphic library or even using direct access to video buffer. You can represent a single bit with multiple pixels if you need a larger barcode. You don't need to use any interpolation I guess. But if you need a certain size (in pixels/centimeters etc.), vector based solution might be handful but still not a must.
C# source code example for generating scalable barcode graphics.
Steps:
1) Open a new C# Windows Forms sample project named BarCode.
2) Add a PictureBox and change BackColor to White and Dock to Fill.
3) Add Load and Resize events to Form1.
4) Copy & Paste the source code below over Form1.cs file.
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;
namespace BarCode
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public bool[] barCodeBits;
private void Form1_Load(object sender, EventArgs e)
{
Random r = new Random();
int numberOfBits = 100;
barCodeBits = new bool[numberOfBits];
for(int i = 0; i < numberOfBits; i++) {
barCodeBits[i] = (r.Next(0, 2) == 1) ? true : false;
}
Form1_Resize(null, null);
}
private void Form1_Resize(object sender, EventArgs e)
{
int w = pictureBox1.Width;
int h = pictureBox1.Height;
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics g = Graphics.FromImage(pictureBox1.Image);
Brush b = new SolidBrush(Color.Black);
for(int pos = 0; pos < barCodeBits.Length; pos++) {
if(barCodeBits[pos]) {
g.FillRectangle(b, ((float)pos / (float)barCodeBits.Length) * w, 0, (1.0f / (float)barCodeBits.Length) * w, h);
}
}
}
}
}
You don't have to develop barcodes using vector based graphics. I fact have a look at this link on codeproject as most of the work is already done for you. This genrates a bitmap of the required barcode.

Categories

Resources