2013-07-27 39 views
7

我想將兩個不同的gif文件合併成一個文件。在C#中,也許這是Image.SaveAdd的一個bug,誰可以幫我解決它?

首先,我學到了很多關於gif格式的知識。我知道延遲時間值是在Graphics Control Extension中設置的,它是一個gif文件塊。

我保存的所述第一GIF和設置FrameDelay值,代碼如下:

ImageCodecInfo codeInfo = GetEncoder(ImageFormat.Gif); 
    System.Drawing.Imaging.Encoder saveEncoder = System.Drawing.Imaging.Encoder.SaveFlag; 
    EncoderParameters parameters = new EncoderParameters(1); 

    parameters.Param[0] = new EncoderParameter(saveEncoder, (long)EncoderValue.MultiFrame); 
    PropertyItem PropertyTagFrameDelay = img1.GetPropertyItem(0x5100); 
    PropertyTagFrameDelay.Value = new byte[] { 0x96, 0x00 };// this is the delay value 0x0096, means 1.5 second 
    img1.SetPropertyItem(PropertyTagFrameDelay); 

    PropertyItem LoopCount = img1.GetPropertyItem(0x5101); 
    LoopCount.Value = new byte[] { 0x00, 0x00 };// this means the gif loops endlessly 
    img1.SetPropertyItem(LoopCount); 

    img1.Save(@"c:\ddd.gif", codeInfo, parameters); 

然後我試圖增加另一個圖像作爲第二幀。

parameters = new EncoderParameters(1); 
    parameters.Param[0] = new EncoderParameter(saveEncoder, (long)EncoderValue.FrameDimensionTime); 
    PropertyTagFrameDelay = img2.GetPropertyItem(0x5100); 
    PropertyTagFrameDelay.Value = new byte[] { 0x96, 0x00 };// this is the delay value 0x0096, means 1.5 second 
    img2.SetPropertyItem(PropertyTagFrameDelay); 

最後,我應該終止這張圖片。

parameters = new EncoderParameters(1); 
    parameters.Param[0] = new EncoderParameter(saveEncoder, (long)EncoderValue.Flush); 
    img1.SaveAdd(parameters); 

而且我發現第二幀的延遲時間始終爲0

我嘗試過很多方法,但我不知道,使其爲0x96。

那麼它有什麼問題?

+2

它只是不被內置在GDI +中的gif編碼器支持。你無法完成這項工作。 –

+0

真的嗎?這是真的嗎? –

+0

好,愚蠢的問題,但只是要確定:你在談論動畫GIF,對吧? – RenniePet

回答

15

這根本不是由的.NET圖像編碼器的任何支持。 GDI +和WIC都沒有提供System.Drawing.BitmapSystem.Windows.Media.Imaging.PngBitmapEncoder類的基礎本機編解碼器。

雖然這聽起來像是一個非常奇怪的疏忽,但最可能的原因是GIF受到了軟件專利的阻礙。 Unisys擁有LZW壓縮算法的權利,並開始爲其獲得許可費用aggressively pursuing。從可以獲得最多資金的最明顯目標開始,微軟永遠位居榜首。他們不謙虛要麼,即使用在其網頁上的GIF非商業或私人網站不得不咳嗽了美元在1999年

這殺死了圖像格式。在此之前無處不在,幾乎每個人都停止使用它們。驚人的迅速,這隻需要幾個個月。很高興與每個人完全一樣,他們充滿了動畫gtws順便說一句,這是嚴重過頭。你可能會發現一些網頁從後面,然後在backback機器所有在眼角正在移動。不是唯一幸運的巧合,它是正在開發的開源PNG格式的核心原因。感謝我們幸運的明星:)

專利在2004年左右過期,取決於你住在哪裏,所以你不必擔心Unisys的來信。長話短說,你必須購買另一個圖書館才能將此功能添加到你的程序中。這是由existing SO question覆蓋,無需在這裏重複。

+3

+1多麼有趣而有見地的答案Hans。感謝分享! –

1

更新:

