Page 1 of 1

Module: Get raw frame delays from gif

Posted: Thu Feb 23, 2017 10:48 pm
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

Re: Module: Get raw frame delays from gif

Posted: Thu Feb 23, 2017 11:28 pm
by walbus
Nice code

Re: Module: Get raw frame delays from gif

Posted: Thu Feb 23, 2017 11:41 pm
by davido
@netmaestro (aka. gifmaestro),
Excellent, as usual.

Thank you for sharing. :D

Re: Module: Get raw frame delays from gif

Posted: Fri Feb 24, 2017 7:40 am
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.