複数の画像からGIFアニメーションを作る
キャプチャした映像からGIFアニメーションを生成した時のもの。
ソース
ほぼコピペ。ちょっと修正
http://bloggingabout.net/blogs/rick/archive/2005/05/10/3830.aspx
3MF Project: What's In A GIF - Bit by Byte
GifClass.cs
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; namespace GifCreator { public class GifClass { public GIFVersion m_Version = GIFVersion.GIF87a; public Bitmap m_Image = null; public List<byte> m_GifSignature = new List<byte>(); public List<byte> m_ScreenDescriptor = new List<byte>(); public List<byte> m_ColorTable = new List<byte>(); public List<byte> m_ImageDescriptor = new List<byte>(); public List<byte> m_ImageData = new List<byte>(); public GifClass() { } public void LoadGifPicture(string filename) { LoadGifPicture((Bitmap)Bitmap.FromFile(filename)); } public void LoadGifPicture(Bitmap gifPicture) { MemoryStream stream = new MemoryStream(); m_Image = gifPicture; m_Image.Save(stream, ImageFormat.Gif); List<byte> dataList = new List<byte>(stream.ToArray()); if (!AnalyzeGifSignature(dataList)) { throw (new Exception("File is not a gif!")); } AnalyzeScreenDescriptor(dataList); GIFBlockType blockType = GetTypeOfNextBlock(dataList); while (blockType != GIFBlockType.Trailer) { switch (blockType) { case GIFBlockType.ImageDescriptor: AnalyzeImageDescriptor(dataList); break; case GIFBlockType.Extension: ThrowAwayExtensionBlock(dataList); break; default: break; } blockType = GetTypeOfNextBlock(dataList); } } private bool AnalyzeGifSignature(List<byte> gifData) { for (int i = 0; i < 6; i++) m_GifSignature.Add(gifData[i]); gifData.RemoveRange(0, 6); List<char> chars = m_GifSignature.ConvertAll<char>(new Converter<byte,char>(ByteToChar)); string s = new string(chars.ToArray()); if (s == GIFVersion.GIF89a.ToString()) m_Version = GIFVersion.GIF89a; else if (s == GIFVersion.GIF87a.ToString()) m_Version = GIFVersion.GIF87a; else return false; return true; } private char ByteToChar(byte b) { return (char)b; } private void AnalyzeScreenDescriptor(List<byte> gifData) { for (int i = 0; i < 7; i++) m_ScreenDescriptor.Add(gifData[i]); gifData.RemoveRange(0, 7); // if the first bit of the fifth byte is set the GlobelColorTable follows this block bool globalColorTableFollows = (m_ScreenDescriptor[4] & 0x80) != 0; if (globalColorTableFollows) { int pixel = m_ScreenDescriptor[4] & 0x07; int lengthOfColorTableInByte = 3 * ((int)Math.Pow(2, pixel + 1)); for (int i = 0; i < lengthOfColorTableInByte; i++) m_ColorTable.Add(gifData[i]); gifData.RemoveRange(0, lengthOfColorTableInByte); } m_ScreenDescriptor[4] = (byte)(m_ScreenDescriptor[4] & 0x7F); } private GIFBlockType GetTypeOfNextBlock(List<byte> gifData) { GIFBlockType blockType = (GIFBlockType)gifData[0]; return blockType; } private void AnalyzeImageDescriptor(List<byte> gifData) { for (int i = 0; i < 10; i++) m_ImageDescriptor.Add(gifData[i]); gifData.RemoveRange(0, 10); // get ColorTable if exists bool localColorMapFollows = (m_ImageDescriptor[9] & 0x80) != 0; if (localColorMapFollows) { int pixel = m_ImageDescriptor[9] & 0x07; int lengthOfColorTableInByte = 3 * ((int)Math.Pow(2, pixel + 1)); m_ColorTable.Clear(); for (int i = 0; i < lengthOfColorTableInByte; i++) m_ColorTable.Add(gifData[i]); gifData.RemoveRange(0, lengthOfColorTableInByte); } else { int lastThreeBitsOfGlobalTableDescription = m_ScreenDescriptor[4] & 0x07; m_ImageDescriptor[9] = (byte)(m_ImageDescriptor[9] & 0xF8); m_ImageDescriptor[9] = (byte)(m_ImageDescriptor[9] | lastThreeBitsOfGlobalTableDescription); } m_ImageDescriptor[9] = (byte)(m_ImageDescriptor[9] | 0x80); GetImageData(gifData); } private void GetImageData(List<byte> gifData) { m_ImageData.Add(gifData[0]); gifData.RemoveAt(0); while (gifData[0] != 0x00) { int countOfFollowingDataBytes = gifData[0]; for (int i = 0; i <= countOfFollowingDataBytes; i++) { m_ImageData.Add(gifData[i]); } gifData.RemoveRange(0, countOfFollowingDataBytes + 1); } m_ImageData.Add(gifData[0]); gifData.RemoveAt(0); } private void ThrowAwayExtensionBlock(List<byte> gifData) { gifData.RemoveRange(0,2); // Delete ExtensionBlockIndicator and ExtensionDetermination while (gifData[0] != 0) { gifData.RemoveRange(0, gifData[0] + 1); } gifData.RemoveAt(0); } public enum GIFVersion { GIF87a, GIF89a } enum GIFBlockType { ImageDescriptor = 0x2C, Extension = 0x21, Trailer = 0x3B } } }
GifCreator.cs
using System; using System.Collections.Generic; using System.IO; using System.Text; namespace GifCreator { public class GifCreator { public static void CreateAnimatedGif(List<string> gifFiles, int delay, string outputFile) { BinaryWriter writer = new BinaryWriter(new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite)); byte[] gif_Signature = new byte[] { (byte)'G', (byte)'I', (byte)'F', (byte)'8', (byte)'9', (byte)'a' }; writer.Write(gif_Signature); for (int i = 0; i < gifFiles.Count; i++) { GifClass gif = new GifClass(); gif.LoadGifPicture(gifFiles[i]); if (i == 0) writer.Write(gif.m_ScreenDescriptor.ToArray()); writer.Write(GifCreator.CreateGraphicControlExtensionBlock(delay)); writer.Write(gif.m_ImageDescriptor.ToArray()); writer.Write(gif.m_ColorTable.ToArray()); writer.Write(gif.m_ImageData.ToArray()); } writer.Write(GifCreator.CreateLoopBlock()); writer.Write((byte)0x3B); //End file writer.Close(); } public static byte[] CreateGraphicControlExtensionBlock(int delay) { byte[] result = new byte[8]; // Split the delay into high- and lowbyte byte d1 = (byte)(delay % 256); byte d2 = (byte)(delay / 256); result[0] = (byte)0x21; // Start ExtensionBlock result[1] = (byte)0xF9; // GraphicControlExtension result[2] = (byte)0x04; // Size of DataBlock (4) result[3] = (byte)0x00; result[4] = d1; result[5] = d2; result[6] = (byte)0x00; result[7] = (byte)0x00; return result; } public static byte[] CreateLoopBlock() { return CreateLoopBlock(0); } public static byte[] CreateLoopBlock(int numberOfRepeatings) { byte rep1 = (byte)(numberOfRepeatings % 256); byte rep2 = (byte)(numberOfRepeatings / 256); byte[] result = new byte[19]; result[0] = (byte)0x21; // Start ExtensionBlock result[1] = (byte)0xFF; // ApplicationExtension result[2] = (byte)0x0B; // Size of DataBlock (11) for NETSCAPE2.0) result[3] = (byte)'N'; result[4] = (byte)'E'; result[5] = (byte)'T'; result[6] = (byte)'S'; result[7] = (byte)'C'; result[8] = (byte)'A'; result[9] = (byte)'P'; result[10] = (byte)'E'; result[11] = (byte)'2'; result[12] = (byte)'.'; result[13] = (byte)'0'; result[14] = (byte)0x03; // Size of Loop Block result[15] = (byte)0x01; // Loop Indicator result[16] = (byte)rep1; // Number of repetitions result[17] = (byte)rep2; // 0 for endless loop result[18] = (byte)0x00; return result; } public enum GIFVersion { GIF87a, GIF89a } enum GIFBlockType { ImageDescriptor = 0x2C, Extension = 0x21, Trailer = 0x3B } } }
Program.cs
using System; using System.Collections.Generic; using System.Text; using System.IO; using GifCreator; using System.Drawing; namespace gifmaker { class Program { static void Main(string[] args) { List<string> files = new List<string>(new string[] { "1.jpg", "2.jpg" }); GifCreator.GifCreator.CreateAnimatedGif(files, 5, "out.gif"); } } }