2013-07-27 19 views
7

Próbuję połączyć dwa różne pliki GIF w jeden plik.W języku C#, być może jest to błąd dla Image.SaveAdd, kto może mi pomóc go rozwiązać?

Po pierwsze, nauczyłem się dużo o formacie gif. I wiem, że wartość opóźnienia jest ustawiana w rozszerzeniu graficznym, które jest blokiem pliku gif.

Uratowałem pierwszy gif i ustawić wartość FrameDelay, kod jak poniżej:

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); 

Potem próbowałem dodać kolejny obraz jako drugiej ramce.

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); 

Wreszcie, powinienem zakończyć to zdjęcie.

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

I stwierdziliśmy, że czas opóźnienia drugiej ramki jest zawsze 0.

Próbowałam wiele metod, ale nie mam pojęcia, aby to samo 0x96.

Co jest nie tak z tym?

+2

Po prostu nie jest obsługiwany przez koder GIF wbudowany w GDI +. Nie możesz tego zrobić. –

+0

naprawdę? Czy to prawda?? –

+0

OK, głupie pytanie, ale dla pewności: Mówisz o animowanych GIF-ach, prawda? – RenniePet

Odpowiedz

15

To po prostu nie jest obsługiwany przez dowolny koderów .NET graficznych. Ani przez GDI +, ani przez WIC, bazowe natywne kodeki dla klas System.Drawing.Bitmap i System.Windows.Media.Imaging.PngBitmapEncoder.

Chociaż brzmi to jak bardzo dziwne niedopatrzenie, najprawdopodobniej powodem było to, że GIF był obciążony patentem na oprogramowanie. Unisys posiadał prawa do algorytmu kompresji LZW i rozpoczął aggressively pursuing uzyskiwania opłat licencyjnych za to. Rozpoczynając od najbardziej oczywistych celów, w których można uzyskać większość pieniędzy, Microsoft jest zawsze na szczycie listy. Nie były też skromne, niekomercyjna lub prywatna strona internetowa, która używała GIF-ów na swoich stronach internetowych, musiała wykasować pięć tysięcy dolarów w 1999.

To zabiło format obrazu. Wszechobecny przed tym, prawie każdy przestał z nich korzystać. Oszałamiająco szybko, to tylko zajęło kilka miesięcy. Szczęśliwie zbiegając się z tym, że wszyscy mają już mnóstwo animowanych gifów, przedtem było to znacznie przesadzone. Możesz znaleźć kilka stron internetowych z powrotem - potem na komputerze zwrotnym, gdzie wszystko, co robił w kąciku oka. To nie jedyny szczęśliwy zbieg okoliczności, to główny powód opracowania formatu PNG o otwartym kodzie źródłowym. Dziękujemy naszym szczęśliwym gwiazdkom :)

Patent wygasł około 2004 roku, w zależności od tego, gdzie mieszkasz, więc nie będziesz już musiał się bać listu od Unisys.

Krótko mówiąc, będziesz musiał rozejrzeć się po innej bibliotece, aby dodać tę funkcję do swojego programu. Jest dobrze pokryty tym existing SO question, nie ma potrzeby powtarzania go tutaj.

+3

+1 Cóż za interesująca i wnikliwa odpowiedź Hansa. Dzięki za udostępnienie! –

1

Aktualizacja:

zrobiłem więcej badań i myślę, że te ffmpeg i zalecenia mplayer są warte wypróbowania:
Create animated gif from a set of jpeg images

Aktualizacja 2:

Ten kod z Rick van den Bosch jest bardzo dobrze, ponieważ daje dostęp do czasów opóźnień:

.Net (co najmniej 1,1, mogą one być włączone w wersji 2.0) nie daje możliwości tworzenia animowanych plików GIF za pośrednictwem GDI +.

//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(); 

Hans wymienione produkty nie są obsługiwane, więc ten trzeci roztwór jest propozycja RenniePet do wyodrębnienia ramki z obu GIF, a następnie połączyć ze sobą wszystkie klatki.

Dodaj odniesienie do System.Drawing.DLL i użyć tego kodu, aby uzyskać Ramki:

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 
    } 
} 

Możemy wyodrębnić wszystkie klatki tak:

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; 
} 

Następnie łączymy wszystkie klatki do jednego animowanego gif użyciu NGif

Pobierz kod, otwórz rozwiązanie i skompiluj projekt Komponenty, aby uzyskać bibliotekę DLL Gif.Components.dll i odwołaj się do tej biblioteki DLL w swoim rozwiązaniu.

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

Jeśli chcesz korzystać z biblioteki innej firmy, możesz użyć numeru Magick.NET. To jest opakowanie C# dla ImageMagick.

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"); 
} 
Powiązane problemy