Using an animated Gif on a gui Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Using an animated Gif on a gui

17 Nov 2020, 16:33

I was searching for how to load a gif into a Gui and found this example:

Code by @tmplinshi

Code: Select all

#SingleInstance Force
SetBatchLines -1
;#include Gdip.ahk

pToken := Gdip_Startup()
OnExit, Exit

Gui, Add, Picture, hwndhwndGif1 w80 h80
Gui, Add, Button, x+10 w80 h80 gPlayPause hwndhwndPlayPause, Play
Gui, Show,, Animated gif

gif1 := new Gif("mario_running.gif", hwndGif1)
return

;######################################################

PlayPause:
	isPlaying := gif1.isPlaying
	GuiControl,, % hwndPlayPause, % (isPlaying) ? "Play" : "Pause"
	if (!isPlaying) {
		gif1.Play()
	} else {
		gif1.Pause()
	}
return

;######################################################

Exit:
Gdip_ShutDown(pToken)
ExitApp
return

;######################################################

class Gif
{	
	__New(file, hwnd, autoPlay := false)
	{
		this.file := file
		this.hwnd := hwnd
		this.isPlaying := false
		this.pBitmap := Gdip_CreateBitmapFromFile(this.file)

		; Gdip_GetImageDimensions(this.pBitmap, width, height)
		ControlGetPos,,, width, height,, ahk_id %hwnd%
		this.width := width, this.height := height

		DllCall("Gdiplus\GdipCreateFromHWND", "ptr", hwnd, "ptrp", pGraphics)
		this.pGraphics := pGraphics
		
		DllCall("Gdiplus\GdipImageGetFrameDimensionsCount", "ptr", this.pBitmap, "uptr*", frameDimensions)
		this.SetCapacity("dimensionIDs", 32)
		DllCall("Gdiplus\GdipImageGetFrameDimensionsList", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", frameDimensions)
		DllCall("Gdiplus\GdipImageGetFrameCount", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int*", count)
		this.frameCount := count
		this.frameCurrent := -1
		this.frameDelay := this.GetFrameDelay(this.pBitmap)

		if autoPlay
			this.play()
	}

	; Return a zero-based array, containing the frames delay (in milliseconds)
	GetFrameDelay(pImage) {
		static PropertyTagFrameDelay := 0x5100

		DllCall("Gdiplus\GdipGetPropertyItemSize", "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt*", ItemSize)
		VarSetCapacity(Item, ItemSize, 0)
		DllCall("Gdiplus\GdipGetPropertyItem"    , "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt", ItemSize, "Ptr", &Item)

		PropLen := NumGet(Item, 4, "UInt")
		PropVal := NumGet(Item, 8 + A_PtrSize, "UPtr")

		outArray := []
		Loop, % PropLen//4 {
			if !n := NumGet(PropVal+0, (A_Index-1)*4, "UInt")
				n := 10
			outArray[A_Index-1] := n * 10
		}
		return outArray
	}
	
	Play()
	{
		this.isPlaying := true
		fn := this._Play.Bind(this)
		this._fn := fn
		SetTimer, % fn, -1
	}
	
	Pause()
	{
		this.isPlaying := false
		fn := this._fn
		SetTimer, % fn, Delete
	}
	
	_Play()
	{
		this.frameCurrent := (this.frameCurrent = this.frameCount-1) ? 0 : this.frameCurrent + 1
		DllCall("Gdiplus\GdipImageSelectActiveFrame", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", this.frameCurrent)

		; hBitmap := Gdip_CreateHBITMAPFromBitmap(this.pBitmap)
		; SetImage(this.hwnd, hBitmap)
		; DeleteObject(hBitmap)
		DllCall("gdiplus\GdipDrawImageRectI", "ptr", this.pGraphics, "ptr", this.pBitmap
        , "int", 0, "int", 0, "int", this.width, "int", this.height)

		fn := this._fn
		SetTimer, % fn, % -1 * this.frameDelay[this.frameCurrent]
	}
	
	__Delete()
	{
		Gdip_DisposeImage(this.pBitmap)
		Gdip_DeleteGraphics(this.pGraphics)
		Object.Delete("dimensionIDs")
	}
}

But it looks like there's something wrong in the code, old frames are not getting clean:
2020-11-17_18-30-14.gif
2020-11-17_18-30-14.gif (163.17 KiB) Viewed 5566 times
I tried: clear the control with guicontrol, blank, use a transparent image between each frame, nothing did work.
Does someone have any idea what could be wrong?
c7aesa7r
Posts: 209
Joined: 02 Jun 2016, 21:09

Re: Using an animated Gif on a gui

18 Nov 2020, 04:32

@teadrinker Sure. I also test with many others, all got the same behavior.
2020-11-18_06-30-58.gif
2020-11-18_06-30-58.gif (125.88 KiB) Viewed 5515 times
Attachments
crash.gif
crash.gif (13.53 KiB) Viewed 5515 times
cubone.gif
cubone.gif (14.04 KiB) Viewed 5515 times
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using an animated Gif on a gui  Topic is solved

18 Nov 2020, 10:50

Try this:

Code: Select all

#NoEnv
; #Include Gdip_All.ahk
SetBatchLines, -1
filePath := "C:\Users\User\Desktop\crash.gif" ; specify the file path to gif

pToken := Gdip_Startup()
OnExit, Exit

exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
Gui, Add, Picture, y10 hwndhwndGif1, % filePath
Gui, Add, Button, xp y+10 w80 h24 gPlayPause hwndhwndPlayPause, Play

gif1 := new Gif(filePath, hwndGif1)
Gui, Show, AutoSize, Animated gif
return

GuiClose:
   ExitApp

;######################################################

PlayPause:
isPlaying := gif1.isPlaying
GuiControl,, % hwndPlayPause, % (isPlaying) ? "Play" : "Pause"
if (!isPlaying) {
   gif1.Play()
} else {
   gif1.Pause()
}
return

;######################################################

Exit:
Gdip_ShutDown(pToken)
ExitApp
return

;######################################################

class Gif
{   
   __New(file, hwnd, cycle := true)
   {
      this.file := file
      this.hwnd := hwnd
      this.cycle := cycle
      this.pBitmap := Gdip_CreateBitmapFromFile(this.file)
      Gdip_GetImageDimensions(this.pBitmap, width, height)
      this.width := width, this.height := height
      this.isPlaying := false
      
      DllCall("Gdiplus\GdipImageGetFrameDimensionsCount", "ptr", this.pBitmap, "uptr*", frameDimensions)
      this.SetCapacity("dimensionIDs", 16*frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameDimensionsList", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameCount", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int*", count)
      this.frameCount := count
      this.frameCurrent := -1
      this.frameDelay := this.GetFrameDelay(this.pBitmap)
      this._Play("")
   }

   ; Return a zero-based array, containing the frames delay (in milliseconds)
   GetFrameDelay(pImage) {
      static PropertyTagFrameDelay := 0x5100

      DllCall("Gdiplus\GdipGetPropertyItemSize", "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt*", ItemSize)
      VarSetCapacity(Item, ItemSize, 0)
      DllCall("Gdiplus\GdipGetPropertyItem"    , "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt", ItemSize, "Ptr", &Item)

      PropLen := NumGet(Item, 4, "UInt")
      PropVal := NumGet(Item, 8 + A_PtrSize, "UPtr")

      outArray := []
      Loop, % PropLen//4 {
         if !n := NumGet(PropVal+0, (A_Index-1)*4, "UInt")
            n := 10
         outArray[A_Index-1] := n * 10
      }
      return outArray
   }
   
   Play()
   {
      this.isPlaying := true
      fn := this._Play.Bind(this)
      this._fn := fn
      SetTimer, % fn, -1
   }
   
   Pause()
   {
      this.isPlaying := false
      fn := this._fn
      SetTimer, % fn, Delete
   }
   
   _Play(mode := "set")
   {
      this.frameCurrent := mod(++this.frameCurrent, this.frameCount)
      DllCall("Gdiplus\GdipImageSelectActiveFrame", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", this.frameCurrent)
      hBitmap := Gdip_CreateHBITMAPFromBitmap(this.pBitmap)
      SetImage(this.hwnd, hBitmap)
      DeleteObject(hBitmap)
      if (mode = "set" && this.frameCurrent < (this.cycle ? 0xFFFFFFFF : this.frameCount - 1)) {
         fn := this._fn
         SetTimer, % fn, % -1 * this.frameDelay[this.frameCurrent]
      }
   }
   
   __Delete()
   {
      Gdip_DisposeImage(this.pBitmap)
      Object.Delete("dimensionIDs")
   }
}
Milton
Posts: 6
Joined: 15 Dec 2020, 19:51

Re: Using an animated Gif on a gui

15 Dec 2020, 19:59

teadrinker wrote:
18 Nov 2020, 10:50
Try this:

Code: Select all

#NoEnv
; #Include Gdip_All.ahk
SetBatchLines, -1
filePath := "C:\Users\User\Desktop\crash.gif" ; specify the file path to gif

pToken := Gdip_Startup()
OnExit, Exit

exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
Gui, Add, Picture, y10 hwndhwndGif1, % filePath
Gui, Add, Button, xp y+10 w80 h24 gPlayPause hwndhwndPlayPause, Play

gif1 := new Gif(filePath, hwndGif1)
Gui, Show, AutoSize, Animated gif
return

GuiClose:
   ExitApp

;######################################################

PlayPause:
isPlaying := gif1.isPlaying
GuiControl,, % hwndPlayPause, % (isPlaying) ? "Play" : "Pause"
if (!isPlaying) {
   gif1.Play()
} else {
   gif1.Pause()
}
return

;######################################################

Exit:
Gdip_ShutDown(pToken)
ExitApp
return

;######################################################

class Gif
{   
   __New(file, hwnd, cycle := true)
   {
      this.file := file
      this.hwnd := hwnd
      this.cycle := cycle
      this.pBitmap := Gdip_CreateBitmapFromFile(this.file)
      Gdip_GetImageDimensions(this.pBitmap, width, height)
      this.width := width, this.height := height
      this.isPlaying := false
      
      DllCall("Gdiplus\GdipImageGetFrameDimensionsCount", "ptr", this.pBitmap, "uptr*", frameDimensions)
      this.SetCapacity("dimensionIDs", 16*frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameDimensionsList", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameCount", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int*", count)
      this.frameCount := count
      this.frameCurrent := -1
      this.frameDelay := this.GetFrameDelay(this.pBitmap)
      this._Play("")
   }

   ; Return a zero-based array, containing the frames delay (in milliseconds)
   GetFrameDelay(pImage) {
      static PropertyTagFrameDelay := 0x5100

      DllCall("Gdiplus\GdipGetPropertyItemSize", "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt*", ItemSize)
      VarSetCapacity(Item, ItemSize, 0)
      DllCall("Gdiplus\GdipGetPropertyItem"    , "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt", ItemSize, "Ptr", &Item)

      PropLen := NumGet(Item, 4, "UInt")
      PropVal := NumGet(Item, 8 + A_PtrSize, "UPtr")

      outArray := []
      Loop, % PropLen//4 {
         if !n := NumGet(PropVal+0, (A_Index-1)*4, "UInt")
            n := 10
         outArray[A_Index-1] := n * 10
      }
      return outArray
   }
   
   Play()
   {
      this.isPlaying := true
      fn := this._Play.Bind(this)
      this._fn := fn
      SetTimer, % fn, -1
   }
   
   Pause()
   {
      this.isPlaying := false
      fn := this._fn
      SetTimer, % fn, Delete
   }
   
   _Play(mode := "set")
   {
      this.frameCurrent := mod(++this.frameCurrent, this.frameCount)
      DllCall("Gdiplus\GdipImageSelectActiveFrame", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", this.frameCurrent)
      hBitmap := Gdip_CreateHBITMAPFromBitmap(this.pBitmap)
      SetImage(this.hwnd, hBitmap)
      DeleteObject(hBitmap)
      if (mode = "set" && this.frameCurrent < (this.cycle ? 0xFFFFFFFF : this.frameCount - 1)) {
         fn := this._fn
         SetTimer, % fn, % -1 * this.frameDelay[this.frameCurrent]
      }
   }
   
   __Delete()
   {
      Gdip_DisposeImage(this.pBitmap)
      Object.Delete("dimensionIDs")
   }
}
how can I set gif image width and height? I've tried like this "this.with:=50" but does not work, native resolution is always taken no matter if I specified this.width and this.height
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using an animated Gif on a gui

16 Dec 2020, 08:28

Replace the line SetImage(this.hwnd, hBitmap) with GuiControl,, % this.hwnd, HBITMAP: %hBitmap%, then you can change the picture size here:

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1, % filePath
like this

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1 w400 h600, % filePath
or like this

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1 w400 h-1, % filePath
Milton
Posts: 6
Joined: 15 Dec 2020, 19:51

Re: Using an animated Gif on a gui

17 Dec 2020, 04:23

teadrinker wrote:
16 Dec 2020, 08:28
Replace the line SetImage(this.hwnd, hBitmap) with GuiControl,, % this.hwnd, HBITMAP: %hBitmap%, then you can change the picture size here:

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1, % filePath
like this

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1 w400 h600, % filePath
or like this

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1 w400 h-1, % filePath
Thank you!!
Milton
Posts: 6
Joined: 15 Dec 2020, 19:51

Re: Using an animated Gif on a gui

22 Dec 2020, 02:59

teadrinker wrote:
16 Dec 2020, 08:28
Replace the line SetImage(this.hwnd, hBitmap) with GuiControl,, % this.hwnd, HBITMAP: %hBitmap%, then you can change the picture size here:

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1, % filePath
like this

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1 w400 h600, % filePath
or like this

Code: Select all

Gui, Add, Picture, y10 hwndhwndGif1 w400 h-1, % filePath
Sorry for bothering again but how can I change the gif source? if I change the filepath it only adds the frames to previous generated frames bitmap making a mix of frames
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using an animated Gif on a gui

22 Dec 2020, 03:57

Try:

Code: Select all

gif1.Pause()
GuiControl,, % hwndGif1, % newFilePath
gif1 := new Gif(newFilePath, hwndGif1)
gif1.Play()
Not tested :)
Milton
Posts: 6
Joined: 15 Dec 2020, 19:51

Re: Using an animated Gif on a gui

22 Dec 2020, 04:08

teadrinker wrote:
22 Dec 2020, 03:57
Try:

Code: Select all

gif1.Pause()
GuiControl,, % hwndGif1, % newFilePath
gif1 := new Gif(newFilePath, hwndGif1)
gif1.Play()
Not tested :)
Thank you! it works
Milton
Posts: 6
Joined: 15 Dec 2020, 19:51

Re: Using an animated Gif on a gui

23 Dec 2020, 04:46

teadrinker wrote:
22 Dec 2020, 03:57
Try:

Code: Select all

gif1.Pause()
GuiControl,, % hwndGif1, % newFilePath
gif1 := new Gif(newFilePath, hwndGif1)
gif1.Play()
Not tested :)
Here I am with a new asking hope you can help me out with this, after changing gif file over 5 times (time interval does not matter) it starts mixing previous gif frames
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using an animated Gif on a gui

23 Dec 2020, 05:44

I can try to help if you show your code and attach your gifs.
Milton
Posts: 6
Joined: 15 Dec 2020, 19:51

Re: Using an animated Gif on a gui

24 Dec 2020, 00:30

teadrinker wrote:
23 Dec 2020, 05:44
I can try to help if you show your code and attach your gifs.
Sorry for late
This is not my full code, but it shows what happens when gif files are after 4 or 5 and so on, also gif files does not matter so i'll not attach them you can use 2 gif and switch them like this code.
btw Happy Christmas My friend.

Code: Select all

#Include Gdip.ahk
pToken := Gdip_Startup()
gifFiles:=[]
    Loop, Files, E:\HDD\Downloads\Gifs\*.gif
    {
      gifFiles.push(A_LoopFileFullPath)
    }
index:=Rand(1,gifFiles.Length())
GifFile:=gifFiles[index]


Gui, 1: -Caption
Gui, 1: Add, Pic, AltSubmit BackgroundTrans hwndhwndGif, % GifFile
ScreenHeight:=A_ScreenHeight-40
Gui, 1: Show, w%A_ScreenWidth% h%ScreenHeight% center NA

gif1 := new Gif(GifFile, hwndGif)
gif1.Play()

SetTimer, ChangeGif, 5000
Return

ChangeGif:
index:=Rand(1,gifFiles.Length())
GifFile:=gifFiles[index]
  gif1.Pause()
  gif1 := new Gif(GifFile, hwndGif)
  gif1.Play()
  Return
Rand( a=0.0, b=1 ) {
  IfEqual,a,,Random,,% r := b = 1 ? Rand(0,0xFFFFFFFF) : b
    Else Random,r,a,b
      Return r
  }

class Gif
  {   
     __New(file, hwnd, cycle := true)
     {
        this.file := file
        this.hwnd := hwnd
        this.cycle := cycle
        this.pBitmap := 0
        this.pBitmap := Gdip_CreateBitmapFromFile(this.file)
        this.isPlaying := false
        
        DllCall("Gdiplus\GdipImageGetFrameDimensionsCount", "ptr", this.pBitmap, "uptr*", frameDimensions)
        this.SetCapacity("dimensionIDs", 16*frameDimensions)
        DllCall("Gdiplus\GdipImageGetFrameDimensionsList", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", frameDimensions)
        DllCall("Gdiplus\GdipImageGetFrameCount", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int*", count)
        this.frameCount := count
        this.frameCurrent := -1
        this.frameDelay := this.GetFrameDelay(this.pBitmap)
        this._Play("")
     }

     ; Return a zero-based array, containing the frames delay (in milliseconds)
     GetFrameDelay(pImage) {
        static PropertyTagFrameDelay := 0x5100

        DllCall("Gdiplus\GdipGetPropertyItemSize", "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt*", ItemSize)
        VarSetCapacity(Item, ItemSize, 0)
        DllCall("Gdiplus\GdipGetPropertyItem"    , "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt", ItemSize, "Ptr", &Item)
        PropLen := NumGet(Item, 4, "UInt")
        PropVal := NumGet(Item, 8 + A_PtrSize, "UPtr")

        outArray := []
        Loop, % PropLen//4 {
           if !n := NumGet(PropVal+0, (A_Index-1)*4, "UInt")
              n := 10
           outArray[A_Index-1] := n * 10
        }
        return outArray
     }
     
     Play()
     {
        this.isPlaying := true
        fn := this._Play.Bind(this)
        this._fn := fn
        SetTimer, % fn, -1
     }
     Pause()
       {
          this.isPlaying := false
          fn := this._fn
          SetTimer, % fn, Delete
       }

     _Play(mode := "set")
     {
          this.frameCurrent := mod(++this.frameCurrent, this.frameCount)
         DllCall("Gdiplus\GdipImageSelectActiveFrame", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", this.frameCurrent)
        hBitmap := Gdip_CreateHBITMAPFromBitmap(this.pBitmap)
        GuiControl,, % this.hwnd, HBITMAP: %hBitmap%
        DeleteObject(hBitmap)
        if (mode = "set" && this.frameCurrent < (this.cycle ? 0xFFFFFFFF : this.frameCount - 1)) {
           fn := this._fn
           SetTimer, % fn, % -1 * this.frameDelay[this.frameCurrent]
        }
     }
     
     __Delete()
     {
        Gdip_DisposeImage(this.pBitmap)
        Object.Delete("dimensionIDs")
     }

  }
[Mod edit: [code][/code] tags added.]
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using an animated Gif on a gui

24 Dec 2020, 05:54

Can't make it working for now. Pephaps you have to destroy the picture every time you want to change gif, and create new one.
User avatar
powbam
Posts: 1
Joined: 03 Mar 2021, 13:26

Re: Using an animated Gif on a gui

03 Mar 2021, 13:53

Hi @teadrinker - so far you are a godsend.. I've been poring over every single AHK gif-related thread but your answers earlier in the thread helped me quite a lot.

I only have one issue tho - is it possible to retain the exStyles (to avoid the crazy flickering) and have my gifs inside of tabs?

Code: Select all

#SingleInstance, Force
SendMode Input
SetWorkingDir, %A_ScriptDir%
#NoEnv
#Include Gdip.ahk
SetBatchLines, -1


filePath := A_ScriptDir "\tst2.gif" ; specify the file path to gif
pToken := Gdip_Startup()
OnExit, Exit

exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%

Gui, Add, Tab3, w580 h585, Main||Custom|Settings
Gui, Tab, 1
Gui, Add, Picture, w300 h450 x80 y80 hwndhwndGif1, % filePath

gif1 := new Gif(filePath, hwndGif1)
gif1.Play()

Gui, Show, w600 h600, Animated gif
return

GuiClose:
   ExitApp

;######################################################

;######################################################

Exit:
Gdip_ShutDown(pToken)
ExitApp
return

;######################################################

class Gif
{   
   __New(file, hwnd, cycle := true)
   {
      this.file := file
      this.hwnd := hwnd
      this.cycle := cycle
      this.pBitmap := Gdip_CreateBitmapFromFile(this.file)
      Gdip_GetImageDimensions(this.pBitmap, width, height)
      this.width := width, this.height := height
      this.isPlaying := false
      
      DllCall("Gdiplus\GdipImageGetFrameDimensionsCount", "ptr", this.pBitmap, "uptr*", frameDimensions)
      this.SetCapacity("dimensionIDs", 16*frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameDimensionsList", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameCount", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int*", count)
      this.frameCount := count
      this.frameCurrent := -1
      this.frameDelay := this.GetFrameDelay(this.pBitmap)
      this._Play("")
   }

   ; Return a zero-based array, containing the frames delay (in milliseconds)
   GetFrameDelay(pImage) {
      static PropertyTagFrameDelay := 0x5100

      DllCall("Gdiplus\GdipGetPropertyItemSize", "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt*", ItemSize)
      VarSetCapacity(Item, ItemSize, 0)
      DllCall("Gdiplus\GdipGetPropertyItem"    , "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt", ItemSize, "Ptr", &Item)

      PropLen := NumGet(Item, 4, "UInt")
      PropVal := NumGet(Item, 8 + A_PtrSize, "UPtr")

      outArray := []
      Loop, % PropLen//4 {
         if !n := NumGet(PropVal+0, (A_Index-1)*4, "UInt")
            n := 10
         outArray[A_Index-1] := n * 10
      }
      return outArray
   }
   
   Play()
   {
      this.isPlaying := true
      fn := this._Play.Bind(this)
      this._fn := fn
      SetTimer, % fn, -1
   }
   
   Pause()
   {
      this.isPlaying := false
      fn := this._fn
      SetTimer, % fn, Delete
   }
   
   _Play(mode := "set")
   {
      this.frameCurrent := mod(++this.frameCurrent, this.frameCount)
      DllCall("Gdiplus\GdipImageSelectActiveFrame", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", this.frameCurrent)
      hBitmap := Gdip_CreateHBITMAPFromBitmap(this.pBitmap)

      ;SetImage(this.hwnd, hBitmap)
      GuiControl,, % this.hwnd, HBITMAP: %hBitmap%

      DeleteObject(hBitmap)
      if (mode = "set" && this.frameCurrent < (this.cycle ? 0xFFFFFFFF : this.frameCount - 1)) {
         fn := this._fn
         SetTimer, % fn, % -1 * this.frameDelay[this.frameCurrent]
      }
   }
   
   __Delete()
   {
      Gdip_DisposeImage(this.pBitmap)
      Object.Delete("dimensionIDs")
   }
}
Edit: I've discovered that Tab2 tabs work with it..

Code: Select all

Gui, Add, Tab2, w580 h585, Main||Custom|Settings
Gui, Tab, 1
Gui, Add, Picture, w300 h450 x80 y80 hwndhwndGif1, % filePath
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using an animated Gif on a gui

03 Mar 2021, 16:34

@powbam
Looks like Tab3 does not support a combination of WS_EX_COMPOSITED and WS_EX_LAYERED. Some of controls do not support them.
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: Using an animated Gif on a gui

19 Sep 2021, 19:30

@teadrinker

The class is beyond my understanding right now. I am including a sample code based on what you posted, my goal is to have the ability to play the full animation just once when the "play once" button is clicked.

Would appreciate any help with this.

Edit: the attached gif looks blank because its white text on transparent background. On the gui it will show because its on a black background

Code: Select all

#NoEnv
;#Include Gdip_All.ahk
#Include Gdip.ahk
SetBatchLines, -1
;filePath := "C:\Users\User\Desktop\welcome.gif" ; specify the file path to gif
filePath := "welcome.gif" ; specify the file path to gif

pToken := Gdip_Startup()
OnExit, Exit

exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
gui, color, black
Gui, Add, Picture, w480 y10 hwndhwndGif1, % filePath
Gui, Add, Button, xp y+10 w80 h24 gPlayPause hwndhwndPlayPause, Play
Gui, Add, Button, xp y+10 w80 h24 gPlayOnce hwndhwndPlayOnce, Play Once


gif1 := new Gif(filePath, hwndGif1)
Gui, Show, AutoSize, Animated gif
return

GuiClose:
   ExitApp
   

;######################################################   
PlayOnce:

return

;######################################################

PlayPause:
isPlaying := gif1.isPlaying
GuiControl,, % hwndPlayPause, % (isPlaying) ? "Play" : "Pause"
if (!isPlaying) {
   gif1.Play()
} else {
   gif1.Pause()
}
return

;######################################################

Exit:
Gdip_ShutDown(pToken)
ExitApp
return

;######################################################

class Gif
{   
   __New(file, hwnd, cycle := true)
   {
      this.file := file
      this.hwnd := hwnd
      this.cycle := cycle
      this.pBitmap := Gdip_CreateBitmapFromFile(this.file)
      Gdip_GetImageDimensions(this.pBitmap, width, height)
      this.width := width, this.height := height
      this.isPlaying := false
      
      DllCall("Gdiplus\GdipImageGetFrameDimensionsCount", "ptr", this.pBitmap, "uptr*", frameDimensions)
      this.SetCapacity("dimensionIDs", 16*frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameDimensionsList", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", frameDimensions)
      DllCall("Gdiplus\GdipImageGetFrameCount", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int*", count)
      this.frameCount := count
      this.frameCurrent := -1
      this.frameDelay := this.GetFrameDelay(this.pBitmap)
      this._Play("")
   }

   ; Return a zero-based array, containing the frames delay (in milliseconds)
   GetFrameDelay(pImage) {
      static PropertyTagFrameDelay := 0x5100

      DllCall("Gdiplus\GdipGetPropertyItemSize", "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt*", ItemSize)
      VarSetCapacity(Item, ItemSize, 0)
      DllCall("Gdiplus\GdipGetPropertyItem"    , "Ptr", pImage, "UInt", PropertyTagFrameDelay, "UInt", ItemSize, "Ptr", &Item)

      PropLen := NumGet(Item, 4, "UInt")
      PropVal := NumGet(Item, 8 + A_PtrSize, "UPtr")

      outArray := []
      Loop, % PropLen//4 {
         if !n := NumGet(PropVal+0, (A_Index-1)*4, "UInt")
            n := 10
         outArray[A_Index-1] := n * 10
      }
      return outArray
   }
   
   Play()
   {
      this.isPlaying := true
      fn := this._Play.Bind(this)
      this._fn := fn
      SetTimer, % fn, -1
   }
   
   Pause()
   {
      this.isPlaying := false
      fn := this._fn
      SetTimer, % fn, Delete
   }
   
   _Play(mode := "set")
   {
      this.frameCurrent := mod(++this.frameCurrent, this.frameCount)
      DllCall("Gdiplus\GdipImageSelectActiveFrame", "ptr", this.pBitmap, "uptr", this.GetAddress("dimensionIDs"), "int", this.frameCurrent)
      hBitmap := Gdip_CreateHBITMAPFromBitmap(this.pBitmap)
      SetImage(this.hwnd, hBitmap)
      DeleteObject(hBitmap)
      if (mode = "set" && this.frameCurrent < (this.cycle ? 0xFFFFFFFF : this.frameCount - 1)) {
         fn := this._fn
         SetTimer, % fn, % -1 * this.frameDelay[this.frameCurrent]
      }
   }
   
   __Delete()
   {
      Gdip_DisposeImage(this.pBitmap)
      Object.Delete("dimensionIDs")
   }
}
Attachments
welcome.gif
welcome.gif (6.7 KiB) Viewed 4671 times
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Using an animated Gif on a gui

20 Sep 2021, 02:36

There is already a new version of the class:

Code: Select all

#NoEnv
SetBatchLines, -1
SetWorkingDir, % A_ScriptDir

if !FileExist("abstract.gif")
   UrlDownloadToFile, https://i.imgur.com/egxGqKK.gif, abstract.gif
filePath := "abstract.gif"

Menu, Tray, Icon, % "HBITMAP:*" . LoadPicture(filePath, "GDI+ w32 h32")
exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles% +hwndhGui
Gui, Add, Picture,      hwndhPic  gOnClick, % "HBITMAP:" . LoadPicture(filePath, "GDI+")
OnMessage(0x20, Func("WM_SETCURSOR").Bind(hPic))
Gui, Add, Button, xp y+10 w75 h24 gOnClick Default, Play
Gui, Add, Button, x+5 yp  wp  hp  gOnClick        , Stop
Gui, Add, Button, x+5 yp  w35 hp  gOnClick        , <
Gui, Add, Button, x+5 yp  wp  hp  gOnClick        , >

UserFunc := Func("PlayGif").Bind(hGui, hPic, FramesCount := [])

MyGif := new AnimateGif(filePath, UserFunc) ; for every frame UserFunc will be called with two params: currentFrameIdx and hBitmap
                                            ; user is responsible for deleting hBitmap
count := FramesCount[1] := MyGif.framesCount
Gui, Show,, % "Frame: " . Format("{:0" . StrLen(count) . "}", 1) . "/" . count
Return

OnClick:
   try GuiControl,, Pause, Play
   Switch A_GuiControl {
      Case "Stop": MyGif.Stop()
      Case   "<" : MyGif.Prev()
      Case   ">" : MyGif.Next()
      Default:
         if MyGif.playing
            MyGif.Pause()
         else {
            GuiControl,, Play, Pause
            MyGif.Play()
         }
   }
   Return

GuiClose:
   ExitApp

PlayGif(hGui, hPic, FramesCount, currentFrameIdx, hBitmap) {
   GuiControl,, % hPic, HBITMAP: %hBitmap%
   count := FramesCount[1]
   frame := Format("{:0" . StrLen(count) . "}", currentFrameIdx)
   Gui, %hGui%: Show, NA, % "Frame: " . frame . "/" . count
}

WM_SETCURSOR(hPic, wp) {
   static hCursor, flags := (LR_DEFAULTSIZE := 0x40) | (LR_SHARED := 0x8000)
        , params := [ "Ptr", 0, "UInt", OCR_HAND := 32649
                              , "UInt", IMAGE_CURSOR := 2
                              , "Int", 0, "Int", 0, "UInt", flags, "Ptr" ]    
   (!hCursor && hCursor := DllCall("LoadImage", params*))
   if (wp = hPic)
      Return DllCall("SetCursor", "Ptr", hCursor)
}

class AnimateGif
{
; UserFunc will be called with two params: currentFrameIdx and hBitmap
; user is responsible for deleting hBitmap
   __New(gifFile, UserFunc := "", cycle := true) {
      this.GDIp := new GDIplus
      this.pBitmap := this.GDIp.CreateBitmapFromFile(gifFile)
      this.Frames := new this._FramesHandling(this.GDIp, this.pBitmap, UserFunc, cycle)
      this.GDIp.GetImageDimensions(this.pBitmap, width, height)
      this.width := width
      this.height := height
   }
   Play() {
      this.playing := true
      this.Frames.PlayFrames()
   }
   Pause() {
      if this.playing {
         this.playing := false
         timer := this.Frames._PlayTimer
         SetTimer, % timer, Delete
      }
   }
   Stop() {
      this.Pause()
      this.ShowFrame(1)
   }
   Prev() {
      this.ShowFrame("prev")
   }
   Next() {
      this.ShowFrame("next")
   }
   ShowFrame(which) {          ; 'which' can be "prev", "next" or "", or 1-based frame index
      this.Pause()
      (which = "prev" && this.Frames.currentFrame -= 2)
      (which + 0 && this.Frames.currentFrame := which - 1)
      this.Frames.PlayFrames()
   }
   GetFrameByIndex(idx) {
      Return hBitmap := this.Frames.GetFrame(idx - 1)
   }
   playing[] {
      get {
         Return this.Frames.playing
      }
      set {
         Return this.Frames.playing := value
      }
   }
   framesCount[] {
      get {
         Return this.Frames.frameCount
      }
   }
   __Delete() {
      this.Frames.Clear()
      this.GDIp.DisposeImage(this.pBitmap)
      this.Delete("Frames")
      this.GDIp.Release()
   }
   
   class _FramesHandling {
      __New(GDIp, pBitmap, userFunc, cycle) {
         this.GDIp := GDIp
         this.pBitmap := pBitmap
         this.userFunc := userFunc
         this.cycle := cycle
         this.GetFrameCount()
         this.GetFrameDelay()
         this._PlayTimer := ObjBindMethod(this, "PlayFrames")
         this._currentFrame := 1
      }
      currentFrame[] {
         get {
            Return this._currentFrame
         }
         set {
            Return this._currentFrame := value < 1 ? this.frameCount + value : value > this.frameCount ? 1 : value
         }
      }
      PlayFrames() {
         Critical
         frameIdx := ++this.currentFrame - 1
         if ( this.playing && this.currentFrame != (this.cycle ? 0xFFFFFFFF : this.frameCount) ) {
            timer := this._PlayTimer
            SetTimer, % timer, % "-" this.frameDelay[frameIdx]
         }
         if userFunc := this.userFunc
            %userFunc%(this.currentFrame, this.GetFrame(frameIdx))
      }
      GetFrameCount() {
         this.frameCount := this.GDIp.GetFrameCount(this.pBitmap, dimensionIDs, size)
         this.SetCapacity("dimensionIDs", size)
         this.pDimensionIDs := this.GetAddress("dimensionIDs")
         DllCall("RtlMoveMemory", "Ptr", this.pDimensionIDs, "Ptr", &dimensionIDs, "Ptr", size)
         VarSetCapacity(dimensionIDs, 0), dimensionIDs := ""
         this.currentFrame := 0
      }
      GetFrameDelay() {
         this.GDIp.GetPropertyItem(this.pBitmap, PropertyTagFrameDelay := 0x5100, item)
         len := NumGet(item, 4, "UInt")
         val := NumGet(item, 8 + A_PtrSize, "UPtr")
         this.frameDelay := []
         Loop, % len//4 {
            i := A_Index - 1
            n := NumGet(val + i*4, "UInt") * 10
            this.frameDelay[i] := n ? n : 100
         }
      }
      GetFrame(idx) {
         this.GDIp.ImageSelectActiveFrame(this.pBitmap, this.pDimensionIDs, idx)
         Return this.GDIp.CreateHBITMAPFromBitmap(this.pBitmap)
      }
      Clear() {
         this.playing := false
         timer := this._PlayTimer
         SetTimer, % timer, Delete
         this.Delete("dimensionIDs")
      }
   }
}

class GDIplus {
   __New() {
      static Instance := ""
      if Instance.references {
         ++Instance.references
         Return Instance
      }
      this.references := 1
      if !DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
         DllCall("LoadLibrary", "Str", "gdiplus")
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "UPtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
      Return Instance := this
   }
   Release() {
      if --this.references
         Return
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      if hModule := DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
         DllCall("FreeLibrary", "Ptr", hModule)
   }
   CreateBitmapFromFile(sFile) {
      DllCall("gdiplus\GdipCreateBitmapFromFile", "WStr", sFile, "PtrP", pBitmap)
      Return pBitmap
   }
   CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) {
      DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "PtrP", hbm, "Int", Background)
      Return hbm
   }
   GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
      DllCall("gdiplus\GdipGetImageWidth", "Ptr", pBitmap, "UIntP", Width)
      DllCall("gdiplus\GdipGetImageHeight", "Ptr", pBitmap, "UIntP", Height)
   }
   GetFrameCount(pBitmap, ByRef dimensionIDs, ByRef size) {
      DllCall("gdiplus\GdipImageGetFrameDimensionsCount", "Ptr", pBitmap, "UIntP", frameDimensions)
      VarSetCapacity(dimensionIDs, size := 16*frameDimensions)
      DllCall("gdiplus\GdipImageGetFrameDimensionsList", "Ptr", pBitmap, "Ptr", &dimensionIDs, "UInt", frameDimensions)
      DllCall("gdiplus\GdipImageGetFrameCount", "Ptr", pBitmap, "Ptr", &dimensionIDs, "UIntP", count)
      Return count
   }
   GetPropertyItem(pBitmap, tag, ByRef item) {
      DllCall("gdiplus\GdipGetPropertyItemSize", "Ptr", pBitmap, "UInt", tag, "UIntP", size)
      VarSetCapacity(item, size, 0)
      DllCall("gdiplus\GdipGetPropertyItem", "Ptr", pBitmap, "UInt", tag, "UInt", size, "Ptr", &item)
      Return size
   }
   ImageSelectActiveFrame(pBitmap, pDimensionIDs, idx) {
      Return DllCall("gdiplus\GdipImageSelectActiveFrame", "Ptr", pBitmap, "Ptr", pDimensionIDs, "Int", idx)
   }
   DisposeImage(pBitmap) {
      Return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
   }
}
For your gif:

Code: Select all

SetBatchLines, -1
gifFile := "welcome.gif"

exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
gui, color, black
Gui, Add, Picture, w480 y10 hwndhwndGif1, % gifFile
Gui, Add, Button, xp y+10 w80 h24 hwndhPlayPause gOnButtonClick, Play
Gui, Add, Button, xp y+10 w80 h24 gOnButtonClick, Play Once

UserFunc := Func("PlayGif").Bind(hwndGif1, hPlayPause, FramesCount := [], PlayOnce := [false])

MyGif := new AnimateGif(gifFile, UserFunc)
FramesCount[1] := MyGif.framesCount

Gui, Show,, Animated gif
Return

OnButtonClick:
   Switch A_GuiControl {
      Case "Play":
         GuiControl,, Play, Pause
         MyGif.Play()
      Case "Pause":
         GuiControl,, Pause, Play
         MyGif.Pause()
      Case "Play Once":
         PlayOnce[1] := true
         if !MyGif.playing {
            GuiControl,, Play, Pause
            MyGif.Play()
         }
   }
   Return

PlayGif(hPic, hPlayPause, FramesCount, PlayOnce, currentFrameIdx, hBitmap) {
   global MyGif
   GuiControl,, % hPic, HBITMAP: %hBitmap%
   if (PlayOnce[1] && currentFrameIdx = FramesCount[1]) {
      MyGif.Stop()
      PlayOnce[1] := false
      GuiControl,, %hPlayPause%, Play
   }
}