我做更多的研究,我想這些的ffmpeg和mplayer的建議是值得一試:
Create animated gif from a set of jpeg images

更新2:

這從Rick van den Bosch代碼是非常也可以,因爲它可以讓您訪問延遲時間:

.net(至少1.1,他們可能將它合併到2.0中)不會給你 通過GDI +創建動畫GIF的可能性。

//Variable declaration 
StringCollection stringCollection; 
MemoryStream memoryStream; 
BinaryWriter binaryWriter; 
Image image; 
Byte[] buf1; 
Byte[] buf2; 
Byte[] buf3; 
//Variable declaration 

stringCollection = a_StringCollection_containing_images; 

Response.ContentType = "Image/gif"; 
memoryStream = new MemoryStream(); 
buf2 = new Byte[19]; 
buf3 = new Byte[8]; 
buf2[0] = 33; //extension introducer 
buf2[1] = 255; //application extension 
buf2[2] = 11; //size of block 
buf2[3] = 78; //N 
buf2[4] = 69; //E 
buf2[5] = 84; //T 
buf2[6] = 83; //S 
buf2[7] = 67; //C 
buf2[8] = 65; //A 
buf2[9] = 80; //P 
buf2[10] = 69; //E 
buf2[11] = 50; //2 
buf2[12] = 46; //. 
buf2[13] = 48; //0 
buf2[14] = 3; //Size of block 
buf2[15] = 1; // 
buf2[16] = 0; // 
buf2[17] = 0; // 
buf2[18] = 0; //Block terminator 
buf3[0] = 33; //Extension introducer 
buf3[1] = 249; //Graphic control extension 
buf3[2] = 4; //Size of block 
buf3[3] = 9; //Flags: reserved, disposal method, user input, transparent color 
buf3[4] = 10; //Delay time low byte 
buf3[5] = 3; //Delay time high byte 
buf3[6] = 255; //Transparent color index 
buf3[7] = 0; //Block terminator 
binaryWriter = new BinaryWriter(Response.OutputStream); 
for (int picCount = 0; picCount < stringCollection.Count; picCount++) 
{ 
    image = Bitmap.FromFile(stringCollection[picCount]); 
    image.Save(memoryStream, ImageFormat.Gif); 
    buf1 = memoryStream.ToArray(); 

    if (picCount == 0) 
    { 
     //only write these the first time.... 
     binaryWriter.Write(buf1, 0, 781); //Header & global color table 
     binaryWriter.Write(buf2, 0, 19); //Application extension 
    } 

    binaryWriter.Write(buf3, 0, 8); //Graphic extension 
    binaryWriter.Write(buf1, 789, buf1.Length - 790); //Image data 

    if (picCount == stringCollection.Count - 1) 
    { 
     //only write this one the last time.... 
     binaryWriter.Write(";"); //Image terminator 
    } 

    memoryStream.SetLength(0); 
} 
binaryWriter.Close(); 
Response.End(); 

正如漢斯提到其不支持,所以這第三個解決方案是RenniePet的建議提取來自gif文件的幀,然後所有的幀結合在一起。

添加對System.Drawing的引用。DLL並使用此代碼來獲取幀:

using System.Drawing; 
using System.Drawing.Imaging; 

public class GifImage 
{ 
    private Image gifImage; 
    private FrameDimension dimension; 
    private int frameCount; 
    private int currentFrame = -1; 
    private bool reverse; 
    private int step = 1; 

    public GifImage(string path) 
    { 
     gifImage = Image.FromFile(path); 
     //initialize 
     dimension = new FrameDimension(gifImage.FrameDimensionsList[0]); 
     //gets the GUID 
     //total frames in the animation 
     frameCount = gifImage.GetFrameCount(dimension); 
    } 

    public int GetFrameCount() 
    { 
     return frameCount; 
    } 

    public bool ReverseAtEnd 
    { 
     //whether the gif should play backwards when it reaches the end 
     get { return reverse; } 
     set { reverse = value; } 
    } 

