I'm displaying some gifs in my c# .net program using PictureBox controls.
I want to emulate the way browsers like chrome, firefox, etc set a minimum frame delay for their gifs.
There's one gif that has 0 framedelay and shows up really fast in my program, but slower in browser because the browser sets the delay.
I'm getting the frame delay rate with this code, but I don't know how to set it.
PropertyItem item = img.GetPropertyItem(0x5100);
The only answers I've found online haven't been very detailed, and just say "ignore the framerate" without telling me much. Is there a way to make a copy of my gif and explicitly set the frame delay property without saving the image? The nature of the program is dynamic, so the gifs in question may be anything and it has to be flexible so I can't just change the frame delay once.
Edit: I can only think of having to go into the binary file of the gif itself and changing it there, but this seems like a much more complicated solution to the relatively simple problem.
Maybe the simplest approach would be to write your own mini-player:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private AnimatedGif _animatedGif;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_animatedGif = new AnimatedGif(#"..\..\playing-cards.gif");
}
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
var animatedGif = _animatedGif;
var frames = animatedGif.Frames;
for (var i = 0; i < frames; i++)
{
var image = animatedGif.GetFrame(i);
pictureBox1.Image = image;
var millisecondsTimeout = animatedGif.Durations[i] * 10;
Thread.Sleep(millisecondsTimeout);
}
});
}
}
internal class AnimatedGif
{
public AnimatedGif(string filename)
{
if (filename == null) throw new ArgumentNullException("filename");
var image = Image.FromFile(filename);
var item = image.PropertyItems.SingleOrDefault(s => s.Id == 0x5100);
if (item == null) throw new ArgumentNullException("filename");
var frames = item.Value.Length / 4;
var durations = new int[frames];
for (var i = 0; i < frames; i++)
{
durations[i] = BitConverter.ToInt32(item.Value, i * 4);
}
Frames = frames;
Durations = durations;
Image = image;
}
public Image Image { get; set; }
public int Frames { get; set; }
public int[] Durations { get; set; }
public Image GetFrame(int index)
{
var activeFrame = Image.SelectActiveFrame(FrameDimension.Time, index);
if (activeFrame != 0) return null;
var bitmap = new Bitmap(Image);
return bitmap;
}
}
}
Then improve it by accounting for looping, background, etc ... IDs for these properties are explained here : Property Item Descriptions
Related
I have some code below that can take the current Image and displaying it whenever I click a PushSnap Button. How can I go about having it continuously capturing and displaying the updated image in an interval (say 100 ms)
private void PushSnap_Click(object sender, EventArgs e)
{
if (mycam == null)
{
MessageBox.Show("Internal Error: mycam is null");
return; // internal error
}
string text = "";
if (IsMyFormStatus_Opened())
{
if (!mydcam.buf_alloc(3))
{
MessageBox.Show("Frame allocation failed");
return;
}
}
// start acquisition
mycam.m_capmode = CAMCAP_START.SNAP; //one time capturing where Acquisition will start after m_nFrameCount frames
if (!mycam.cap_start())
{
return;
}
MyFormStatus_Acquiring();
MyThreadCapture_Start();
}
The Following MyThreadCapture_Start()
private void MyThreadCapture_Start()
{
m_threadCapture = new Thread(new ThreadStart(OnThreadCapture));
m_threadCapture.IsBackground = true;
m_threadCapture.Start();
}
and the following OnThreadCapture()
private void OnThreadCapture()
{
using (mycamwait = new MycamWait())
{
while (True)
{
CAMWAIT eventmask = CAMWAIT.CAPEVENT.FRAMEREADY | CAMWAIT.CAPEVENT.STOPPED;
DCAMWAIT eventhappened = DCAMWAIT.NONE;
if (mycamwait.start(eventmask, ref eventhappened))
{
if (eventhappened & CAMWAIT.CAPEVENT.FRAMEREADY)
{
int NewestFrame = 0;
int FrameCount = 0;
if (mycam.cap_transferinfo(ref NewestFrame, ref FrameCount))
{
MyUpdateImage(iNewestFrame);
}
}
if (eventhappened & CAMWAIT.CAPEVENT.STOPPED)
{
bContinue = false;
if (m_cap_stopping == false && mycam.m_capmode == CAMCAP_START.SNAP)
{
//cap_stop() happens automatically, therefore update the main dialog
//MySnapCaptureFinished();
}
}
}
}
}
Afterwards the Display is updated in the following way
private void UpdateDisplay()
{
Image oldImg = PicDisplay.Image;
if (m_bitmap != null)
{
// Show center of image
Int32 y0 = (m_image.height - PicDisplay.Height) / 2;
Int32 x0 = (m_image.width - PicDisplay.Width) / 2;
Rectangle rc = new Rectangle(x0, y0, PicDisplay.Width, PicDisplay.Height);
Bitmap bmp = new Bitmap(PicDisplay.Width, PicDisplay.Height, PixelFormat.Format24bppRgb);
using (var gr = Graphics.FromImage(bmp))
{
gr.DrawImage(m_bitmap, 0, 0, rc, GraphicsUnit.Pixel);
}
PicDisplay.Image = bmp;
PicDisplay.Refresh();
}
else
{
PicDisplay.Image = null;
}
if (oldImg != null)
oldImg.Dispose();
}
I would really recommend using Tasks instead of Threads. That should let you update your methods to take parameters and return results:
Image CaptureImage(); // I.e. similar code to OnThreadCapture
UpdateDisplay(Image);
And capture an image like:
public async void PushSnap_Click(){
...
try{
var image = await Task.Run(CaptureImage);
DisplayImage(image);
}
catch{
// Handle exceptions
}
That should let you simply rewrite your capture code to use a loop if you want live capture:
while(showLiveImagesBool){
var image = await Task.Run(CaptureImage);
DisplayImage(image);
}
The await-part should ensure the UI thread is not blocked while waiting for an image to be captured. If you do not want to show every image you might use a timer instead of a loop, just pick a timer that runs on the UI thread.
Keep in mind that showing live images will require a bit more performance, so you might need to optimize display and/or capture code. Ideally you should reuse image buffers when doing the capturing and displaying, otherwise you will allocate a fair amount of large objects that require a 2 gen GC to clean up.
I need to save an image to disk that came from a web-cam between 5 and 10 seconds ago from when the "save" command comes in via serial port.
To get there, I have the webcam going into a pictureBox.Image (using opencv4), and then 2 Bitmap variables. Every 5 seconds a timer ticks, and Stored_bitmap_2 = Stored_bitmap_1, then Stored_bitmap_1 = (bitmap) pictureBox.Image.
When the right serial command comes in, I try to
Stored_image_2.Save("C:\Users\GreenWorld\Desktop\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
and I get a run-time error of invalid parameter.
When I do the same thing in a stand-alone project with some buttons (inside the button-click event handler), it works every time.
When I do this inside the serialPort_DataReceived handler, I get all kinds of cross-thread errors. So, I moved the save attempt to its own subroutine, and that fixed that but now this.
I am by no means a professional programmer, I'm an engineer with a simple problem and I can usually write a little simplistic code to fix my immediate issue. Please go easy on me in the explanation :-)
Sample code:
using OpenCvSharp;
using OpenCvSharp.Extensions;
namespace Weld_picture
{
public partial class Form1 : Form
{
// Create class-level accessible variables
int Welding_camera_ID = 1;
VideoCapture capture;
Mat frame;
Bitmap image;
private Thread camera;
bool isCameraRunning = false;
string Serial_command = "";
Bitmap Stored_image_1;
Bitmap Stored_image_2;
bool Image_saved = false;
string Station_ID = "GWM-PWS-01";
string File_name = "";
private void CaptureCamera() // from someone else's sample code
{
camera = new Thread(new ThreadStart(CaptureCameraCallback));
camera.Start();
}
private void CaptureCameraCallback() // from someone else's sample code
{
frame = new Mat();
capture = new VideoCapture(Welding_camera_ID);
capture.Open(Welding_camera_ID);
if (capture.IsOpened())
{
while (isCameraRunning)
{
capture.Read(frame);
image = BitmapConverter.ToBitmap(frame);
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = image;
}
}
}
public Form1()
{
InitializeComponent();
SerialPort1.Open();
}
private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string dummy;
char CR = (char)0x0D;
while (SerialPort1.BytesToRead > 0)
{
Serial_command += (char)SerialPort1.ReadByte();
}
while (Serial_command.IndexOf(CR) > 0)
{
dummy = Serial_command.Substring(0, Serial_command.IndexOf(CR));
Serial_command = Serial_command.Substring(Serial_command.IndexOf(CR) + 1, (Serial_command.Length - (Serial_command.IndexOf(CR) + 1)));
Serial_command.Trim();
dummy.Trim();
proc_Process_serial_data(dummy);
}
}
//*************************************************************************************************************************************
/* the first timer is a 5-second interval. It's the "memory" function so that if/when the save-to-disk is triggered I can store the last-time-shutter-open image */
//*************************************************************************************************************************************
private void Timer_picture_interval_Tick(object sender, EventArgs e)
{
checkBox1.Checked = !checkBox1.Checked;
Timer_picture_interval.Stop();
Stored_image_2 = Stored_image_1;
Stored_image_1 = (Bitmap) pictureBox1.Image;
Timer_picture_interval.Start();
}
//*************************************************************************************************************************************
// the second timer is a 30-second interval. It's the way to turn capture off if the PLC/camera box somehow goes off-line
//*************************************************************************************************************************************
private void Timer_camera_powerdown_Tick(object sender, EventArgs e)
{
if (isCameraRunning)
capture.Release();
isCameraRunning = false;
Timer_picture_interval.Stop();
}
//*************************************************************************************************************************************
private void proc_Process_serial_data(string Serial_string)
{
if (Serial_string.IndexOf("Still here") > 0)
{
if (!isCameraRunning)
CaptureCamera();
isCameraRunning = true;
}
if (Serial_string.IndexOf("Sun's up") > 0)
{
Timer_picture_interval.Start();
Timer_camera_powerdown.Start();
}
if (Serial_string.IndexOf("It's dark") > 0)
{
if ((Stored_image_2 != null) && (!Image_saved)) // in case there's 2 subsequent requests to save the same thing (weld stutter)
{
File_name = "C:\\Users\\GreenWorld\\Desktop\\" + Station_ID + " D" + DateTime.Now.ToString("yyyy_MM_dd THH_mm_ss") + ".jpg";
Stored_image_2.Image.Save("C:\\Users\\GreenWorld\\Desktop\\test.bmp" , System.Drawing.Imaging.ImageFormat.Bmp );
Image_saved = true;
Timer_picture_interval.Stop();
}
Timer_camera_powerdown.Start();
}
}
}
}
You likely be able to fix the cros-threading error by simply calling your proc_Process_serial_data() method within an Invoke() call.
Change:
proc_Process_serial_data(dummy);
To:
this.Invoke((MethodInvoker)delegate ()
{
proc_Process_serial_data(dummy);
});
Also, these two lines aren't actually doing anything:
Serial_command.Trim();
dummy.Trim();
To Trim() the strings, you have capture the returned strings and re-assign them to the original variables:
Serial_command = Serial_command.Trim();
dummy = dummy.Trim();
I am using NAudio in a C#-Application and my problem is, that the playback speed depends on the sampling rate. I used two Wav files that contain a 1000Hz sin wave with either Fs = 44100Hz and Fs = 88200 Hz. I checked the output signal of my soundcard with an oscilloscope. It turns out that my file with Fs = 44100 is over after half the expected time, whereas the output frequency is doubled (2kHz). When using the files in other players (e.g. windows media player, audacity) everything looks fine. When debugging I looked into the waveformat of these files and everything looked fine as well. I also varied the bit resolution resulting in no difference. I am not sure if i am just missing out of something.
I use this in another program where this problem first occured. Therefore I made a small rudimentary programm to see if I already made a mistake in my bigger application, the same problem exists there aswell.
It would be really great and highly appreciated, if someone could help me.
Kind regards
Leo
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 NAudio.Wave;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
AsioOut asioOut;
AudioFileReader afr4;
AudioFileReader afr8;
AudioFileReader afr0;
MixingWaveProvider32 mwp;
int playingID = 0;
int channelID;
Boolean audioplaying = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
asioOut = new AsioOut();
afr4 = new AudioFileReader("../wavs/sin1000Hz_1.75s_16bit_44100Hz_ramp50ms.wav");
afr8 = new AudioFileReader("../wavs/sin1000Hz_1.75s_16bit_88200Hz_ramp50ms.wav");
afr0 = new AudioFileReader("../wavs/sin14607Hz_180sec_20msRamp_fs96000_24bit_mono_doppelt.wav");
mwp = new MixingWaveProvider32();
afr4.Volume = 1.0f;
afr8.Volume = 1.0f;
for (int i = 0; i < asioOut.DriverOutputChannelCount; i++) {
Console.WriteLine(i + ": " + asioOut.AsioOutputChannelName(i));
if (asioOut.AsioOutputChannelName(i).Equals("Analog 3 (1)")) {
channelID = i;
}
}
asioOut.ChannelOffset = channelID;
asioOut.Init(mwp);
asioOut.PlaybackStopped += OnPlaybackStopped;
}
//a button to play the Fs = 44100Hz File
private void button1_Click(object sender, EventArgs e)
{
if (audioplaying == false) {
mwp.AddInputStream(afr4);
afr4.Position = 0;
audioplaying = true;
playingID = 4;
asioOut.Play();
}
}
//a button to play the Fs = 88200Hz File
private void button2_Click(object sender, EventArgs e)
{
if (audioplaying == false) {
mwp.AddInputStream(afr8);
afr8.Position = 0;
audioplaying = true;
playingID = 8;
asioOut.Play();
}
}
//a button to play just another audiofile
private void button4_Click(object sender, EventArgs e)
{
if (audioplaying == false) {
mwp.AddInputStream(afr0);
afr0.Position = 0;
audioplaying = true;
playingID = 0;
asioOut.Play();
}
}
//after playback every Input gets removed again to setup for new playback
protected virtual void OnPlaybackStopped(object sender, EventArgs e) {
if (playingID == 4) {
mwp.RemoveInputStream(afr4);
} else if (playingID == 8) {
mwp.RemoveInputStream(afr8);
} else {
mwp.RemoveInputStream(afr0);
}
audioplaying = false;
}
private void button3_Click(object sender, EventArgs e)
{
asioOut.Stop();
}
}
}
You can't use MixingWaveProvider32 to mix together streams with different WaveFormat. You need to pick a single WaveFormat and pass that into the constructor. You should then only add items that have that WaveFormat although MixingWaveProvider32 isn't enforcing that when you only have one item at a time like you are doing
I am studying C# Programming and Kinect sensors programming (I am quite a
newbie in both C# language and Kinect). I am trying to use Microsoft Visual C# 2010 Express to write applications using sensors of Xtion Pro Live to control robots. In my main form, there are 2 picturebox objects, 2 button object, 3 label and 3 textbox objects. One of the two picturebox objects (pictureBox1) is to display the RGB camera. The other one pictureBox2) is to simulate the hand positions in 2D (x and y coordinates) graphics. One of the two buttons (button1) is to initialize Xtion Pro Live and to show RGB camera on picturebox1, and to simulate hand positions on picturebox2. The other one (button2) is to exit the program. The three labels, label1, label2, label3, and the three textboxes, textbox1, textbox2, textbox3 are used to show 3 coordinates of the hand.
This is my code in Form1.cs:
using OpenNI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace HandTracking
{
public partial class Form1 : Form
{
public const string path = #"C:/Vu/Kinect/Data/SamplesConfig.xml";
public bool run;
public Thread thread;
public Bitmap bitmap;
public Context context;
public ScriptNode node;
public ImageGenerator image;
public DepthGenerator depth;
public GestureGenerator gesture;
public HandsGenerator hand;
public Queue<Point3D> handpoint = new Queue<Point3D>();
public const int maxpoint = 30;
enum GestureStatus
{
Unrecognized, Progress, Recognized
}
private GestureStatus gesstatus = GestureStatus.Unrecognized;
enum HandsStatus
{
Untracked, Create, Update
}
private HandsStatus handstatus = HandsStatus.Untracked;
public Pen pen = new Pen(Color.Red, 5);
public Brush brush = new SolidBrush(Color.Magenta);
public Font font = new Font("Times New Roman", 20);
public PointF point = new PointF(0, 0);
public Form1()
{
InitializeComponent();
this.pictureBox1.BackColor = Color.Black;
this.pictureBox2.BackColor = Color.White;
this.button1.Enabled = true;
}
public void button1_Click(object sender, EventArgs e)
{
this.button1.Enabled = false;
try
{
context = Context.CreateFromXmlFile(path, out node);
image = context.FindExistingNode(NodeType.Image) as ImageGenerator;
if (image == null)
throw new Exception(context.GlobalErrorState);
depth = context.FindExistingNode(NodeType.Depth) as DepthGenerator;
if (depth == null)
throw new Exception(context.GlobalErrorState);
depth.AlternativeViewpointCapability.SetViewpoint(image);
gesture = context.FindExistingNode(NodeType.Gesture) as GestureGenerator;
if (gesture == null)
throw new Exception(context.GlobalErrorState);
gesture.AddGesture("RaiseHand");
gesture.GestureRecognized += new EventHandler<GestureRecognizedEventArgs>(GestureRecognized);
gesture.GestureProgress += new EventHandler<GestureProgressEventArgs> (GestureProgress);
hand = context.FindExistingNode(NodeType.Hands) as HandsGenerator;
if (hand == null)
throw new Exception(context.GlobalErrorState);
hand.HandCreate += new EventHandler<HandCreateEventArgs>(HandCreate);
hand.HandUpdate += new EventHandler<HandUpdateEventArgs>(HandUpdate);
context.StartGeneratingAll();
MapOutputMode map = image.MapOutputMode;
bitmap = new Bitmap((int)map.XRes, (int)map.YRes, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
run = true;
thread = new Thread(CallThread);
thread.Start();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
public void GestureRecognized(object sender, GestureRecognizedEventArgs e)
{
gesstatus = GestureStatus.Recognized;
hand.StartTracking(e.EndPosition);
}
public void GestureProgress(object sender, GestureProgressEventArgs e)
{
gesstatus = GestureStatus.Progress;
}
public void HandCreate(object sender, HandCreateEventArgs e)
{
handstatus = HandsStatus.Create;
}
public void HandUpdate(object sender, HandUpdateEventArgs e)
{
handstatus = HandsStatus.Update;
handpoint.Enqueue(e.Position);
}
public void button2_Click(object sender, EventArgs e)
{
DialogResult result = MessageBox.Show("Do you want to quit?", "Confirm", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
if (result == DialogResult.OK)
{
try
{
run = false;
if (thread != null)
thread.Join();
this.Close();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
public void CallThread()
{
try
{
while (run)
{
Data();
pictureBox1.Invalidate();
pictureBox2.Invalidate();
}
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
public unsafe void Data()
{
context.WaitAndUpdateAll();
ImageMetaData imd = image.GetMetaData();
lock (this)
{
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
BitmapData data = bitmap.LockBits(rect, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
byte* dstp = (byte*)data.Scan0.ToPointer();
byte* imstp = (byte*)image.ImageMapPtr.ToPointer();
for (int i = 0; i < imd.DataSize; i += 3, dstp += 3, imstp += 3)
{
dstp[0] = imstp[2];
dstp[1] = imstp[1];
dstp[2] = imstp[0];
}
bitmap.UnlockBits(data);
if (handpoint.Count != 0)
{
Point3D start = depth.ConvertRealWorldToProjective(handpoint.Peek());
foreach (Point3D hpoint in handpoint)
{
Point3D pt = depth.ConvertRealWorldToProjective(hpoint);
HandPosition(start);
start = pt;
}
}
string mess = "Gesture: RaiseHand" + " ,Status:" + gesstatus.ToString() + "\n" + "Hand: " + handstatus.ToString();
PicDraw(bitmap, mess);
}
}
public void HandPosition(Point3D pt)
{
try
{
float a,b,c;
string handx, handy, handz;
a=pt.X;
b=pt.Y;
c=pt.Z;
handx = a.ToString();
handy = b.ToString();
handz = c.ToString();
Graphics g = pictureBox2.CreateGraphics();
g.FillEllipse(brush, a - 5, b - 5, 20, 20);
textBox1.Text = handx;
textBox2.Text = handy;
textBox3.Text = handz;
g.Dispose();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
public void PicDraw(Bitmap bmap, string me)
{
try
{
Graphics g;
pictureBox1.Image = bmap;
g = Graphics.FromImage(pictureBox1.Image);
g.DrawString(me, font, brush, point);
g.Dispose();
}
catch (Exception error)
{
MessageBox.Show(error.Message);
}
}
}
}
And this is my code in Program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace HandTracking
{
static class Program
{
........
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 f1 = new Form1();
f1.Text = "Hand Tracking";
f1.StartPosition = FormStartPosition.CenterScreen;
Application.Run(f1);
}
}
}
When I compile the program many times, each time different errors are thrown: InvalidOperationException not handled; Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on; Object is currently in use elsewhere.
I don't know whether the way I pass parameters at: HandPosition(start) and PicDraw(bitmap, mess) were correct. Any problem will come out if I use HandPosition(start) to control a robot? Can anybody show me my mistakes and help me correct the progam?
The problem is that you are trying to access a WinForms control on a thread other than the UI thread. It is being done from the thread you are creating with Thread.Start.
A much better technique for Windows Forms is to use the BackgroundWorker approach (if you are using .NET 4, Task Parallel Library is very nice). With BackgroundWorker, you send the thread off to do some work, and it gives the UI thread updates, sending back any objects of your choice. Since you are newer to C#, BackgroundWorker is the right choice because it is simple to use, and there is tons of documentation on it if you get stuck.
On the UI thread, you just take those updates, and then update your WinForms controls from the UI thread. Problem fixed.
To learn more about BackgroundWorker, Google it, or you can start here:
http://www.codeproject.com/Articles/99143/BackgroundWorker-Class-Sample-for-Beginners
I found a class to make a gif file containing multiple frame animations run in front of a background image. This is my class:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace AnimSprites {
public class AnimSprite {
private int frame, interval, width, height;
private string imgFile;
private Image img;
private Timer frameTimer;
public AnimSprite(string f_imgFile, int f_width) {
frame = 0;
width = f_width;
imgFile = f_imgFile;
img = new Bitmap(imgFile);
height = img.Height;
}
public void Start(int f_interval) {
interval = f_interval;
frameTimer = new Timer();
frameTimer.Interval = interval;
frameTimer.Tick += new EventHandler(advanceFrame);
frameTimer.Start();
}
public void Start() {
Start(100);
}
public void Stop() {
frameTimer.Stop();
frameTimer.Dispose();
}
public Bitmap Paint(Graphics e) {
Bitmap temp;
Graphics tempGraphics;
temp = new Bitmap(width, height, e);
tempGraphics = Graphics.FromImage(temp);
tempGraphics.DrawImageUnscaled(img, 0-(width*frame), 0);
tempGraphics.Dispose();
return(temp);
}
private void advanceFrame(Object sender, EventArgs e) {
frame++;
if ( frame >= img.Width/width )
frame = 0;
}
}
}
How can I use this class to make my gif file (running_dog.gif) run over background.jpg from left to right?
This is the dog.gif file: dog.gif
The class you've included expects the animation frames to go from left to right rather than top to bottom as your .gif does.
You can alter it by changing the constructor to
public AnimSprite(string f_imgFile, int f_height) {
frame = 0;
height = f_height;
imgFile = f_imgFile;
img = new Bitmap(imgFile);
width = img.Width;
}
and the advanceFrame method to
private void advanceFrame(Object sender, EventArgs e) {
frame++;
if ( frame >= img.Height/height )
frame = 0;
}
}
and your call to DrawImageUnscaled to
tempGraphics.DrawImageUnscaled(img, 0, 0-(height*frame));