Module: Get raw frame delays from gif

Share your advanced PureBasic knowledge/code with the community.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Module: Get raw frame delays from gif

Post by netmaestro »

In case Fred doesn't change the ImageFrameDelay() return values, here's something you can use to get the real ones:

[edit] Only 6 hours old and it's already obsolete, as Fred is changing the PB command to return the same. I don't mind though, I figured it would happen and I really only wrote this for fun. It should find life after retirement as a resource for those looking to learn how to traverse a gif file and find other information which may not be supplied natively. So it's going out to stud, let's say.

Code: Select all

; Project:             Return Raw Frame Delays from Gif (in milliseconds)
; Author:              Lloyd Gallant (netmaestro)
; Birth Date:          February 23, 2017
; Obsolete Date:       February 23, 2017
; License:             Free to modify, sell, share, reverse-engineer or obliterate with a drone

DeclareModule FrameDelays
  
  Structure GIF_HEADER_AND_LOGICAL_SCREEN_DESCRIPTOR
    HeaderBytes.a[6]
    Width.u
    Height.u
    PackedByte.a
    BackgroundColorIndex.a
    PixelAspectRatio.a
  EndStructure
  
  Structure GRAPHICS_CONTROL_EXTENSION
    Introducer.a
    Label.a
    BlockSize.a
    PackedByte.a
    DelayTime.u
    TransparentColorIndex.a
    BlockTerminator.a
  EndStructure
  
  Structure IMAGE_DESCRIPTOR
    Separator.a
    ImageLeft.w
    ImageTop.w
    ImageWidth.w
    ImageHeight.w
    PackedByte.a
  EndStructure
  
  #GIFHEADER89a      = $613938464947
  #GIFHEADER87a      = $613738464947
  #GIFHEADERMASK     = $FFFFFFFFFFFF
  #COLORTABLE_EXISTS = $80
  
  Declare IsLocalColorTable(*buffer)
  Declare IsGlobalColorTable(*buffer)
  Declare ProcessGifData(*buffer, buffersize, Array FrameDelays(1))
  Declare FrameDelaysFromBuffer(*buffer, size, Array FrameDelays(1))
  Declare FrameDelaysFromFile(filename$, Array FrameDelays(1))
  
EndDeclareModule