GuiClose() {
   ExitApp
}

class AnimateGif
{
; UserFunc will be called with two params: currentFrameIdx and hBitmap
; user is responsible for deleting hBitmap
   __New(gifFile, UserFunc := "", cycle := true) {
      this.GDIp := new GDIplus
      this.pBitmap := this.GDIp.CreateBitmapFromFile(gifFile)
      this.Frames := new this._FramesHandling(this.GDIp, this.pBitmap, UserFunc, cycle)
      this.GDIp.GetImageDimensions(this.pBitmap, width, height)
      this.width := width
      this.height := height
   }
   Play() {
      this.playing := true
      this.Frames.PlayFrames()
   }
   Pause() {
      if this.playing {
         this.playing := false
         timer := this.Frames._PlayTimer
         SetTimer, % timer, Delete
      }
   }
   Stop() {
      this.Pause()
      this.ShowFrame(1)
   }
   Prev() {
      this.ShowFrame("prev")
   }
   Next() {
      this.ShowFrame("next")
   }
   ShowFrame(which) {          ; 'which' can be "prev", "next" or "", or 1-based frame index
      this.Pause()
      (which = "prev" && this.Frames.currentFrame -= 2)
      (which + 0 && this.Frames.currentFrame := which - 1)
      this.Frames.PlayFrames()
   }
   GetFrameByIndex(idx) {
      Return hBitmap := this.Frames.GetFrame(idx - 1)
   }
   playing[] {
      get {
         Return this.Frames.playing
      }
      set {
         Return this.Frames.playing := value
      }
   }
   framesCount[] {
      get {
         Return this.Frames.frameCount
      }
   }
   __Delete() {
      this.Frames.Clear()
      this.GDIp.DisposeImage(this.pBitmap)
      this.Delete("Frames")
      this.GDIp.Release()
   }
   
