Function GetGIFDelay()

Share your advanced PureBasic knowledge/code with the community.
Mesa
Enthusiast
Enthusiast
Posts: 349
Joined: Fri Feb 24, 2012 10:19 am

Function GetGIFDelay()

Post by Mesa »

Each animated GIF has its own delay, so i've tried to create a GetGIFDelay() Function() to animated it with the good delay.

Code: Select all

;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


For whom is interesting with a GIF header decompilator, here is the beginning of one (not complete):

Code: Select all

;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";"C:\Documents and Settings\ok\Bureau\000divers\run01.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.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8433
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Function GetGIFDelay()

Post by netmaestro »

You're returning a single delay time? An animated gif has multiple frames and each frame has its own graphics control extension containing its own individual delay time. If you're reading a gif containing 28 frames, you need to return an array with 28 elements to have anything useful. Sometimes the frame delays are all the same but usually not.
BERESHEIT
walbus
Addict
Addict
Posts: 929
Joined: Sat Mar 02, 2013 9:17 am

Re: Function GetGIFDelay()

Post by walbus »

Yep, and you can herefore not use a low priority window timer, you must use a own stable timer
Fred
Administrator
Administrator
Posts: 16681
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Function GetGIFDelay()

Post by Fred »

I will add an ImageFrameDelay() function in the next beta to get this info easily, per frame.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: Function GetGIFDelay()

Post by ts-soft »

Fred wrote:I will add an ImageFrameDelay() function in the next beta to get this info easily, per frame.
Image Top News
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
Post Reply