|
#region Using Statements
using System;
using System.Collections.Generic;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using AViD.Figures;
using System.ComponentModel;
using System.Threading;
using Gif.Components;
#endregion Using Statements
namespace AViD
{
/// <summary>
/// The AnimatedGifBuilder class constructs the animated gif with the provided inputs asynchronously.
/// </summary>
public class AnimatedGifBuilder : IWorker
{
#region Constants
/// <summary>
/// The default time we'll wait on the thread after we've aborted it.
/// </summary>
private const int DEFAULT_JOIN_TIME = 5000;
#endregion Constants
#region Constructor
/// <summary>
/// Constructs a new <see cref="AnimatedGifBuilder"/> instance to construct an animated gif.
/// </summary>
/// <param name="strFileName">The filename of which the animated gif will be written to.</param>
/// <param name="intNumberOfFrames">The number of frames to create in the animated gif.</param>
/// <param name="intDelayBetweenFrames">The delay in milliseconds between the frames of the animated gif.</param>
/// <param name="formAViD">The <see cref="AViD"/> form which will allow us to capture the screen shots.</param>
/// <param name="camera">The camera which controls the point of view of the capture.</param>
public AnimatedGifBuilder(string strFileName, int intNumberOfFrames, int intDelayBetweenFrames, AViD formAViD, Camera camera)
{
this.m_blnAbortBuild = false;
this.m_blnEnableEvents = true;
this.m_Handlers = new EventHandlerList();
this.m_Thread = null;
this.m_strFileName = strFileName;
this.m_intNumberOfFrames = intNumberOfFrames;
this.m_intDelayBetweenFrames = intDelayBetweenFrames;
this.m_formAViD = formAViD;
this.m_Camera = camera;
}
#endregion Constructor
#region Public Properties
/// <summary>
/// Gets/sets whether or not the events are enabled.
/// </summary>
public bool EnableEvents
{
get
{
lock (this.m_objThreadLock)
{
return this.m_blnEnableEvents;
}
}
set
{
lock (this.m_objThreadLock)
{
if (!this.IsWorking)
{
this.m_blnEnableEvents = value;
}
}
}
}
/// <summary>
/// Gets whether or not the AnimatedGifBuilder is working.
/// </summary>
public bool IsWorking
{
get
{
lock (this.m_objThreadLock)
{
return ((this.m_Thread != null) && this.m_Thread.IsAlive);
}
}
}
/// <summary>
/// Gets the exception that was produced (if any) during the thread's execution.
/// </summary>
public Exception Exception
{
get
{
lock (this.m_objThreadLock)
{
return this.m_Exception;
}
}
protected set
{
lock (this.m_objThreadLock)
{
this.m_Exception = value;
}
}
}
#endregion Public Properties
#region Public Methods
/// <summary>
/// Attempts to abort the current task.
/// </summary>
public void Abort()
{
Thread thread;
if (this.IsWorking)
{
// Attempt to inform the thread to abort (this is more graceful than an abort call on the thread).
this.AbortBuild = true;
if ((thread = this.Thread) != null)
{
// Attempt to join on the thread, waiting for it to end.
thread.Join(AnimatedGifBuilder.DEFAULT_JOIN_TIME);
}
// Clear the thread out.
this.Thread = null;
}
}
/// <summary>
/// Performs the build of the animated gif asynchronously.
/// </summary>
public void Work()
{
lock (this.m_objThreadLock)
{
if (this.IsWorking && !this.AbortBuild)
throw new ArgumentException("Existing thread still exists, thus the build was aborted.");
else
{
// Clear the abort work flag.
this.AbortBuild = false;
this.m_Thread = new Thread(new ThreadStart(this._Build));
this.m_Thread.Start();
}
}
}
#endregion Public Methods
#region Events/Delegates
/// <summary>
/// Handles the event at which the AnimatedGifBuilder is done.
/// </summary>
public event EventHandler Complete
{
add
{
lock (this.m_objThreadLock)
{
this.m_Handlers.AddHandler(this.m_objCompleteEventHandler, value);
}
}
remove
{
lock (this.m_objThreadLock)
{
this.m_Handlers.RemoveHandler(this.m_objCompleteEventHandler, value);
}
}
}
/// <summary>
/// Handles the event at which the AnimatedGifBuilder has a progress message for an external body.
/// </summary>
public event EventHandler<ProgressEventArgs> Progress
{
add
{
lock (this.m_objThreadLock)
{
this.m_Handlers.AddHandler(this.m_objProgressEventHandler, value);
}
}
remove
{
lock (this.m_objThreadLock)
{
this.m_Handlers.RemoveHandler(this.m_objProgressEventHandler, value);
}
}
}
/// <summary>
/// Gets the screen shot of the currently displayed dendrimer.
/// </summary>
/// <param name="blnShowSelectedFigures">If true, the glow given to selected figures will be shown; If false, it will not be shown.</param>
/// <param name="blnForceRender">If true, forces the form to rerender the dendrimer.</param>
/// <returns>The current screen shot of the dendrimer being rendered.</returns>
public delegate Bitmap GetScreenShotHandler(bool blnShowSelectedFigures, bool blnForceRender);
#endregion Events/Delegates
#region Protected Members
/// <summary>
/// Gets/sets the flag to indicate the build needs to be aborted.
/// </summary>
protected bool AbortBuild
{
get
{
lock (this.m_objThreadLock)
{
return this.m_blnAbortBuild;
}
}
set
{
lock (this.m_objThreadLock)
{
this.m_blnAbortBuild = value;
}
}
}
/// <summary>
/// Gets/sets the <see cref="Thread"/> used to execute the build asynchronously.
/// </summary>
protected Thread Thread
{
get
{
lock (this.m_objThreadLock)
{
return this.m_Thread;
}
}
set
{
lock (this.m_objThreadLock)
{
this.m_Thread = value;
}
}
}
#endregion Protected Members
#region Protected Methods
/// <summary>
/// Notifies any listeners of what the current progress is.
/// </summary>
protected void SendProgress()
{
double dblPercentComplete;
if (this.m_intNumberOfFrames > 0)
dblPercentComplete = (double)this.m_intCurrentFrame / (double)this.m_intNumberOfFrames;
else
dblPercentComplete = 1.0d;
this.OnProgress(new ProgressEventArgs(Math.Max(0.0d, Math.Min(dblPercentComplete, 1.0d))));
}
/// <summary>
/// Handles firing the Progress event for the AnimatedGifBuilder.
/// </summary>
/// <param name="e">The <see cref="ProgressEventArgs"/> arguments for the event.</param>
protected void OnProgress(ProgressEventArgs e)
{
EventHandler<ProgressEventArgs> handler;
if (this.EnableEvents)
{
if ((handler = (this.m_Handlers[this.m_objProgressEventHandler] as EventHandler<ProgressEventArgs>)) != null)
handler(this, e);
}
}
/// <summary>
/// Handles firing the Complete event for the Worker.
/// </summary>
protected void OnComplete()
{
EventHandler handler;
if (this.EnableEvents)
{
if ((handler = (this.m_Handlers[this.m_objCompleteEventHandler] as EventHandler)) != null)
handler(this, EventArgs.Empty);
}
}
#endregion Protected Methods
#region Private Members
private object m_objThreadLock = new object();
private object m_objCompleteEventHandler = new object();
private object m_objProgressEventHandler = new object();
private bool m_blnAbortBuild;
private bool m_blnEnableEvents;
private EventHandlerList m_Handlers;
private Thread m_Thread;
private Exception m_Exception;
private readonly string m_strFileName;
private readonly int m_intNumberOfFrames;
private readonly int m_intDelayBetweenFrames;
private readonly AViD m_formAViD;
private readonly Camera m_Camera;
private int m_intCurrentFrame;
#endregion Private Members
#region Private Methods
/// <summary>
/// Builds the animated gif by rotating and then capturing the displayed image of the dendrimer.
/// </summary>
private void _Build()
{
AnimatedGifEncoder gifEncoder;
GetScreenShotHandler delegateScreenShot;
float fltAnglePerIteration;
// Initialize the build.
this.m_intCurrentFrame = 0;
// Creates the Gif Encoder.
gifEncoder = new AnimatedGifEncoder();
try
{
// Set the output file name.
gifEncoder.Start(this.m_strFileName);
// Set the delay between the frames.
gifEncoder.SetDelay(this.m_intDelayBetweenFrames);
// Set the image to repeat infinitly.
gifEncoder.SetRepeat(0);
// Determine the angle per iteration.
fltAnglePerIteration = (((float)Math.PI) * 2.0f) / (float)this.m_intNumberOfFrames;
// Generate the delegate.
delegateScreenShot = new GetScreenShotHandler(this.m_formAViD.GetScreenShot);
// Now, rotate the camera around taking snapshots.
for (this.m_intCurrentFrame = 0; this.m_intCurrentFrame < this.m_intNumberOfFrames; ++this.m_intCurrentFrame)
{
// Rotate the camera.
this.m_Camera.Rotate(fltAnglePerIteration, 0);
// Rerender the scene and then capture the screenshot.
using (Bitmap image = (Bitmap)this.m_formAViD.Invoke(delegateScreenShot, new object[] { true, true }))
gifEncoder.AddFrame(image);
// Notify any listeners of the progress we've made.
this.SendProgress();
if (this.AbortBuild)
return;
}
}
catch (Exception e)
{
// Save off this exception.
this.Exception = e;
}
finally
{
// Finishes the animated gif.
gifEncoder.Finish();
// Let the calling body know the work is done.
this.OnComplete();
}
}
#endregion Private Methods
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Since I've begun my profession as a software developer, I've learned one important fact - change is inevitable. Requirements change, code changes, and life changes.
So..If you're not moving forward, you're moving backwards.