   class _FramesHandling {
      __New(GDIp, pBitmap, userFunc, cycle) {
         this.GDIp := GDIp
         this.pBitmap := pBitmap
         this.userFunc := userFunc
         this.cycle := cycle
         this.GetFrameCount()
         this.GetFrameDelay()
         this._PlayTimer := ObjBindMethod(this, "PlayFrames")
         this._currentFrame := 1
      }
      currentFrame[] {
         get {
            Return this._currentFrame
         }
         set {
            Return this._currentFrame := value < 1 ? this.frameCount + value : value > this.frameCount ? 1 : value
         }
      }
      PlayFrames() {
         Critical
         frameIdx := ++this.currentFrame - 1
         if ( this.playing && this.currentFrame != (this.cycle ? 0xFFFFFFFF : this.frameCount) ) {
            timer := this._PlayTimer
            SetTimer, % timer, % "-" this.frameDelay[frameIdx]
         }
         if userFunc := this.userFunc
            %userFunc%(this.currentFrame, this.GetFrame(frameIdx))
      }
      GetFrameCount() {
         this.frameCount := this.GDIp.GetFrameCount(this.pBitmap, dimensionIDs, size)
         this.SetCapacity("dimensionIDs", size)
         this.pDimensionIDs := this.GetAddress("dimensionIDs")
         DllCall("RtlMoveMemory", "Ptr", this.pDimensionIDs, "Ptr", &dimensionIDs, "Ptr", size)
         VarSetCapacity(dimensionIDs, 0), dimensionIDs := ""
         this.currentFrame := 0
      }
      GetFrameDelay() {
         this.GDIp.GetPropertyItem(this.pBitmap, PropertyTagFrameDelay := 0x5100, item)
         len := NumGet(item, 4, "UInt")
         val := NumGet(item, 8 + A_PtrSize, "UPtr")
         this.frameDelay := []
         Loop, % len//4 {
            i := A_Index - 1
            n := NumGet(val + i*4, "UInt") * 10
            this.frameDelay[i] := n ? n : 100
         }
      }
      GetFrame(idx) {
         this.GDIp.ImageSelectActiveFrame(this.pBitmap, this.pDimensionIDs, idx)
         Return this.GDIp.CreateHBITMAPFromBitmap(this.pBitmap)
      }
      Clear() {
         this.playing := false
         timer := this._PlayTimer
         SetTimer, % timer, Delete
         this.Delete("dimensionIDs")
      }
   }
}