    public Image GetNextFrame() 
    { 

     currentFrame += step; 

     //if the animation reaches a boundary... 
     if (currentFrame >= frameCount || currentFrame < 1) 
     { 
      if (reverse) 
      { 
       step *= -1; 
       //...reverse the count 
       //apply it 
       currentFrame += step; 
      } 
      else 
      { 
       currentFrame = 0; 
       //...or start over 
      } 
     } 
     return GetFrame(currentFrame); 
    } 

    public Image GetFrame(int index) 
    { 
     gifImage.SelectActiveFrame(dimension, index); 
     //find the frame 
     return (Image)gifImage.Clone(); 
     //return a copy of it 
    } 
} 

我們可以提取所有的幀是這樣的:使用

private static readonly string tempFolder = @"C:\temp\"; 

static void Main(string[] args) 
{ 
    CombineGifs(@"c:\temp\a.gif", @"c:\temp\b.gif"); 
} 

public static void CombineGifs(string firstImageFilePath, string secondImageFilePath) 
{ 
    int frameCounter = ExtractGifFramesAndGetCount(firstImageFilePath, 0); 
    int secondframeCounter = ExtractGifFramesAndGetCount(secondImageFilePath, frameCounter); 

    string filePathOfCombinedGif = CombineFramesIntoGif(0, secondframeCounter); 
} 

private static int ExtractGifFramesAndGetCount(string filePath, int imageNameStartNumber) 
{ 
    ////NGif had an error when I tried it 
    //GifDecoder gifDecoder = new GifDecoder(); 
    //gifDecoder.Read(filePath); 

    //int frameCounter = imageNameStartNumber + gifDecoder.GetFrameCount(); 
    //for (int i = imageNameStartNumber; i < frameCounter; i++) 
    //{ 
    // Image frame = gifDecoder.GetFrame(i); // frame i 
    // frame.Save(tempFolder + i.ToString() + ".png", ImageFormat.Png); 
    //} 

    //So we'll use the Gifimage implementation 
    GifImage gifImage = new GifImage(filePath); 
    gifImage.ReverseAtEnd = false; 
    int frameCounter = imageNameStartNumber + gifImage.GetFrameCount(); 
    for (int i = imageNameStartNumber; i < frameCounter; i++) 
    { 
     Image img = gifImage.GetNextFrame(); 
     img.Save(tempFolder + i.ToString() + ".png"); 
    } 
    return frameCounter; 
} 

接下來,我們所有的幀合併成一個gif動畫NGif

下載代碼,打開解決方案並編譯Components項目以獲取DLL Gif.Components.dll並在您的解決方案中引用該DLL。

private static string CombineFramesIntoGif(int startFrameCount, int endFrameCount) 
{ 
    List<string> imageFilePaths = new List<string>(); 
    for (int i = startFrameCount; i < endFrameCount; i++) 
    { 
     imageFilePaths.Add(tempFolder + i.ToString() + ".png"); 
    } 

    string outputFilePath = tempFolder + "test.gif"; 
    AnimatedGifEncoder e = new AnimatedGifEncoder(); 
    e.Start(outputFilePath); 
    e.SetDelay(500); 
    //-1:no repeat,0:always repeat 
    e.SetRepeat(0); 
    for (int i = 0; i < imageFilePaths.Count; i++) 
    { 
     e.AddFrame(Image.FromFile(imageFilePaths[i])); 
    } 
    e.Finish(); 
    return outputFilePath; 
} 
1

如果您願意使用第三方庫,則可以使用Magick.NET。這是一個用於ImageMagick的C#封裝。

using (MagickImageCollection images = new MagickImageCollection()) 
{ 
    MagickImage firstFrame = new MagickImage("first.gif"); 
    firstFrame.AnimationDelay = 1500; 
    images.Add(firstFrame); 

    MagickImage secondFrame = new MagickImage("second.gif"); 
    secondFrame.AnimationDelay = 200; 
    images.Add(secondFrame); 

    // This method will try to make your output image smaller. 
    images.OptimizePlus(); 

    images.Write(@"c:\ddd.gif"); 
} 
相關問題