Fonction GetGIFDelay()

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Mesa
Messages : 1092
Inscription : mer. 14/sept./2011 16:59

Fonction GetGIFDelay()

Message par Mesa »

Tous les GIF animés ont leur propre vitesse d'affichage, qui est bien caché dans le fichier.
Alors voici une fonction pour récupérer ce paramètre (quand c'est possible).

Code : Tout sélectionner

;http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
;http://giflib.sourceforge.net/gif89.txt

;EnableExplicit
Procedure GetGIFDelay(File$)
  
  Protected HeaderGIF89a$ = "GIF89a" 
  Protected HeaderGIF87a$ = "GIF87a"
  
  Protected*HeaderGIF89a = Ascii("GIF89a")
  Protected*HeaderGIF87a = Ascii("GIF87a")
  
  
  Protected Packed_Fields.b
  Protected Header${6}=""  
  
  Protected FileID, i, n,Size_of_Global_Color_Table, Delay_Time.u 
  Protected temp, tempb.a, tempb2.a, tempb3.a, tempb4.a
  
  Macro GetBit(Value, bit) 
    (Value&(1<<bit))>>bit  
  EndMacro 
  
  ;- Main
  ;- Open GIF
  FileID=ReadFile(#PB_Any, File$ )
  
  If FileID = 0
    MessageRequester("Attention", "The file is corrupted")
    End
  EndIf
  
  
  ;- Verify integrity by reading the last byte which must be "3B"
  FileSeek(FileID, Lof(FileID) - 1)
  If ReadByte(FileID) <> $3B
    ;Debug "OOPS, the file is corrupted !"
    MessageRequester("Attention", "The file is corrupted")
    End
  EndIf
  FileSeek(FileID, 0)
  
  
  ;- Read header
  For i=0 To 5
    PokeB(@header$+i,ReadByte(FileID))
  Next i
  
  If CompareMemoryString(*HeaderGIF89a, @Header$, #PB_String_CaseSensitive , 6, #PB_Ascii) = #PB_String_Equal  
    Header$=HeaderGIF89a$
  ElseIf CompareMemoryString(*HeaderGIF87a, @Header$, #PB_String_CaseSensitive , 6, #PB_Ascii) = #PB_String_Equal  
    Header$=HeaderGIF87a$
  Else
    ;Debug "Not a GIF (or header not find)"
    MessageRequester("Attention", "Not a GIF (or header not find)")
    End  
  EndIf
  
  ;- Try to get the delay time...
  FileSeek(FileID, 4, #PB_Relative)
  Packed_Fields = ReadByte(FileID) 
  FileSeek(FileID, 2, #PB_Relative)
  temp=GetBit(Packed_Fields, 2)*4+GetBit(Packed_Fields, 1)*2+GetBit(Packed_Fields, 0)
  Size_of_Global_Color_Table = 3*Int(Pow(2.0, temp+1))
  FileSeek(FileID, Size_of_Global_Color_Table, #PB_Relative)
  
  n=0
  Repeat 
    n=n+1
    If n=4
      Break ; To avoid a infinite loop
    EndIf
    
    tempb=ReadByte(FileID)
    If tempb = $21
      
      tempb2=ReadByte(FileID)
      
      ;Application Extension
      If tempb2 = $FF
        ;Debug "It is the optionnal Application Extension with a text, NETSCAPE..." 
        FileSeek(FileID, 17, #PB_Relative) 
      EndIf
      
      ;Plain Text Extension
      If tempb2 = $01
        ;Debug "It is the Plain Text Extension" 
        Repeat
          tempb3=ReadByte(FileID)
          FileSeek(FileID, tempb3, #PB_Relative) 
        Until tempb3 =0
      EndIf
      
      ;Comment Extension
      If tempb2 = $FE
        ;Debug "It is the Comment  Extension" 
        Repeat
          tempb4=ReadByte(FileID)
          FileSeek(FileID, tempb4, #PB_Relative) 
        Until tempb4 =0
      EndIf
    EndIf 
  Until tempb2 = $F9
  
  If tempb2 =$F9
    ;Optionnal Graphics Control Extension
    ;Debug "Hi Ha !, we've got an Optionnal Graphics Control Extension" 
    FileSeek(FileID, 2, #PB_Relative)
    Delay_Time = ReadUnicodeCharacter(FileID)
    
    ;Debug  Str(Delay_Time*10) + " ms"
  Else
    ;Debug " Sorry, delay was not found"
    Delay_Time = 20
  EndIf
  
  
  ;Debug "ok"
  CloseFile(FileID)
  ProcedureReturn Delay_Time*10 ; in millisecondes
EndProcedure


;****************** EXAMPLE ********************
UseGIFImageDecoder()

; Loading a GIF file
GIFFile$ = #PB_Compiler_Home + "Examples/Sources/Data/PureBasicLogo.gif"

If LoadImage(0, GIFFile$)
  GIFDelay = GetGIFDelay(GIFFile$)
  ;Debug GIFDelay
  
  OpenWindow(0, 100, 100, ImageWidth(0), ImageHeight(0), "GIF viewer: Delay=" + Str(GIFDelay) + "ms")
  
  ImageGadget(0, 0, 0, ImageWidth(0), ImageHeight(0),ImageID(0))
  
  AddWindowTimer(0, 1, GIFDelay) ; A Timer to animate the GIF
  
  Repeat
    Event = WaitWindowEvent()
    
    If Event = #PB_Event_Timer
      SetImageFrame(0, Frame)
      
      Frame+1
      If Frame >= ImageFrameCount(0) : Frame = 0 : EndIf
      SetGadgetState(0, ImageID(0))
      
    EndIf
    
  Until Event = #PB_Event_CloseWindow
Else
  Debug "Impossible to load the file: " + GIFFile$
EndIf

Et pour ceux qui voudrait voir le début d'un décompilateur de GIF (à finir), c'est là:

Code : Tout sélectionner

;http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
;http://giflib.sourceforge.net/gif89.txt



;Header 6 bytes= signature 3 bytes + version 3 bytes :
; GIF89a $47 $49 $46 $38 $39 $61 // Or GIF87a $47 $49 $46 $38 $37 $61
HeaderGIF89a$ = "GIF89a" 
HeaderGIF87a$ = "GIF87a"
;ShowMemoryViewer(@HeaderGIF89a$, 13)
*HeaderGIF89a = Ascii("GIF89a")
*HeaderGIF87a = Ascii("GIF87a")
;ShowMemoryViewer(*HeaderGIF89a, 13)
;====================================================================================

;Logical Screen Descriptor 7 bytes
Logical_Screen_Width.u;         Unsigned
Logical_Screen_Height.u;        Unsigned
Packed_Fields.b        ;        Byte
Background_Color_Index.b ;      Byte ;The next byte gives us the background color index. This byte is only meaningful if the global color table flag is 1, and if there is no global color table, this byte should be 0.
Pixel_Aspect_Ratio.b     ;      Byte ;The last byte of the logical screen descriptor is the pixel aspect ratio. Modern viewers don't use this. Until 5.0, GIFLIB ignored this flag on input and zeroed it on output; now it is read and preserved if present. The GIF standard doesn't give a rationale for it, but it seems likely that the designers intended it for representing image captures from the analog television of the day, which had rectangular pixel-equivalents. The GIF specification says that if there was a value specified in this byte, N, the actual ratio used would be (N + 15) / 64 for all N<>0.


;  <Packed_Fields> = Global Color Table Flag       1 Bit
;                    Color Resolution              3 Bits
;                    Sort Flag                     1 Bit
;                    Size of Global Color Table    3 Bits

;The first (most-significant) bit is the global color table flag. If it's 0, then there is no global color table. If it's 1, then a global color table will follow. In our sample image, we can see that we will have a global color table (as will usually be the case).
;The Next three bits are the color resolution. They are only meaningful If there is a Global color table, And allow you To compute its size. If the value of this filed is N, the number of entries IN the Global color table will be 2 ^ (N+1) - that is, two raised To the power (N+1). Thus, the 001 IN the sample image represents 2 bits/pixel; 111 would represent 8 bits/pixel.
;The next single bit is the sort flag. If the values is 1, then the colors in the global color table are sorted in order of "decreasing importance," which typically means "decreasing frequency" in the image. This can help the image decoder, but is not required. In the sample file this value has been left at 0.
;Size of Global Color Table - If the Global Color Table Flag is set To 1, the value IN this field is used To calculate the number
;             of bytes contained in the Global Color Table. To determine that
;             actual size of the color table, raise 2 to [the value of the field
;             + 1].  Even If there is no Global Color Table specified, set this
;             field according To the above formula so that decoders can choose
;             the best graphics mode To display the stream IN.  (This field is
;             made up of the 3 least significant bits of the byte.)
;====================================================================================

;Global Color Table : The length of the global color table is 2^(N+1) entries where N is the value of the color depth field in the logical screen descriptor. The table will take up 3*2^(N+1) bytes in the stream.



; Macro BitGet (Value, Bit) ; from 1 to 8, 16, 32, 64
; 		Bool(Value & (1 << (Bit - 1))) 
; 	EndMacro
Macro GetBit(Value, bit) 
  (Value&(1<<bit))>>bit  ;Translates as 'value' ANDed with 2^bit and shifted back to bitposition 0 
EndMacro 

;- Main
;- Open GIF
File$=#PB_Compiler_Home+"Examples/Sources/Data/PureBasicLogo.gif";
FileID=ReadFile(#PB_Any, File$ )


;- Verify integrity by reading the last byte which must be "3B"
FileSeek(FileID, Lof(FileID) - 1)
If ReadByte(FileID) <> $3B
  Debug "OOPS, the file is corrupted !"
  ;End
EndIf
FileSeek(FileID, 0)


;- Read header 6 bytes
Header${6}="" 
For i=0 To 5
  PokeB(@header$+i,ReadByte(FileID))
Next i
;Debug header$
;ShowMemoryViewer(@header$, 13) 
;Debug PeekS(@header$, -1, #PB_UTF8)
If CompareMemoryString(*HeaderGIF89a, @Header$, #PB_String_CaseSensitive , 6, #PB_Ascii) = #PB_String_Equal  
  Header$=HeaderGIF89a$
ElseIf CompareMemoryString(*HeaderGIF87a, @Header$, #PB_String_CaseSensitive , 6, #PB_Ascii) = #PB_String_Equal  
  Header$=HeaderGIF87a$
Else
  Debug "Not a GIF ( or header not find)"
  End  
EndIf
;Debug Header$

;- Logical Screen Descriptor 7 bytes
Logical_Screen_Width = ReadUnicodeCharacter(FileID) ;      Unsigned
Logical_Screen_Height = ReadUnicodeCharacter(FileID);      Unsigned
Packed_Fields = ReadByte(FileID)                    ;      Byte
Background_Color_Index = ReadByte(FileID)           ;      Byte
Pixel_Aspect_Ratio = ReadByte(FileID)               ;      Byte
                                                    ; Debug Logical_Screen_Width
                                                    ; Debug Logical_Screen_Height
                                                    ; Debug Hex(Packed_Fields, #PB_Byte)
                                                    ; Debug Bin(Packed_Fields, #PB_Byte)
                                                    ; Debug Hex( Background_Color_Index, #PB_Byte)
                                                    ; Debug Hex(Pixel_Aspect_Ratio, #PB_Byte)
Color_Table_Flag = GetBit(Packed_Fields, 7)         ;1 Bit
Color_Resolution = GetBit(Packed_Fields, 6)*4+GetBit(Packed_Fields, 5)*2+GetBit(Packed_Fields, 4)+1 ; in bits by pixel            ;3 Bits
Sort_Flag = GetBit(Packed_Fields, 3)                                                                ;1 Bit
temp=GetBit(Packed_Fields, 2)*4+GetBit(Packed_Fields, 1)*2+GetBit(Packed_Fields, 0)
Size_of_Global_Color_Table = 3*Int(Pow(2.0, temp+1))    ;3 Bits; 3 bytes for each entry
                                                        ; Debug Color_Table_Flag
                                                        ; Debug Color_Resolution
                                                        ; Debug Sort_Flag
                                                        ;Debug Str(Size_of_Global_Color_Table) + " bytes"

;- Global Color Table     size= Size_of_Global_Color_Table 768 bytes if 3bytes*256 colors
; r g b
; Here we could extract each colors...

;Optionnal Application Extension
FileSeek(FileID, Size_of_Global_Color_Table, #PB_Relative)
extension_introducer.a=$21 ; '!'
n=0
Repeat 
  n=n+1
  If n=4
    Break
  EndIf
  
  tempb.a=ReadByte(FileID)
  ;  Debug Chr(tempb)
  ;  Debug Hex(tempb,#PB_Byte)
  If tempb = $21
    ;Debug "There is an optionnal thing"
    
    tempb2.a=ReadByte(FileID)
    ;Debug Hex(tempb2,#PB_Byte)
    If tempb2 = $FF
      ;Debug "It is the optionnal Application Extension with a text, NETSCAPE..." 
      FileSeek(FileID, 17, #PB_Relative) 
    EndIf
    
    ;Plain Text Extension
    If tempb2 = $01
      ;Debug "It is the Plain Text Extension" 
      Repeat
        tempb3.a=ReadByte(FileID)
        FileSeek(FileID, tempb3, #PB_Relative) 
      Until tempb3 = 0
    EndIf
    
    
    ;Comment Extension
    If tempb2 = $FE
      ;Debug "It is the Comment  Extension" 
      Repeat
        tempb4.a=ReadByte(FileID)
        FileSeek(FileID, tempb4, #PB_Relative) 
      Until tempb4 = 0
    EndIf
  EndIf 
Until tempb2 = $F9



If tempb2 =$F9
  ;Optionnal Graphics Control Extension
  ;graphic_control_label = $F9
  Debug "Hi Ha !, we've got an Optionnal Graphics Control Extension" 
  Block_Size.a=ReadByte(FileID); must be 4
  Packed_Fields2.a=ReadByte(FileID)
  Delay_Time.u = ReadUnicodeCharacter(FileID)
  Transparent_Color_Index.a=ReadByte(FileID)
  Block_Terminator.b=ReadByte(FileID) ; must be 00
  
  ;     <Packed Fields>  =       Reserved                      3 Bits
  ;                              Disposal Method               3 Bits
  ;                              User Input Flag               1 Bit
  ;                              Transparent Color Flag        1 Bit
  ;
  ;Transparency Flag - Indicates whether a transparency index is
  ;             given IN the Transparent Index field. (This field is the least
  ;             significant bit of the byte.)
  ; 
  ;             Values :    0 -   Transparent Index is Not given.
  ;                         1 -   Transparent Index is given.
  ; 
  ;Delay Time - If Not 0, this field specifies the number of
  ;             hundredths (1/100) of a second To WAIT before continuing With the
  ;             processing of the Data Stream. The clock starts ticking immediately
  ;             after the graphic is rendered. This field may be used IN
  ;             conjunction With the User Input Flag field.
  ; 
  ;Transparency Index - The Transparency Index is such that when
  ;             encountered, the corresponding pixel of the display device is Not
  ;             modified And processing goes on To the Next pixel. The index is
  ;             present If And only If the Transparency Flag is set To 1.
  ;
  
  Transparent_Color_Flag = GetBit(Packed_Fields2, 0)
  If Transparent_Color_Flag = 1
    Debug "Transparency ON"
  Else 
    Debug "Transparency OFF"
  EndIf
  
  
  Debug  Str(Delay_Time*10) + " ms"
Else
  Debug " Sorry, delay was not found"
EndIf


Debug "ok"
CloseFile(FileID)


Mesa.