class GDIplus {
   __New() {
      static Instance := ""
      if Instance.references {
         ++Instance.references
         Return Instance
      }
      this.references := 1
      if !DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
         DllCall("LoadLibrary", "Str", "gdiplus")
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "UPtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
      Return Instance := this
   }
   Release() {
      if --this.references
         Return
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      if hModule := DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
         DllCall("FreeLibrary", "Ptr", hModule)
   }
   CreateBitmapFromFile(sFile) {
      DllCall("gdiplus\GdipCreateBitmapFromFile", "WStr", sFile, "PtrP", pBitmap)
      Return pBitmap
   }
   CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) {
      DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "PtrP", hbm, "Int", Background)
      Return hbm
   }
   GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
      DllCall("gdiplus\GdipGetImageWidth", "Ptr", pBitmap, "UIntP", Width)
      DllCall("gdiplus\GdipGetImageHeight", "Ptr", pBitmap, "UIntP", Height)
   }
   GetFrameCount(pBitmap, ByRef dimensionIDs, ByRef size) {
      DllCall("gdiplus\GdipImageGetFrameDimensionsCount", "Ptr", pBitmap, "UIntP", frameDimensions)
      VarSetCapacity(dimensionIDs, size := 16*frameDimensions)
      DllCall("gdiplus\GdipImageGetFrameDimensionsList", "Ptr", pBitmap, "Ptr", &dimensionIDs, "UInt", frameDimensions)
      DllCall("gdiplus\GdipImageGetFrameCount", "Ptr", pBitmap, "Ptr", &dimensionIDs, "UIntP", count)
      Return count
   }
   GetPropertyItem(pBitmap, tag, ByRef item) {
      DllCall("gdiplus\GdipGetPropertyItemSize", "Ptr", pBitmap, "UInt", tag, "UIntP", size)
      VarSetCapacity(item, size, 0)
      DllCall("gdiplus\GdipGetPropertyItem", "Ptr", pBitmap, "UInt", tag, "UInt", size, "Ptr", &item)
      Return size
   }
   ImageSelectActiveFrame(pBitmap, pDimensionIDs, idx) {
      Return DllCall("gdiplus\GdipImageSelectActiveFrame", "Ptr", pBitmap, "Ptr", pDimensionIDs, "Int", idx)
   }
   DisposeImage(pBitmap) {
      Return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
   }
}
However, if you always only need to play your gif once, just set the cycle parameter to false:

