まえがき
この前作った C#でgifアニメを作る - ルパン三世のタイトルコール風は、「繰り返せない」という問題があったので、改善してみました。
例によってWindows7以降1であればインストール不要で使えます。
タグに「Qiita」を入れているのは、Qiita投稿に活用できそうなので入れてみました。
こんな感じ
参考サイト
ソースコード - 各画像の生成
ソースコード - gifアニメファイルへの変換
gif生成部分(MyGifEncorderクラスの中身)は参考サイト(dobon.net)から拝借しました。
(そのままコピペするのもアレなので、少しだけいじってます。)
usingSystem;usingSystem.Collections.Generic;usingSystem.Drawing;usingSystem.Drawing.Imaging;usingSystem.IO;publicstaticclassMyGifEncorder{publicclassBitmapAndDelayTime{publicBitmapbitmap;publicushortdelayTime;publicBitmapAndDelayTime(Bitmap_bitmap,ushort_delayTime){bitmap=_bitmap;delayTime=_delayTime;}}publicstaticvoidSaveAnimatedGif(stringfileName,List<BitmapAndDelayTime>baseImages,ushortloopCount){if(baseImages.Count==0){return;}//書き込み先のファイルを開くvarwriterFs=newFileStream(fileName,FileMode.Create,FileAccess.Write,FileShare.None);//BinaryWriterで書き込むvarwriter=newBinaryWriter(writerFs);varms=newMemoryStream();try{boolhasGlobalColorTable=false;intcolorTableSize=0;intimagesCount=baseImages.Count;for(inti=0;i<imagesCount;i++){//画像をGIFに変換して、MemoryStreamに入れるBitmapbmp=baseImages[i].bitmap;bmp.Save(ms,ImageFormat.Gif);ms.Position=0;if(i==0){writer.Write(ReadBytes(ms,6));//ヘッダを書き込む // "GIF89a" のはず// http://www.tohoho-web.com/wwwgif.htm#GIFHeader//Logical Screen Descriptorbyte[]screenDescriptor=ReadBytes(ms,7);//Global Color Tableがあるか確認if((screenDescriptor[4]&0x80)!=0){//Color Tableのサイズを取得colorTableSize=screenDescriptor[4]&0x07;hasGlobalColorTable=true;}else{hasGlobalColorTable=false;}//Global Color Tableを使わない//広域配色表フラグと広域配色表の寸法を消すscreenDescriptor[4]&=0x78;writer.Write(screenDescriptor);//Application Extensionwriter.Write(GetApplicationExtension(loopCount));}else{//HeaderとLogical Screen Descriptorをスキップms.Position+=6+7;}byte[]colorTable=null;if(hasGlobalColorTable){//Color Tableを取得colorTable=ReadBytes(ms,(1<<(colorTableSize+1))*3);}//Graphics Control Extensionwriter.Write(GetGraphicControlExtension(baseImages[i].delayTime));{byte[]tmp=PeekBytes(ms,2);if(tmp[0]==0x21&&tmp[1]==0xF9){//基のGraphics Control Extensionをスキップms.Position+=8;}}//Image Descriptorbyte[]imageDescriptor=ReadBytes(ms,10);if(imageDescriptor[0]!=0x2C){// Image SeparatorthrownewException("Unexpected format.");}if(!hasGlobalColorTable){//Local Color Tableを持っているか確認if((imageDescriptor[9]&0x80)==0){thrownewException("Not found local color table.");// not support}colorTableSize=imageDescriptor[9]&0x07;//Color Tableのサイズを取得//Color Tableを取得colorTable=ReadBytes(ms,(1<<(colorTableSize+1))*3);}//狭域配色表フラグ (Local Color Table Flag) と狭域配色表の寸法を追加imageDescriptor[9]|=(byte)(0x80|colorTableSize);writer.Write(imageDescriptor);writer.Write(colorTable);//Local Color Tableを書き込む//Image Dataを書き込む (終了部は書き込まない)writer.Write(ReadBytes(ms,(int)(ms.Length-ms.Position-1)));if(i==imagesCount-1){writer.Write((byte)0x3B);//終了部 (Trailer)}ms.SetLength(0);//MemoryStreamをリセット}}finally{ms.Close();writer.Close();writerFs.Close();}}privatestaticbyte[]ReadBytes(MemoryStreamms,intcount){byte[]bs=newbyte[count];intn=ms.Read(bs,0,count);if(n<count){thrownewException("ReadBytes failed.");}returnbs;}privatestaticbyte[]PeekBytes(MemoryStreamms,intcount){byte[]bs=newbyte[count];longpos=ms.Position;intn=ms.Read(bs,0,count);ms.Position=pos;// positionを戻すif(n<count){thrownewException("PeekBytes failed.");}returnbs;}// loopCount: 繰り返し回数(0 = 無限)privatestaticbyte[]GetApplicationExtension(ushortloopCount){byte[]bs=newbyte[19]{0x21,// [0] 拡張導入符 (Extension Introducer)0xFF,// [1] アプリケーション拡張ラベル (Application Extension Label)0x0B,// [2] ブロック寸法 (Block Size)// [3..10] "NETSCAPE" アプリケーション識別名 (Application Identifier)0x4E,0x45,0x54,0x53,0x43,0x41,0x50,0x45,0x32,0x2E,0x30,// [11..13] "2.0" アプリケーション確証符号 (Application Authentication Code)0x03,// [14] データ副ブロック寸法 (Data Sub-block Size)0x01,// [15] 詰め込み欄 [ネットスケープ拡張コード (Netscape Extension Code)]0x00,0x00,// [16..17] ※以降の処理で代入 繰り返し回数 (Loop Count)0x00// [18] ブロック終了符 (Block Terminator)};bs[16]=(byte)(loopCount&0xFF);bs[17]=(byte)((loopCount>>8)&0xFF);returnbs;}// delayTime: 遅延時間 (単位:10ms)privatestaticbyte[]GetGraphicControlExtension(ushortdelayTime){byte[]bs=newbyte[8]{0x21,// [0] 拡張導入符 (Extension Introducer)0xF9,// [1] グラフィック制御ラベル (Graphic Control Label)0x04,// [2] ブロック寸法 (Block Size, Byte Size)0x00,// [3] 詰め込み欄 (Packed Field)// bit 0: 1=透過色指標を使う// bit 3-2: 消去方法: 1=そのまま残す 2=背景色でつぶす 3=直前の画像に戻す0x00,0x00,// [4..5] ※以降の処理で代入 遅延時間 (Delay Time)0x00,// [6] 透過色指標 (Transparency Index, Transparent Color Index)0x00// [7] ブロック終了符 (Block Terminator)};bs[4]=(byte)(delayTime&0xFF);bs[5]=(byte)((delayTime>>8)&0xFF);returnbs;}}classGifEncoderTest2{constintLoopCount=0;[STAThread]staticvoidMain(string[]args){if(args.Length!=1){Console.WriteLine("Argument error.");return;}ushortdelayTime=10;// unit:[10ms]ushortdelayTimeEndFrame=100;// unit:[10ms]constintMaxFrames=1000;constintFileNameDigits=3;stringprefix=args[0];varbmps=newList<MyGifEncorder.BitmapAndDelayTime>();for(inti=0;i<MaxFrames;i++){strings=prefix+i.ToString("D"+FileNameDigits.ToString())+".png";FileInfofi=newFileInfo(s);if(fi.Exists){Bitmaptmp=(Bitmap)Image.FromFile(fi.FullName);bmps.Add(newMyGifEncorder.BitmapAndDelayTime(tmp,delayTime));}else{if(i==0){Console.WriteLine("File \""+fi.FullName+"\" is not found.");return;}break;}}// 最後のdelayを長くするbmps[bmps.Count-1].delayTime=delayTimeEndFrame;// 生成MyGifEncorder.SaveAnimatedGif("out.gif",bmps,LoopCount);}}Windows10環境でしか試していないので、今回のプログラムがWindows7で動くかは未確認です。 ↩
