2013-07-21 80 views
7

Recentemente, stavo cercando di rispondere ad un altro SO question sul caricamento dei frame (Bitmap e durata) di GIFs animato. Il codice può essere trovato su pastenbin.MonoMac System.Drawing.Image.GetPropertyItem (0x5100)

Nel fare ulteriori test su questo codice prima di spostarlo nella mia libreria di dev, ho notato che c'è un problema con questa riga di codice:

//Get the times stored in the gif 
//PropertyTagFrameDelay ((PROPID) 0x5100) comes from gdiplusimaging.h 
//More info on http://msdn.microsoft.com/en-us/library/windows/desktop/ms534416(v=vs.85).aspx 
var times = img.GetPropertyItem(0x5100).Value; 

Quando si esegue questo su Windows .Net utilizzando questo (example GIF), la matrice ha le stesse dimensioni della quantità di fotogrammi nell'animazione GIF e piena della durata dei fotogrammi. In questo caso un byte [20] che si converte (BitConverter.ToInt32()) durate:

[75,0,0,0,125,0,0,0,125,0,0,0,125,0,0,0,250,0,0,0] 

In MonoMac tuttavia, questa riga di codice per lo stesso esempio GIF restituisce un byte[4] che converte solo una durata (la prima):

[75,0,0,0] 

ho testato questo per 10 diversi GIF's e il risultato è sempre lo stesso. In Windows tutte le durate sono nel byte [], mentre MonoMac elenca solo il primo Durata:

[x,0,0,0] 
[75,0,0,0] 
[50,0,0,0] 
[125,0,0,0] 

Guardando il Mono System.Drawing.Imagesource code, la lunghezza sembrano essere impostato in questo metodo, che è un wrapper GDI:

status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid,out propSize); 

Tuttavia, non vedo alcun problema, non con la fonte come con la mia implementazione. Mi manca qualcosa o si tratta di un bug?

+0

Credo che troverete la risposta nell'implementazione Mono GDI Plus. Ho dato un'occhiata a questo, ma non ho la competenza sul codec gif per decifrare cosa sta succedendo. Di seguito è riportato un po 'di ciò che ho trovato. [Image.FromFile] (https://github.com/mono/mono/blob/master/mcs/class/System.Drawing/System.Drawing/Image.cs) chiama in [libgdiplus] (https: // github. com/mono/libgdiplus/albero/master/src). All'interno di libgdiplus ci sono funzioni per caricare le immagini. La funzione 'gdip_load_gif_image' all'interno del file [gifcodec.c] (https://github.com/mono/libgdiplus/blob/master/src/gifcodec.c) carica le immagini gif. –

+0

Dovrai esaminare cosa succede all'interno di 'gdip_load_gif_image'. Come ho detto, è qui che l'immagine viene caricata/decodificata e dove indovinerei il bug. Non ho la competenza GIF per capire cosa sta succedendo. In bocca al lupo. –

risposta

1

Se look into libgdiplus vedrete che le proprietà sono lette dalla bitmap attiva:

if (gdip_bitmapdata_property_find_id(image->active_bitmap, propID, &index) != Ok) { 

È possibile impostare l'immagine bitmap attiva chiamando Image.SelectActiveFrame e quindi mono torneranno le durate corrette, uno per uno. Dal momento che questa è un'incompatibilità con Windows, lo definirei un bug mono. Come soluzione semplice, puoi semplicemente controllare la lunghezza dell'array e gestire entrambi i casi. Questo sarà migliore di un controllo per mono, perché se mono viene risolto questo continuerà a funzionare.

+0

Funziona alla grande e grazie per il suggerimento di compatibilità cross-platform. – dsfgsho

2

Non vedo nulla di sbagliato nella sorgente mono. Sarebbe stato utile se avessi postato una delle immagini campione che hai provato. Una stranezza riguardo al formato dell'immagine GIF è che il blocco dell'estensione di controllo grafico che contiene il frame time è opzionale e può essere omesso prima di un descrittore di immagine. Probabilità diverse da zero quindi che si hanno file GIF che hanno solo un GCE che si applica a tutti i frame, si suppone che si applichi lo stesso frame time a ogni frame.

Si noti che non si sono ottenuti 4 valori, il tempo di frame è codificato come valore a 32 bit e si sta visualizzando la codifica little endian per esso in un byte []. Dovresti usare BitConverter.ToInt32(), come hai fatto correttamente nel codice di esempio.

Ritengo pertanto probabilmente si dovrebbe usare questo invece:

//convert 4 bit value to integer 
var duration = BitConverter.ToInt32(times, 4*i % times.Length); 

notano che c'è un altro dettaglio di implementazione brutto sui frame GIF, incornicia # 2 e fino non deve essere la stessa dimensione del telaio # 1. Ogni frame ha un campo di metadati che descrive cosa dovrebbe essere fatto con il frame precedente per unirlo a quello successivo. Non ci sono ID di proprietà che io conosca per ottenere l'offset del frame, la dimensione e il metodo undraw per ogni frame. Penso che sia necessario rendere ogni fotogramma in una bitmap per ottenere una sequenza corretta di immagini. Dettagli molto brutti, GIF deve morire.

+0

Nasty dettagli davvero. Questo è [uno dei GIFS] (http://upload.wikimedia.org/wikipedia/commons/5/50/Triple-Spiral-Labyrinth-animated.gif) Ho provato che funziona su Windows ma non su MonoMac con il [ codice su pastebin] (http://pastebin.com/Y6iUGDX9). Per questo GIF, la variabile 'times' è ' byte [20] = {75,0,0,0,125,0,0,0,125,0,0,0,125,0,0,050,0,0,0,0} 'su Windows ma ' byte [4] = {75,0,0,0}} su MonoMac. Come accennato in precedenza, tutte le GIF ho provato a lavorare su Windows quindi immagino che questa non sia una delle stranezze della GIF ma piuttosto un problema con Mono? – dsfgsho

+0

Sì, non è possibile spazzarlo sotto un tappetino. –