Code: Select all

SetBatchLines, -1
gifFile := "welcome.gif"

exStyles := (WS_EX_COMPOSITED := 0x02000000) | (WS_EX_LAYERED := 0x80000)
Gui, New, +E%exStyles%
gui, color, black
Gui, Add, Picture, w480 y10 hwndhwndGif1, % gifFile
Gui, Add, Button, xp y+10 w80 h24 gPlay, Play Once

UserFunc := Func("PlayGif").Bind(hwndGif1)
MyGif := new AnimateGif(gifFile, UserFunc, false)

Gui, Show,, Animated gif
Return

Play:
   MyGif.Play()
   Return

PlayGif(hPic, currentFrameIdx, hBitmap) {
   GuiControl,, % hPic, HBITMAP: %hBitmap%
}

GuiClose() {
   ExitApp
}

class AnimateGif
{
; UserFunc will be called with two params: currentFrameIdx and hBitmap
; user is responsible for deleting hBitmap
   __New(gifFile, UserFunc := "", cycle := true) {
      this.GDIp := new GDIplus
      this.pBitmap := this.GDIp.CreateBitmapFromFile(gifFile)
      this.Frames := new this._FramesHandling(this.GDIp, this.pBitmap, UserFunc, cycle)
      this.GDIp.GetImageDimensions(this.pBitmap, width, height)
      this.width := width
      this.height := height
   }
   Play() {
      this.playing := true
      this.Frames.PlayFrames()
   }
   Pause() {
      if this.playing {
         this.playing := false
         timer := this.Frames._PlayTimer
         SetTimer, % timer, Delete
      }
   }
   Stop() {
      this.Pause()
      this.ShowFrame(1)
   }
   Prev() {
      this.ShowFrame("prev")
   }
   Next() {
      this.ShowFrame("next")
   }
   ShowFrame(which) {          ; 'which' can be "prev", "next" or "", or 1-based frame index
      this.Pause()
      (which = "prev" && this.Frames.currentFrame -= 2)
      (which + 0 && this.Frames.currentFrame := which - 1)
      this.Frames.PlayFrames()
   }
   GetFrameByIndex(idx) {
      Return hBitmap := this.Frames.GetFrame(idx - 1)
   }
   playing[] {
      get {
         Return this.Frames.playing
      }
      set {
         Return this.Frames.playing := value
      }
   }
   framesCount[] {
      get {
         Return this.Frames.frameCount
      }
   }
   __Delete() {
      this.Frames.Clear()
      this.GDIp.DisposeImage(this.pBitmap)
      this.Delete("Frames")
      this.GDIp.Release()
   }
   
