This tutorial shows how to load a multipage image, reverse the order of its pages, and export the file to an animated GIF in a WinForms C# application using the LEADTOOLS SDK.
Overview | |
---|---|
Summary | This tutorial covers how to reverse multipage images and export them as animated GIF images in a WinForms C# Application. |
Completion Time | 45 minutes |
Visual Studio Project | Download tutorial project (11 KB) |
Platform | Windows WinForms C# Application |
IDE | Visual Studio 2017, 2019 |
Development License | Download LEADTOOLS |
Get familiar with the basic steps of creating a project by reviewing the Add References and Set a License tutorial, before working on the Convert a Multipage Image to Reverse Animated GIF - WinForms C# tutorial.
Also review the Display Images in an Image Viewer tutorial for details on working with WinForms menus and events.
Start with a copy of the project created in the Add References and Set a License tutorial. If you do not have that project, follow the steps in that tutorial to create it.
The references needed depend upon the purpose of the project. References can be added by one or the other of the following two methods (but not both).
If using NuGet references, this tutorial requires the following NuGet package:
Leadtools.Viewer.Controls.WinForms
If using local DLL references, the following DLLs are needed.
The DLLs are located at <INSTALL_DIR>\LEADTOOLS21\Bin\Dotnet4\x64
:
Leadtools.dll
Leadtools.Drawing.dll
Leadtools.Controls.WinForms.dll
Leadtools.Codecs.dll
Leadtools.Codecs.Gif.dll
Leadtools.Codecs.Tif.dll
For a complete list of which DLL files are required for your application, refer to Files to be Included With Your Application.
The License unlocks the features needed for the project. It must be set before any toolkit function is called. For details, including tutorials for different platforms, refer to Setting a Runtime License.
There are two types of runtime licenses:
Note
Adding LEADTOOLS NuGet and local references and setting a license are covered in more detail in the Add References and Set a License tutorial.
With the project created, the references added, and the license set, coding can begin.
In Solution Explorer, double-click Form1.cs
to display it in the Designer. Click the Events icon in the Properties Windows. Then, double-click the Load event to create an event handler if one does not already exist. This will bring up the code behind the form.
Add the using
statements below to the top.
// Using block at the top
using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using Leadtools;
using Leadtools.Controls;
using Leadtools.Codecs;
using Leadtools.Drawing;
Add the follow global variables to the Form
class.
private RasterPictureBox _pictureBox;
RasterImage _forwardImage = null;
RasterImage _reversedImage = null;
CodecsImageInfo _imageInfo;
Add the following code inside the Form1_Load
event handler to initialize the RasterPictureBox
object.
private void Form1_Load(object sender, EventArgs e)
{
_pictureBox = new RasterPictureBox();
_pictureBox.SizeMode = RasterPictureBoxSizeMode.Fit;
_pictureBox.AutoDisposeImage = false;
RasterPaintProperties paintProperties = _pictureBox.PaintProperties;
paintProperties.PaintEngine = RasterPaintEngine.GdiPlus;
_pictureBox.PaintProperties = paintProperties;
_pictureBox.Dock = DockStyle.Fill;
_pictureBox.BackColor = Color.DarkGray;
Controls.Add(_pictureBox);
_pictureBox.BringToFront();
}
Open Form1.cs
in the Solution Explorer. Go to the Toolbox and double-click MenuStrip, which will add a menu to the form. Add a File dropdown menu. Make sure the text of the dropdown menu is &File. Inside the File dropdown, add a new MenuItem, with the text &Open. Leave the new item's name as openToolStripMenuItem
.
Double-click the Open
menu item to create its event handler.
Add the following code to the openToolStripMenuItem_Click
event handler to load your input file.
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
{
try
{
using (RasterCodecs codecs = new RasterCodecs())
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "GIF image|*.gif|All files|*.*";
if (dlg.ShowDialog(this) == DialogResult.OK)
{
var info = codecs.GetInformation(dlg.FileName, true);
if(info.TotalPages < 2)
{
MessageBox.Show("File doesn't have multiple frames. Not loaded.");
return;
}
_imageInfo = info;
var loadedImage = codecs.Load(dlg.FileName, 0, CodecsLoadByteOrder.BgrOrGray, 1, -1);
AnimateAndReverse(loadedImage);
_pictureBox.Image = _forwardImage;
_pictureBox.StopAnimation();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
Add three new methods to the Form1
class named SetGifProperties(RasterImage image)
, FlattenAndReverse(RasterImage inputImage)
, and AnimateAndReverse(RasterImage originalImage)
. Call the AnimateAndReverse()
method inside the openToolStripMenuItem_Click
event handler, as shown above. Call the SetGifProperties()
and FlattenAndReverse()
methods inside the AnimateAndReverse()
method, as shown below.
Add the following code to the respective methods to animate the loaded image if it's not already animated, and to create a reversed, flattened copy of the animated image.
private void AnimateAndReverse(RasterImage originalImage)
{
for (int i = 1; i <= originalImage.PageCount; i++)
{
originalImage.Page = i;
// If frame has no delay, add a default value
if (originalImage.AnimationDelay == 0)
originalImage.AnimationDelay = 50;
}
// If original image wasn't animated, save then load GIF to animate it
if (!_imageInfo.Gif.HasAnimationLoop)
originalImage = SetGifProperties(originalImage);
_forwardImage = originalImage;
_forwardImage.Page = 1;
var tmpImage = FlattenAndReverse(originalImage);
_reversedImage = SetGifProperties(tmpImage);
}
private RasterImage SetGifProperties(RasterImage image)
{
using (RasterCodecs codecs = new RasterCodecs())
{
// Save as GIF and reload to easily set animation properites
MemoryStream ms = new MemoryStream();
image.Page = 1;
codecs.Save(image, ms, RasterImageFormat.Gif, 8);
ms.Position = 0;
_imageInfo = codecs.GetInformation(ms, true);
image = codecs.Load(ms, 24, CodecsLoadByteOrder.BgrOrGray, 1, -1);
ms.Dispose();
return image;
}
}
private RasterImage FlattenAndReverse(RasterImage inputImage)
{
int w = inputImage.AnimationGlobalSize.Width, h = inputImage.AnimationGlobalSize.Height;
if (w <= 0)
w = inputImage.Width;
if (h <= 0)
h = inputImage.Height;
RasterImage renderImage = new RasterImage(
RasterMemoryFlags.Conventional,
w,
h,
inputImage.BitsPerPixel,
inputImage.Order,
inputImage.ViewPerspective,
null,
IntPtr.Zero,
0);
// Copy the palette from the animated image to this newly created image
inputImage.CopyPaletteTo(renderImage);
RasterImage flatImage = null;
// Create the RasterImageAnimator object
RasterImageAnimator _animator = new RasterImageAnimator(renderImage, inputImage);
// Animate it and save the frames in reverse order
// Use GDI+ paint engine to support transparent colors
RasterPaintProperties props = _pictureBox.PaintProperties;
props.PaintEngine = RasterPaintEngine.GdiPlus;
using (RasterImageGdiPlusGraphicsContainer _gdiPlusGraphics = new RasterImageGdiPlusGraphicsContainer(renderImage))
{
Graphics g = _gdiPlusGraphics.Graphics;
RasterImageAnimatorState state;
bool done = false;
while (!done)
{
LeadRect srcRect = new LeadRect(0, 0, renderImage.ImageWidth, renderImage.ImageHeight);
LeadRect updateRect;
LeadRect destRect;
state = _animator.Process();
switch (state)
{
case RasterImageAnimatorState.WaitDelay:
case RasterImageAnimatorState.WaitInputDelay:
// Set the animation delay into the last frame that got inserted
flatImage.AnimationDelay = _animator.Delay;
_animator.CancelWait();
break;
case RasterImageAnimatorState.Render:
break;
case RasterImageAnimatorState.WaitInput:
// In case the animated image has the "wait for user input" flags,
// Cancel the waiting
_animator.CancelWait();
break;
case RasterImageAnimatorState.PostRender:
// Get the area in the target image that has changed
updateRect = _animator.GetUpdateRectangle(true);
destRect = new LeadRect(0, 0, renderImage.ImageWidth, renderImage.ImageHeight);
// Paint it
RasterImagePainter.Paint(renderImage, g, srcRect, updateRect, destRect, destRect, props);
if (flatImage == null)
flatImage = renderImage.Clone();
else
flatImage.InsertPage(0, renderImage.Clone()); // Insert at the beginning to reverse the frames
flatImage.Page = 1; // The newly inserted frame is always number 1
flatImage.AnimationDelay = 50; // Set value in case the animation doesn't provide one.
break;
case RasterImageAnimatorState.End:
done = true; // Exist the loop once animation is done
break;
default:
break;
}
}
}
return flatImage;
}
Navigate back to the form's Designer, using the Solution Explorer. Add a new &Save Reversed
MenuItem to the Open dropdown menu. Leave the new item's name as saveReversedToolStripMenuItem
.
Double-click the &Save Reversed
menu item to create its event handler, then add the following code in it to export the animated GIF.
private void saveReversedToolStripMenuItem_Click(object sender, EventArgs e)
{
if (_reversedImage == null)
{
MessageBox.Show("Unable to save! Please load an image first");
return;
}
try
{
SaveFileDialog saveDlg = new SaveFileDialog();
saveDlg.Filter = "GIF image|*.gif";
if (saveDlg.ShowDialog(this) != DialogResult.OK)
return;
using (RasterCodecs codecs = new RasterCodecs())
{
codecs.Save(_pictureBox.Image, saveDlg.FileName, RasterImageFormat.Gif, 8);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Open Form1.cs
in the Solution Explorer. Add a new dropdown menu, Animation, next to the File dropdown menu. Add the following three MenuItems to the new dropdown menu:
Item Text | Item Name |
---|---|
Play &Forward | playForwardToolStripMenuItem |
Play &Reversed | playReversedToolStripMenuItem |
&Stop | stopToolStripMenuItem |
Double-click each of the three new items to add event handlers for them, and implement them as follows:
private void playForwardToolStripMenuItem_Click(object sender, EventArgs e)
{
if(_forwardImage == null)
{
MessageBox.Show("Unable to play animation! Please load an image first");
return;
}
_pictureBox.Image = _forwardImage;
_pictureBox.AnimationMode = RasterPictureBoxAnimationMode.Infinite;
_pictureBox.PlayAnimation();
}
private void playReversedToolStripMenuItem_Click(object sender, EventArgs e)
{
if (_reversedImage == null)
{
MessageBox.Show("Unable to play animation! Please load an image first");
return;
}
_pictureBox.Image = _reversedImage;
_pictureBox.AnimationMode = RasterPictureBoxAnimationMode.Infinite;
_pictureBox.PlayAnimation();
}
private void stopToolStripMenuItem_Click(object sender, EventArgs e)
{
_pictureBox.StopAnimation();
}
Run the project by pressing F5, or by selecting Debug -> Start Debugging.
If the steps were followed correctly, the application runs and loads any multipage image selected by the user, ensures it is animated, and creates a backward-animated copy of the image. It also allows saving the reverse-animated image to a new GIF file, and playing both forward and reversed animations.
This tutorial showed how to add the necessary references to load, reverse, playback, and save animated images. In addition, it showed how to use the RasterPictureBox
and RasterImageAnimator
classes.