Module FrameDelays
  
  Procedure IsLocalColorTable(*buffer)
    
    Protected *ptr.IMAGE_DESCRIPTOR = *buffer
    
    If *ptr\PackedByte & #COLORTABLE_EXISTS
      ProcedureReturn  2 << (*ptr\PackedByte & 7) *3
    Else
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  Procedure IsGlobalColorTable(*gifdata)
    
    Protected *ptr.GIF_HEADER_AND_LOGICAL_SCREEN_DESCRIPTOR = *gifdata
    
    If *ptr\PackedByte & #COLORTABLE_EXISTS
      ProcedureReturn 2 << (*ptr\PackedByte & 7) * 3
    Else
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  Procedure ProcessGifData(*buffer, buffersize, Array FrameDelays(1))
    
    Protected *readptr, *gcereader.GRAPHICS_CONTROL_EXTENSION
    Protected *idreader.IMAGE_DESCRIPTOR
    
    Protected.i index, bytes_colortable, extension_label
    Protected.i morebytes, result, nextblock, FrameCount=0
    Protected.u delaytime
    
    If PeekQ(*buffer) & #GIFHEADERMASK <> #GIFHEADER89a And PeekQ(*buffer) & #GIFHEADERMASK <> #GIFHEADER87a
      ProcedureReturn 0 
    EndIf
    
    *readptr = *buffer + 13
    bytes_colortable = IsGlobalColorTable(*buffer)
    If bytes_colortable
      *readptr + bytes_colortable    
    EndIf
    Repeat
      Select PeekA(*readptr)
          
        Case $21 
          extension_label = PeekA(*readptr + 1)
          Select extension_label
            Case $F9 
              *gcereader.GRAPHICS_CONTROL_EXTENSION = *readptr
              delaytime.u    = *gcereader\DelayTime * 10
              FrameDelays(ArraySize(FrameDelays())-1) = delaytime
              ReDim FrameDelays(ArraySize(FrameDelays())+1)
              *readptr + SizeOf(GRAPHICS_CONTROL_EXTENSION)
              
            Case $FE, $01 
              *readptr+2
              While PeekA(*readptr)
                *readptr+1
                If *readptr >= *buffer+buffersize
                  Break
                EndIf
              Wend
              *readptr+1
              
            Case $FF
              *readptr + 2
              morebytes = PeekA(*readptr) 
              *readptr + morebytes + 1
              morebytes = PeekA(*readptr)
              repeats = PeekU(*readptr+2)
              *readptr + morebytes+1
              morebytes = PeekA(*readptr)
              While morebytes
                *readptr+morebytes+1
                morebytes = PeekA(*readptr)
              Wend
              *readptr+1
              
            Default 
              Break
              
          EndSelect
          
        Case $3B
          Break
          
        Case $2C
          FrameCount+1
          *idreader.IMAGE_DESCRIPTOR = *readptr
          *readptr + SizeOf(IMAGE_DESCRIPTOR)
          
          result = IsLocalColorTable(*idreader) 
          If result 
            bytes_colortable = result
            *readptr + bytes_colortable
          EndIf
          *readptr+1
          nextblock = PeekA(*readptr)
          While nextblock
            *readptr + 1
            *readptr+nextblock
            totalcodebytes+nextblock
            nextblock = PeekA(*readptr)
          Wend
          *readptr+1
          
        Default
          Break
          
      EndSelect
      
    ForEver

    ReDim FrameDelays(FrameCount-1)
    
  EndProcedure
  
  Procedure.i FrameDelaysFromFile(file$, Array FrameDelays(1))
    
    Protected.i buffersize, *buffer, result
    
    If ReadFile(0, file$)
      buffersize = Lof(0)
      *buffer = AllocateMemory(buffersize)
      ReadData(0, *buffer, buffersize)
      CloseFile(0)
      result = ProcessGifData(*buffer, buffersize, FrameDelays())
      FreeMemory(*buffer)
      ProcedureReturn result
    Else
      ProcedureReturn 0
    EndIf
  EndProcedure
  
  Procedure.i FrameDelaysFromBuffer(*buffer, buffersize, Array FrameDelays(1))
    ProcedureReturn ProcessGifData(*buffer, buffersize, FrameDelays())
  EndProcedure
  
EndModule
And a small test program (note the array has one element to begin with)

Code: Select all

UseModule FrameDelays

Dim FrameDelays.i(1)

file$ = #PB_Compiler_Home + "Examples\Sources\Data\PureBasicLogo.gif"
FrameDelaysFromFile(file$, FrameDelays())
For i=1 To ArraySize(FrameDelays())
  Debug Str(i)+" "+Str(framedelays(i))
Next
Last edited by netmaestro on Fri Feb 24, 2017 11:08 am, edited 9 times in total.
BERESHEIT
walbus
Addict
Addict
Posts: 929
Joined: Sat Mar 02, 2013 9:17 am

Re: Module: Get raw frame delays from gif

Post by walbus »

Nice code
Last edited by walbus on Tue Mar 07, 2017 9:29 am, edited 2 times in total.
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Module: Get raw frame delays from gif

Post by davido »

@netmaestro (aka. gifmaestro),
Excellent, as usual.

Thank you for sharing. :D
DE AA EB
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Module: Get raw frame delays from gif

Post by netmaestro »

Fixed a mistake in the code, I had my frames array starting with 1 but that doesn't match with PB's ImageFramexx array, which starts at 0. So to make my code usable with PB's gif lib, I changed it. Code is updated in first post.
BERESHEIT
Post Reply