   class _FramesHandling {
      __New(GDIp, pBitmap, userFunc, cycle) {
         this.GDIp := GDIp
         this.pBitmap := pBitmap
         this.userFunc := userFunc
         this.cycle := cycle
         this.GetFrameCount()
         this.GetFrameDelay()
         this._PlayTimer := ObjBindMethod(this, "PlayFrames")
         this._currentFrame := 1
      }
      currentFrame[] {
         get {
            Return this._currentFrame
         }
         set {
            Return this._currentFrame := value < 1 ? this.frameCount + value : value > this.frameCount ? 1 : value
         }
      }
      PlayFrames() {
         Critical
         frameIdx := ++this.currentFrame - 1
         if ( this.playing && this.currentFrame != (this.cycle ? 0xFFFFFFFF : this.frameCount) ) {
            timer := this._PlayTimer
            SetTimer, % timer, % "-" this.frameDelay[frameIdx]
         }
         if userFunc := this.userFunc
            %userFunc%(this.currentFrame, this.GetFrame(frameIdx))
      }
      GetFrameCount() {
         this.frameCount := this.GDIp.GetFrameCount(this.pBitmap, dimensionIDs, size)
         this.SetCapacity("dimensionIDs", size)
         this.pDimensionIDs := this.GetAddress("dimensionIDs")
         DllCall("RtlMoveMemory", "Ptr", this.pDimensionIDs, "Ptr", &dimensionIDs, "Ptr", size)
         VarSetCapacity(dimensionIDs, 0), dimensionIDs := ""
         this.currentFrame := 0
      }
      GetFrameDelay() {
         this.GDIp.GetPropertyItem(this.pBitmap, PropertyTagFrameDelay := 0x5100, item)
         len := NumGet(item, 4, "UInt")
         val := NumGet(item, 8 + A_PtrSize, "UPtr")
         this.frameDelay := []
         Loop, % len//4 {
            i := A_Index - 1
            n := NumGet(val + i*4, "UInt") * 10
            this.frameDelay[i] := n ? n : 100
         }
      }
      GetFrame(idx) {
         this.GDIp.ImageSelectActiveFrame(this.pBitmap, this.pDimensionIDs, idx)
         Return this.GDIp.CreateHBITMAPFromBitmap(this.pBitmap)
      }
      Clear() {
         this.playing := false
         timer := this._PlayTimer
         SetTimer, % timer, Delete
         this.Delete("dimensionIDs")
      }
   }
}

