2012-11-13 22 views
6

Come si ottiene la durata (tempo di riproduzione) di un'animazione GIF?Modifica GIF in C#

Ecco il mio codice:

GifTime = PoczatkowyGIF.GetFrameCount(FrameDimension.Time); 
//GifTime is a double; PoczatkowyGIF is my gif image. 

GifTime non è esatto. La mia animazione è 3.8s, ma GifTime è 3.9s. Un'altra animazione che ho provato è stata di 0,88, ma GifTime era 0. Quindi, come ottengo l'animazione? Inoltre, come posso modificare la prima volta del fotogramma GIF e aggiungere una cornice con un tempo specifico alla GIF esistente?

+0

Perché sei preoccupato per una differenza di 0,01 secondi. Come hai calcolato la lunghezza dell'animazione? –

+0

Ho usato Easy GIF Animator per questo. Quando aggiungo le ore di ogni fotogramma (ho realizzato l'animazione in Photoshop) è uguale a 3.8. Ma ok, salta questa domanda e rispondi alla seconda ... Se il programma funzionerà correttamente, il mio codice per il conteggio delle durate era corretto. –

+0

Non vedo un'altra domanda a essere onesti. –

risposta

5

Nel seguente frammento di codice, è possibile vedere come calcolare la durata totale, accumulando la durata di ciascun fotogramma:

for (int f = 0; f < frameCount; f++) 
{ 
    this_delay = BitConverter.ToInt32(image.GetPropertyItem(20736).Value, index) * 10; 
    // Minimum delay is 100 ms 
    delay += (this_delay < 100 ? 100 : this_delay); 
    index += 4; 
} 

Nota che una durata telaio non può essere inferiore a 100 ms, e che può spiegare per le tue differenze di calcolo.

È possibile leggere this post sull'esame di un .GIF, dove è possibile trovare il resto del codice.

Inoltre, per modificare l'ultimo fotogramma di un'immagine animata, è possibile utilizzare

gifImage.SelectActiveFrame(dimension, first_frame_index); 

SelectActiveFrame vi permetterà di modificare l'immagine, alterando quella specificata (primo nel tuo caso) telaio.

Non sono completamente sicuro di questo, ma il framework non ha il supporto per l'aggiunta di frame, ho usato MagickNet per ricodificare una nuova GIF per questo scopo. Si consiglia inoltre di dare un'occhiata a NGif in alternativa.

1

Il seguente codice è una classe per creare, aprire o modificare Gif.

Non è possibile leggere il tempo di riproduzione da uno streaming, ma per la seconda parte della domanda, è possibile scrivere un'animazione Gif con ritardo specificato su ciascun fotogramma.

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Drawing; 
using System.Drawing.Imaging; 
using System.IO; 

/// <summary> 
/// Uses default .net GIF encoding and adds animation headers. 
/// </summary> 
public class Gif : IDisposable, IEnumerable<Image> 
{ 
    #region Header Constants 
    const byte FileTrailer = 0x3b, 
     ApplicationBlockSize = 0x0b, 
     GraphicControlExtensionBlockSize = 0x04; 

    const int ApplicationExtensionBlockIdentifier = 0xff21, 
     GraphicControlExtensionBlockIdentifier = 0xf921; 

    const long SourceGlobalColorInfoPosition = 10, 
     SourceGraphicControlExtensionPosition = 781, 
     SourceGraphicControlExtensionLength = 8, 
     SourceImageBlockPosition = 789, 
     SourceImageBlockHeaderLength = 11, 
     SourceColorBlockPosition = 13, 
     SourceColorBlockLength = 768; 

    const string ApplicationIdentification = "NETSCAPE2.0", 
     FileType = "GIF", 
     FileVersion = "89a"; 
    #endregion 

    class GifFrame 
    { 
     public GifFrame(Image image, double delay, int xOffset, int yOffset) 
     { 
      Image = image; 
      Delay = delay; 
      XOffset = xOffset; 
      YOffset = yOffset; 
     } 

     public Image Image; 
     public double Delay; 
     public int XOffset, YOffset; 
    } 

    List<GifFrame> Frames = new List<GifFrame>(); 

    public Gif() { DefaultFrameDelay = 500; } 

    public Gif(Stream InStream, int Repeat = 0, int Delay = 500) 
    { 
     using (Image Animation = Bitmap.FromStream(InStream)) 
     { 
      int Length = Animation.GetFrameCount(FrameDimension.Time); 

      DefaultFrameDelay = Delay; 
      this.Repeat = Repeat; 

      for (int i = 0; i < Length; ++i) 
      { 
       Animation.SelectActiveFrame(FrameDimension.Time, i); 

       var Frame = new Bitmap(Animation.Size.Width, Animation.Size.Height); 

       Graphics.FromImage(Frame).DrawImage(Animation, new Point(0, 0)); 

       Frames.Add(new GifFrame(Frame, Delay, 0, 0)); 
      } 
     } 
    } 

    #region Properties 
    public int DefaultWidth { get; set; } 

    public int DefaultHeight { get; set; } 

    public int Count { get { return Frames.Count; } } 

    /// <summary> 
    /// Default Delay in Milliseconds 
    /// </summary> 
    public int DefaultFrameDelay { get; set; } 

    public int Repeat { get; private set; } 
    #endregion 

    /// <summary> 
    /// Adds a frame to this animation. 
    /// </summary> 
    /// <param name="Image">The image to add</param> 
    /// <param name="XOffset">The positioning x offset this image should be displayed at.</param> 
    /// <param name="YOffset">The positioning y offset this image should be displayed at.</param> 
    public void AddFrame(Image Image, double? frameDelay = null, int XOffset = 0, int YOffset = 0) 
    { 
     Frames.Add(new GifFrame(Image, frameDelay ?? DefaultFrameDelay, XOffset, YOffset)); 
    } 

    public void AddFrame(string FilePath, double? frameDelay = null, int XOffset = 0, int YOffset = 0) 
    { 
     AddFrame(new Bitmap(FilePath), frameDelay, XOffset, YOffset); 
    } 

    public void RemoveAt(int Index) { Frames.RemoveAt(Index); } 

    public void Clear() { Frames.Clear(); } 

    public void Save(Stream OutStream) 
    { 
     using (var Writer = new BinaryWriter(OutStream)) 
     { 
      for (int i = 0; i < Count; ++i) 
      { 
       var Frame = Frames[i]; 

       using (var gifStream = new MemoryStream()) 
       { 
        Frame.Image.Save(gifStream, ImageFormat.Gif); 

        // Steal the global color table info 
        if (i == 0) InitHeader(gifStream, Writer, Frame.Image.Width, Frame.Image.Height); 

        WriteGraphicControlBlock(gifStream, Writer, Frame.Delay); 
        WriteImageBlock(gifStream, Writer, i != 0, Frame.XOffset, Frame.YOffset, Frame.Image.Width, Frame.Image.Height); 
       } 
      } 

      // Complete File 
      Writer.Write(FileTrailer); 
     } 
    } 

    #region Write 
    void InitHeader(Stream sourceGif, BinaryWriter Writer, int w, int h) 
    { 
     // File Header 
     Writer.Write(FileType.ToCharArray()); 
     Writer.Write(FileVersion.ToCharArray()); 

     Writer.Write((short)(DefaultWidth == 0 ? w : DefaultWidth)); // Initial Logical Width 
     Writer.Write((short)(DefaultHeight == 0 ? h : DefaultHeight)); // Initial Logical Height 

     sourceGif.Position = SourceGlobalColorInfoPosition; 
     Writer.Write((byte)sourceGif.ReadByte()); // Global Color Table Info 
     Writer.Write((byte)0); // Background Color Index 
     Writer.Write((byte)0); // Pixel aspect ratio 
     WriteColorTable(sourceGif, Writer); 

     // App Extension Header 
     unchecked { Writer.Write((short)ApplicationExtensionBlockIdentifier); }; 
     Writer.Write((byte)ApplicationBlockSize); 
     Writer.Write(ApplicationIdentification.ToCharArray()); 
     Writer.Write((byte)3); // Application block length 
     Writer.Write((byte)1); 
     Writer.Write((short)Repeat); // Repeat count for images. 
     Writer.Write((byte)0); // terminator 
    } 

    void WriteColorTable(Stream sourceGif, BinaryWriter Writer) 
    { 
     sourceGif.Position = SourceColorBlockPosition; // Locating the image color table 
     var colorTable = new byte[SourceColorBlockLength]; 
     sourceGif.Read(colorTable, 0, colorTable.Length); 
     Writer.Write(colorTable, 0, colorTable.Length); 
    } 

    void WriteGraphicControlBlock(Stream sourceGif, BinaryWriter Writer, double frameDelay) 
    { 
     sourceGif.Position = SourceGraphicControlExtensionPosition; // Locating the source GCE 
     var blockhead = new byte[SourceGraphicControlExtensionLength]; 
     sourceGif.Read(blockhead, 0, blockhead.Length); // Reading source GCE 

     unchecked { Writer.Write((short)GraphicControlExtensionBlockIdentifier); }; // Identifier 
     Writer.Write((byte)GraphicControlExtensionBlockSize); // Block Size 
     Writer.Write((byte)(blockhead[3] & 0xf7 | 0x08)); // Setting disposal flag 
     Writer.Write((short)(frameDelay/10)); // Setting frame delay 
     Writer.Write((byte)blockhead[6]); // Transparent color index 
     Writer.Write((byte)0); // Terminator 
    } 

    void WriteImageBlock(Stream sourceGif, BinaryWriter Writer, bool includeColorTable, int x, int y, int w, int h) 
    { 
     sourceGif.Position = SourceImageBlockPosition; // Locating the image block 
     var header = new byte[SourceImageBlockHeaderLength]; 
     sourceGif.Read(header, 0, header.Length); 
     Writer.Write((byte)header[0]); // Separator 
     Writer.Write((short)x); // Position X 
     Writer.Write((short)y); // Position Y 
     Writer.Write((short)w); // Width 
     Writer.Write((short)h); // Height 

     if (includeColorTable) // If first frame, use global color table - else use local 
     { 
      sourceGif.Position = SourceGlobalColorInfoPosition; 
      Writer.Write((byte)(sourceGif.ReadByte() & 0x3f | 0x80)); // Enabling local color table 
      WriteColorTable(sourceGif, Writer); 
     } 
     else Writer.Write((byte)(header[9] & 0x07 | 0x07)); // Disabling local color table 

     Writer.Write((byte)header[10]); // LZW Min Code Size 

     // Read/Write image data 
     sourceGif.Position = SourceImageBlockPosition + SourceImageBlockHeaderLength; 

     var dataLength = sourceGif.ReadByte(); 
     while (dataLength > 0) 
     { 
      var imgData = new byte[dataLength]; 
      sourceGif.Read(imgData, 0, dataLength); 

      Writer.Write((byte)dataLength); 
      Writer.Write(imgData, 0, dataLength); 
      dataLength = sourceGif.ReadByte(); 
     } 

     Writer.Write((byte)0); // Terminator 
    } 
    #endregion 

    public void Dispose() 
    { 
     Frames.Clear(); 
     Frames = null; 
    } 

    public Image this[int Index] { get { return Frames[Index].Image; } } 

    public IEnumerator<Image> GetEnumerator() { foreach (var Frame in Frames) yield return Frame.Image; } 

    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } 
}