Lottie animations - [x64][x86]

Share your advanced PureBasic knowledge/code with the community.
punak
Enthusiast
Enthusiast
Posts: 115
Joined: Tue Sep 07, 2021 12:08 pm

Lottie animations - [x64][x86]

Post by punak »

Hi all,
With the help of AI and rlottie.dll x64 - rlottie.dll x86 file, I have prepared for you an example of loading Jason animations.
of course, I don't know why the animation speed doesn't change and we haven't found a solution yet.

GitHub repository address:
https://github.com/Samsung/rlottie

Code: Select all

; ======================================
;   Lottie Viewer - rlottie.dll + PureBasic
; ======================================

; === DLL Function Prototypes ===
PrototypeC.i lottie_animation_from_file(filename.p-utf8)
PrototypeC.i lottie_animation_get_totalframe(animation.i)
PrototypeC.i lottie_animation_get_framerate(animation.i)
PrototypeC.i lottie_animation_get_size(animation.i, *width, *height)
PrototypeC.i lottie_animation_render(animation.i, frame.i, buffer.i, width.i, height.i, stride.i)
PrototypeC.i lottie_animation_destroy(animation.i)

; === Load DLL ===
Global rlottieDLL = OpenLibrary(#PB_Any, "rlottie.dll")
If rlottieDLL = 0
  MessageRequester("Error", "rlottie.dll file not found.")
  End
EndIf

Global rlottie_load.lottie_animation_from_file = GetFunction(rlottieDLL, "lottie_animation_from_file")
Global rlottie_totalframe.lottie_animation_get_totalframe = GetFunction(rlottieDLL, "lottie_animation_get_totalframe")
Global rlottie_framerate.lottie_animation_get_framerate = GetFunction(rlottieDLL, "lottie_animation_get_framerate")
Global rlottie_size.lottie_animation_get_size = GetFunction(rlottieDLL, "lottie_animation_get_size")
Global rlottie_render.lottie_animation_render = GetFunction(rlottieDLL, "lottie_animation_render")
Global rlottie_destroy.lottie_animation_destroy = GetFunction(rlottieDLL, "lottie_animation_destroy")

; === Global Variables ===
Global animation.i, *buffer, stride.i
Global winW.i = Val(InputRequester("Image Width", "Enter desired width:", "500"))
Global winH.i = Val(InputRequester("Image Height", "Enter desired height:", "500"))
If winW <= 0 Or winH <= 0 : MessageRequester("Error", "Invalid size.") : End : EndIf
stride = winW * 4
*buffer = AllocateMemory(stride * winH)
If *buffer = 0 : MessageRequester("Error", "Not enough memory.") : End : EndIf

Global frame.i = 0, playing = #False, SpeedFactor.f = 1.0
Global nextFrameTime.q, frameDuration.f, totalFrames.i, framerate.f

; === Load Animation Procedure ===
Procedure LoadLottie(file.s)
  Shared animation, totalFrames, framerate, frameDuration
  If animation : rlottie_destroy(animation) : EndIf
  animation = rlottie_load(file)
  If animation = 0 : MessageRequester("Error", "Failed to load animation.") : ProcedureReturn #False : EndIf
  totalFrames = rlottie_totalframe(animation)
  framerate = rlottie_framerate(animation) : If framerate <= 0 : framerate = 30 : EndIf
  frameDuration = 1000.0 / framerate
  ProcedureReturn #True
EndProcedure

; === Select Initial File ===
Define file.s = OpenFileRequester("Select Lottie File", "", "Lottie JSON|*.json", 0)
If file = "" Or Not LoadLottie(file)
  FreeMemory(*buffer)
  CloseLibrary(rlottieDLL)
  End
EndIf

; === Create Window ===
If OpenWindow(0, 0, 0, winW, winH + 100, "Lottie Viewer", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  ImageGadget(0, 0, 0, winW, winH, 0)
  ButtonGadget(1, 10, winH + 10, 80, 30, "Start")
  ButtonGadget(6, 100, winH + 10, 80, 30, "Open")
  TrackBarGadget(2, 190, winH + 10, winW - 200, 30, 0, totalFrames - 1)
  TextGadget(3, 10, winH + 50, 80, 20, "Speed:")
  TrackBarGadget(4, 80, winH + 50, winW - 180, 20, 25, 200)
  SetGadgetState(4, 100)
  TextGadget(5, winW - 90, winH + 50, 80, 20, "1.00x", #PB_Text_Right)
  CreateImage(10, winW, winH, 32, #PB_Image_Transparent)
  
  Repeat
    Define Event = WindowEvent()
    If Event = #PB_Event_Gadget
      Select EventGadget()
        Case 1 ; Start/Stop
          playing = Bool(Not playing)
          If playing
            SetGadgetText(1, "Stop")
            nextFrameTime = ElapsedMilliseconds() + frameDuration / SpeedFactor
          Else
            SetGadgetText(1, "Start")
          EndIf
          
        Case 6 ; Open
          file = OpenFileRequester("Select New File", "", "Lottie JSON|*.json", 0)
          If file <> "" And LoadLottie(file)
            frame = 0
            SetGadgetState(2, 0)
          EndIf
          
        Case 2 ; Seek
          If Not playing : frame = GetGadgetState(2) : EndIf
          
        Case 4 ; Speed
          SpeedFactor = GetGadgetState(4) / 100.0
          If SpeedFactor < 0.25 : SpeedFactor = 0.25 : EndIf
          SetGadgetText(5, StrF(SpeedFactor, 2) + "x")
          nextFrameTime = ElapsedMilliseconds() + frameDuration / SpeedFactor
      EndSelect
    EndIf
    
    ; === Playback ===
    If playing And ElapsedMilliseconds() >= nextFrameTime
      frame + 1 : If frame >= totalFrames : frame = 0 : EndIf
      SetGadgetState(2, frame)
      nextFrameTime = ElapsedMilliseconds() + frameDuration / SpeedFactor
      ; === Render Frame ===
      rlottie_render(animation, frame, *buffer, winW, winH, stride)
      If IsImage(10)
        StartDrawing(ImageOutput(10))
        Define *dest = DrawingBuffer()
        For y = 0 To winH - 1
          CopyMemory(*buffer + (winH - 1 - y) * stride, *dest + y * stride, stride)
        Next
        StopDrawing()
        SetGadgetState(0, ImageID(10))
      EndIf      
    EndIf
    
    Delay(1)
  Until Event = #PB_Event_CloseWindow
EndIf

; === Cleanup ===
rlottie_destroy(animation)
FreeMemory(*buffer)
CloseLibrary(rlottieDLL)
End
User avatar
idle
Always Here
Always Here
Posts: 6095
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Lottie animations - [x64][x86]

Post by idle »

That looks interesting thanks for sharing it
punak
Enthusiast
Enthusiast
Posts: 115
Joined: Tue Sep 07, 2021 12:08 pm

Re: Lottie animations - [x64][x86]

Post by punak »

idle wrote: Mon Oct 06, 2025 9:40 pm That looks interesting thanks for sharing it
Thank you idle.
I'll post the new version here with two examples.

You can use the animations on the site below as examples.
https://iconscout.com/lottie-animations

Module

Code: Select all

; ======================================
;   Module: LottiePlayer
;   Description: rlottie wrapper for PureBasic
;   Features:
;     - Multiple independent animations
;     - Adjustable size
;     - Independent timers
;     - Speed control
; ======================================

DeclareModule LottiePlayer
  Structure Animation
    handle.i
    *buffer
    width.i
    height.i
    stride.i
    totalFrames.i
    frameRate.f
    frameDuration.f
    currentFrame.i
    SpeedFactor.f
    playing.i
    winID.i
    imgGadget.i
    imgID.i
    timerID.i
  EndStructure

  Declare Init()
  Declare.i CreateAnimation(winID.i, gadgetID.i, width.i, height.i, file.s)
  Declare Play(*anim.Animation)
  Declare Pause(*anim.Animation)
  Declare SetSpeed(*anim.Animation, speed.f)
  Declare NextFrame(*anim.Animation)
  Declare RenderFrame(*anim.Animation)
  Declare Destroy(*anim.Animation)
  Declare Quit()
EndDeclareModule


Module LottiePlayer
  ; === rlottie prototypes ===
  PrototypeC.i lottie_animation_from_file(filename.p-utf8)
  PrototypeC.i lottie_animation_get_totalframe(animation.i)
  PrototypeC.i lottie_animation_get_framerate(animation.i)
  PrototypeC.i lottie_animation_get_size(animation.i, *width, *height)
  PrototypeC.i lottie_animation_render(animation.i, frame.i, buffer.i, width.i, height.i, stride.i)
  PrototypeC.i lottie_animation_destroy(animation.i)

  Global rlottieDLL
  Global rlottie_load.lottie_animation_from_file
  Global rlottie_totalframe.lottie_animation_get_totalframe
  Global rlottie_framerate.lottie_animation_get_framerate
  Global rlottie_size.lottie_animation_get_size
  Global rlottie_render.lottie_animation_render
  Global rlottie_destroy.lottie_animation_destroy

  ; === Initialize DLL ===
  Procedure Init()
    If rlottieDLL = 0
      rlottieDLL = OpenLibrary(#PB_Any, "rlottie.dll")
      If rlottieDLL = 0
        MessageRequester("Error", "rlottie.dll not found.")
        End
      EndIf

      rlottie_load = GetFunction(rlottieDLL, "lottie_animation_from_file")
      rlottie_totalframe = GetFunction(rlottieDLL, "lottie_animation_get_totalframe")
      rlottie_framerate = GetFunction(rlottieDLL, "lottie_animation_get_framerate")
      rlottie_size = GetFunction(rlottieDLL, "lottie_animation_get_size")
      rlottie_render = GetFunction(rlottieDLL, "lottie_animation_render")
      rlottie_destroy = GetFunction(rlottieDLL, "lottie_animation_destroy")
    EndIf
  EndProcedure

  ; === Create animation instance ===
  Procedure.i CreateAnimation(winID.i, gadgetID.i, width.i, height.i, file.s)
    Protected *anim.Animation = AllocateStructure(Animation)
    *anim\winID = winID
    *anim\imgGadget = gadgetID
    *anim\width = width
    *anim\height = height
    *anim\stride = width * 4
    *anim\SpeedFactor = 1.0
    *anim\timerID = gadgetID + 1000 ; unique timer per animation

    *anim\handle = rlottie_load(file)
    If *anim\handle = 0
      MessageRequester("Error", "Failed to load animation: " + file)
      FreeStructure(*anim)
      ProcedureReturn 0
    EndIf

    *anim\totalFrames = rlottie_totalframe(*anim\handle)
    *anim\frameRate   = rlottie_framerate(*anim\handle)
    If *anim\frameRate <= 0 : *anim\frameRate = 30 : EndIf
    *anim\frameDuration = 1000.0 / *anim\frameRate
    *anim\buffer = AllocateMemory(*anim\stride * *anim\height)

    ; 🔹 create real image for drawing
    *anim\imgID = CreateImage(#PB_Any, width, height, 32, #PB_Image_Transparent)
    SetGadgetState(*anim\imgGadget, ImageID(*anim\imgID))

    ProcedureReturn *anim
  EndProcedure

  ; === Render single frame ===
  Procedure RenderFrame(*anim.Animation)
    If *anim = 0 Or *anim\handle = 0 : ProcedureReturn : EndIf

    rlottie_render(*anim\handle, *anim\currentFrame, *anim\buffer, *anim\width, *anim\height, *anim\stride)

    StartDrawing(ImageOutput(*anim\imgID))
      Define *dest = DrawingBuffer()
      ; 🔹 fix flipped image
      For y = 0 To *anim\height - 1
        CopyMemory(*anim\buffer + (*anim\height - 1 - y) * *anim\stride, *dest + y * *anim\stride, *anim\stride)
      Next
    StopDrawing()

    SetGadgetState(*anim\imgGadget, ImageID(*anim\imgID))
  EndProcedure

  ; === Move to next frame ===
  Procedure NextFrame(*anim.Animation)
    *anim\currentFrame + 1
    If *anim\currentFrame >= *anim\totalFrames : *anim\currentFrame = 0 : EndIf
    RenderFrame(*anim)
  EndProcedure

  ; === Play animation ===
  Procedure Play(*anim.Animation)
    If *anim
      *anim\playing = #True
      Protected timeout = Int(*anim\frameDuration / *anim\SpeedFactor)
      If timeout < 5 : timeout = 5 : EndIf
      AddWindowTimer(*anim\winID, *anim\timerID, timeout)
    EndIf
  EndProcedure

  ; === Pause animation ===
  Procedure Pause(*anim.Animation)
    If *anim
      *anim\playing = #False
      RemoveWindowTimer(*anim\winID, *anim\timerID)
    EndIf
  EndProcedure

  ; === Change speed ===
  Procedure SetSpeed(*anim.Animation, speed.f)
    If *anim
      If speed <= 0.1 : speed = 0.1 : EndIf
      *anim\SpeedFactor = speed
      If *anim\playing
        RemoveWindowTimer(*anim\winID, *anim\timerID)
        Protected timeout = Int(*anim\frameDuration / *anim\SpeedFactor)
        If timeout < 5 : timeout = 5 : EndIf
        AddWindowTimer(*anim\winID, *anim\timerID, timeout)
      EndIf
    EndIf
  EndProcedure

  ; === Destroy animation ===
  Procedure Destroy(*anim.Animation)
    If *anim
      RemoveWindowTimer(*anim\winID, *anim\timerID)
      If *anim\handle : rlottie_destroy(*anim\handle) : EndIf
      If *anim\buffer : FreeMemory(*anim\buffer) : EndIf
      If IsImage(*anim\imgID) : FreeImage(*anim\imgID) : EndIf
      FreeStructure(*anim)
    EndIf
  EndProcedure

  Procedure Quit()
    If rlottieDLL
      CloseLibrary(rlottieDLL)
      rlottieDLL = 0
    EndIf
  EndProcedure
EndModule
Example 1: Multi Lottie Animations

Code: Select all

; ======================================
;  Example: Multi Lottie Animations
; ======================================

XIncludeFile "LottiePlayer.pbi"
UseModule LottiePlayer

Init()

; --- Create main window ---
OpenWindow(0, 0, 0, 800, 400, "Multi Lottie Viewer", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ImageGadget(0, 0, 0, 400, 400, 0)
ImageGadget(1, 400, 0, 400, 400, 0)

; --- Load animations (custom sizes) ---
Define *anim1.Animation = CreateAnimation(0, 0, 400, 400, "1.json")
Define *anim2.Animation = CreateAnimation(0, 1, 400, 400, "2.json")

If *anim1 = 0 Or *anim2 = 0
  MessageRequester("Error", "Failed to load animations.")
  End
EndIf

; --- Play both ---
Play(*anim1)
Play(*anim2)

; --- Example: different speeds ---
SetSpeed(*anim1, 1.0)
SetSpeed(*anim2, 0.5)

; --- Event loop ---
Repeat
  Define ev = WaitWindowEvent()

  If ev = #PB_Event_Timer
    Select EventTimer()
      Case *anim1\timerID : NextFrame(*anim1)
      Case *anim2\timerID : NextFrame(*anim2)
    EndSelect
  EndIf

Until ev = #PB_Event_CloseWindow

; --- Cleanup ---
Pause(*anim1) : Pause(*anim2)
Destroy(*anim1) : Destroy(*anim2)
Quit()
Example 2: Lottie Player with Controls (UI)

Code: Select all

; ======================================
; Example: Lottie Player with Controls (UI)
; ======================================

XIncludeFile "LottiePlayer.pbi"
UseModule LottiePlayer

Init()

; === GUI setup ===
#Win_Main = 0
#Img_Anim = 0
#Btn_Play = 1
#Btn_Open = 2
#SeekBar  = 3
#SpeedBar = 4
#SpeedTxt = 5

Define winW = 500, winH = 500
OpenWindow(#Win_Main, 0, 0, winW, winH + 100, "Lottie Player", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ImageGadget(#Img_Anim, 0, 0, winW, winH, 0)
ButtonGadget(#Btn_Play, 10, winH + 10, 80, 30, "Start")
ButtonGadget(#Btn_Open, 100, winH + 10, 80, 30, "Open")

TrackBarGadget(#SeekBar, 190, winH + 10, winW - 200, 30, 0, 100)
TrackBarGadget(#SpeedBar, 80, winH + 50, winW - 180, 20, 10, 400)
TextGadget(#SpeedTxt, winW - 90, winH + 50, 80, 20, "1.00x", #PB_Text_Right)
TextGadget(#PB_Any, 10, winH + 50, 80, 20, "Speed:")

SetGadgetState(#SpeedBar, 100)

; === Animation variables ===
Global *anim.Animation = 0
Global playing = #False

; === Helper procedures ===

Procedure LoadAnimation(file.s)
  Global *anim
  If *anim : Destroy(*anim) : EndIf

  *anim = CreateAnimation(#Win_Main, #Img_Anim, 500, 500, file)
  If *anim = 0
    MessageRequester("Error", "Failed to load: " + file)
    ProcedureReturn
  EndIf

  SetGadgetAttribute(#SeekBar, #PB_TrackBar_Maximum, *anim\totalFrames - 1)
  SetGadgetState(#SeekBar, 0)
  RenderFrame(*anim)
EndProcedure


; === Main event loop ===
Repeat
  Define ev = WaitWindowEvent()

  Select ev
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #Btn_Play
          If *anim
            playing = Bool(Not playing)
            If playing
              SetGadgetText(#Btn_Play, "Stop")
              Play(*anim)
            Else
              SetGadgetText(#Btn_Play, "Start")
              Pause(*anim)
            EndIf
          EndIf

        Case #Btn_Open
          Define file.s = OpenFileRequester("Select Lottie JSON", "", "Lottie JSON|*.json", 0)
          If file <> "" : LoadAnimation(file) : EndIf

        Case #SeekBar
          If *anim And Not playing
            *anim\currentFrame = GetGadgetState(#SeekBar)
            RenderFrame(*anim)
          EndIf

        Case #SpeedBar
          If *anim
            Define speed.f = GetGadgetState(#SpeedBar) / 100.0
            If speed < 0.1 : speed = 0.1 : EndIf
            SetSpeed(*anim, speed)
            SetGadgetText(#SpeedTxt, StrF(speed, 2) + "x")
          EndIf
      EndSelect

    Case #PB_Event_Timer
      If *anim
        If EventTimer() = *anim\timerID
          NextFrame(*anim)
          SetGadgetState(#SeekBar, *anim\currentFrame)
        EndIf
      EndIf

    Case #PB_Event_CloseWindow
      Break
  EndSelect
ForEver

; === Cleanup ===
If *anim
  Pause(*anim)
  Destroy(*anim)
EndIf

Quit()
AZJIO
Addict
Addict
Posts: 2241
Joined: Sun May 14, 2017 1:48 am

Re: Lottie animations - [x64][x86]

Post by AZJIO »

Is there an example of how to get something? When I test it, I get "Open JSON file" and then what? A JSON file can be anything, such as a program setting or a data base.
punak
Enthusiast
Enthusiast
Posts: 115
Joined: Tue Sep 07, 2021 12:08 pm

Re: Lottie animations - [x64][x86]

Post by punak »

AZJIO wrote: Tue Oct 07, 2025 10:14 am Is there an example of how to get something? When I test it, I get "Open JSON file" and then what? A JSON file can be anything, such as a program setting or a data base.
In the previous post, I gave the website address to download free Jason animation, just download one or more of them and then test it.
https://iconscout.com/lottie-animations
pjay
Enthusiast
Enthusiast
Posts: 282
Joined: Thu Mar 30, 2006 11:14 am

Re: Lottie animations - [x64][x86]

Post by pjay »

Wow, this is great :!:

I'd never heard about lottie files until now - it turns out that I had a number of them already stored locally as Microsoft uses them.

I modified your module to allow for rendering into a canvas gadget as the image gadget was flickering:

In your CreateAnimation() & RenderFrame() procedures, I replaced:

Code: Select all

   SetGadgetState(*anim\imgGadget, ImageID(*anim\imgID))
With:

Code: Select all

    Select GadgetType(*anim\imgGadget) ; <<< modified PJ25
      Case #PB_GadgetType_Canvas : SetGadgetAttribute(*anim\imgGadget, #PB_Canvas_Image, ImageID(*anim\imgID))
      Case #PB_GadgetType_Image : SetGadgetState(*anim\imgGadget, ImageID(*anim\imgID))
    EndSelect
I had issues with the background colouring on a couple of test files, but I think this is a problem within the dll.

Thanks for sharing 8)
Post Reply