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.
Related
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.
I am making a program where you bassicly move from tile to tile in windows forms.
So in order to do that, I wanted to use panels each panel has a tag. To detect collision.
So I have an image of my map. and I divided into multiple tiles. However now I have to drag 900 tiles onto panels.
This isn't very effective in 2 ways. First loading 900 textures isn't really a smart idea. Also it would take ages. So i wanted to use a spritesheet or tilemap. But how would I do that in winforms. I believe I have seen some people use a grid view or whatever. However im not sure how to do what I want to do.
What would be the best solution?
Thanks in advance!
For any serious gaming project WinForms is not the best platform. Either WPF or XNA or Unity are able to deliver high performance use of DirectX.
But since you want to do it in Winforms here is a way to do it.
It creates a whopping number of 900 PictureBoxes and loads each with a fraction of an source image:
private void Form1_Load(object sender, EventArgs e)
{
int tileWidth = 30;
int tileHeight = 30;
int tileRows = 30;
int tileCols = 30;
using (Bitmap sourceBmp = new Bitmap("D:\\900x900.jpg"))
{
Size s = new Size(tileWidth, tileHeight);
Rectangle destRect = new Rectangle(Point.Empty, s);
for (int row = 0; row < tileRows; row++)
for (int col = 0; col < tileCols; col++)
{
PictureBox p = new PictureBox();
p.Size = s;
Point loc = new Point(tileWidth * col, tileHeight * row);
Rectangle srcRect = new Rectangle(loc, s);
Bitmap tile = new Bitmap(tileWidth, tileHeight);
Graphics G = Graphics.FromImage(tile);
G.DrawImage(sourceBmp, destRect, srcRect, GraphicsUnit.Pixel);
p.Image = tile;
p.Location = loc;
p.Tag = loc;
p.Name = String.Format("Col={0:00}-Row={1:00}", col, row);
// p.MouseDown += p_MouseDown;
// p.MouseUp += p_MouseUp;
// p.MouseMove += p_MouseMove;
this.Controls.Add(p);
}
}
}
When I tried it I was a bit worried about perfomance, but..
This takes under 1 second to load on my machine.
Starting the programm adds 10MB to VS memory usage. That is like nothing.
For a fun project this will do; for best performance one might use Panels but these will have to be filled and refilled in the Paint event. This solution saves you the hassle and since you don't change the tile picture all the time this works well enough.
Pleae note: I have added a Name and a Tag to each PictureBox, so you can later refer to it. These both contain info about the original position of the Picturebox. The Name looks like this: Col=23-Row=02 and the Tag is the original Location object.
Also: Dynamically added controls take a little extra to script since you can't create their method bodies in the designer. Instead you add them like above. In doing so Intellisense and the Tab key are your best friends..
I have added three event handlers for a few mouse events. When you uncomment them you will have to add the methods like e.g. this:
void p_MouseMove(object sender, MouseEventArgs e)
{
throw new NotImplementedException();
}
But maybe you want to use other events to play like Drag&Drop or keyboard events..
There are two ways to refer to these tiles. Maybe you want to try and/or use both of them: You can loop over the form's controls with a
foreach (Control ctl in this.Controls)
{ if (ctl is PictureBox ) this.Text = ((PictureBox)ctl).Name ; }
It tests for the right type and then casts to PictureBox. As an example it displays the name of the tile in the window title.
Or you can have a variable and set it in the MouseDown event:
PictureBox currentTile;
void p_MouseDown(object sender, MouseEventArgs e)
{
currentTile = (PictureBox ) sender;
}
/I'm working with and testing on a computer that is built with the following:
{1 GB RAM (now 1.5 GB), 1.7 GHz Intel Pentium Processor, ATI Mobility Radeon X600 GFX}
I need scale / transform controls and make it flow smoothly. Currently I'm manipulating the size and location of a control every 24-33ms (30fps), ±3px. When I add a 'fade' effect to an image, it fades in and out smoothly, but it is only 25x25 px in size. The control is 450x75 px to 450x250 px in size. In 2D games such as Bejeweled 3, the sprites animate with no choppy animation.
So as the title would suggest: which is easier/faster on the processor: animating a bitmap (rendering it to the parent control during animation) or animating the control it's self?
EDIT:
Hey, I thought this was a helpful community, not one that down-rates questions that don't seem challenging! (And I've seen more ridiculous questions here with better ratings too!) Please drop me a line first before negatively rating my questions!
I managed to find some free-time in my heck-tick scheduled, to quickly whip up a new project. I'm sure my time could have been better spent else where but hopefully someone else in my shoes may find this of use out there...
The answer is: a Picture over a Control. When rendering a bitmap onto the canvas, there are very little events that will fire, if any. As for the control, it is filled with events - some chained, some looped, and the addition of recursion, so a simple 'LocationChanged' event wouldn't even cover the half of what actually is taking place under the hood.
What I would do for controls that have lots of dynamic animations applied to them during runtime, is to develop a two piece set: a control [rendering] template or active interface (for when the control is at a stand-still or before the play of an animation), and a the animating structure with basic defining properties such as the display image [the rendered control], the rectangle bounds, and any animation algorithms that may be applied latter.
Edit: As Requested, here are the before and after code examples:
// This is the triggering event of the translating animation
private void object_Click(object sender, EventArgs e)
{
// the starting point is at (75,75)
element.Transform(new Point(500, 250));
}
Before:
public class ControlElement : UserControl
{
private Timer tick;
private Point pT0;
public ControlElement() : base()
{
tick = new Timer();
tick.Interval = 30; // about 30fps
tick.Tick += new EventHandler(tick_Tick);
}
void tick_Tick(object sender, EventArgs e)
{
// get the new point from distance and current location/destination
this.Location = Utils.Transform(this.Location, pT0, 3);
if ((pT0.X - this.Location.X)+(pT0.Y - this.Location.Y) <= 0)
{
this.Location = pT0;
tick.Stop();
//this.Visible = true;
}
}
public void Transform(Point destination)
{
pT0 = destination;
//this.Visible = false;
tick.Start();
}
}
After: I create a class that holds a picture of what the control would look like using the DrawToBitmap feature. It still contains the same animation methods as above. I had to add the Location and LocationChanged elements since this class was no longer a control. If and when the actual control needed to be accessed, I would stop rendering and display an instance of the control it's self.
Here is the rendering call:
void element_LocationChanged(object sender, EventArgs e)
{
canvas.Invalidate();
}
void canvas_Paint(object sender, PaintEventArgs e)
{
if (element != null)
{
Bitmap bmp = new Bitmap(element.Display);
Pen p = new Pen(Color.FromArgb(128, 128, 128), 1);
e.Graphics.DrawImage(bmp, element.Location);
e.Graphics.DrawRectangle(p,
element.Location.X, element.Location.Y,
bmp.Width, bmp.Height);
}
}
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.
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);
}
}