class GDIplus {
   __New() {
      static Instance := ""
      if Instance.references {
         ++Instance.references
         Return Instance
      }
      this.references := 1
      if !DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
         DllCall("LoadLibrary", "Str", "gdiplus")
      VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
      DllCall("gdiplus\GdiplusStartup", "UPtrP", pToken, "Ptr", &si, "Ptr", 0)
      this.token := pToken
      Return Instance := this
   }
   Release() {
      if --this.references
         Return
      DllCall("gdiplus\GdiplusShutdown", "Ptr", this.token)
      if hModule := DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
         DllCall("FreeLibrary", "Ptr", hModule)
   }
   CreateBitmapFromFile(sFile) {
      DllCall("gdiplus\GdipCreateBitmapFromFile", "WStr", sFile, "PtrP", pBitmap)
      Return pBitmap
   }
   CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) {
      DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "PtrP", hbm, "Int", Background)
      Return hbm
   }
   GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
      DllCall("gdiplus\GdipGetImageWidth", "Ptr", pBitmap, "UIntP", Width)
      DllCall("gdiplus\GdipGetImageHeight", "Ptr", pBitmap, "UIntP", Height)
   }
   GetFrameCount(pBitmap, ByRef dimensionIDs, ByRef size) {
      DllCall("gdiplus\GdipImageGetFrameDimensionsCount", "Ptr", pBitmap, "UIntP", frameDimensions)
      VarSetCapacity(dimensionIDs, size := 16*frameDimensions)
      DllCall("gdiplus\GdipImageGetFrameDimensionsList", "Ptr", pBitmap, "Ptr", &dimensionIDs, "UInt", frameDimensions)
      DllCall("gdiplus\GdipImageGetFrameCount", "Ptr", pBitmap, "Ptr", &dimensionIDs, "UIntP", count)
      Return count
   }
   GetPropertyItem(pBitmap, tag, ByRef item) {
      DllCall("gdiplus\GdipGetPropertyItemSize", "Ptr", pBitmap, "UInt", tag, "UIntP", size)
      VarSetCapacity(item, size, 0)
      DllCall("gdiplus\GdipGetPropertyItem", "Ptr", pBitmap, "UInt", tag, "UInt", size, "Ptr", &item)
      Return size
   }
   ImageSelectActiveFrame(pBitmap, pDimensionIDs, idx) {
      Return DllCall("gdiplus\GdipImageSelectActiveFrame", "Ptr", pBitmap, "Ptr", pDimensionIDs, "Int", idx)
   }
   DisposeImage(pBitmap) {
      Return DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
   }
}
Last edited by teadrinker on 20 Sep 2021, 06:23, edited 1 time in total.
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: Using an animated Gif on a gui

20 Sep 2021, 03:28

@teadrinker

thank you so much!!!

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Google [Bot], jrachr, sbrady19 and 122 guests