Jump to content

Animated Gif using GDI+


smashly
 Share

Recommended Posts

For some intresting reading of fixes, updates and corrections in running animated gifs with gdi then check out ProgAndy's handy work further down the thread.

For those that are using Zedna's Resources.au3 then jump to this post for more info on running animated gifs from a resource once again skip further down the thread.

Or just jump straight to Zedna's Resources thread and get the latest news on his awesome work. Here

Edited by smashly
Link to comment
Share on other sites

This is so weird, I was just reading the documentation on msdn for using animated gifs in GDI+, luckily I didn't start yet :P

Anyways, this is good job and this is a definite keeper for future use :P

Good job!

Broken link? PM me and I'll send you the file!

Link to comment
Share on other sites

Jesus. Oh jesus. JESUS JESUS JESUS JESUS. This is perfect. This solves so many problems. OMG I ****ING LOVE YOU. It means I'll have to rewrite a little but other than that this IS PERFECT. I knew I should have done some research into GDI.

Edit:

Could you help me out some? I havn't ever really done any work with GDI and I'm scared of breaking your script. Could you forge it into function form right quick for me? Shouldn't take to long. Just a simple function that creates the gif then returns the handle. Also one to stop/start it. Like, I will bear your children if you do this for me. This has me really excited in case you didn't notice.

Edited by BillLuvsU

[center][/center]Working on the next big thing.Currently Playing: Halo 4, League of LegendsXBL GT: iRememberYhslaw

Link to comment
Share on other sites

Could you help me out some? I havn't ever really done any work with GDI and I'm scared of breaking your script. Could you forge it into function form right quick for me? Shouldn't take to long. Just a simple function that creates the gif then returns the handle. Also one to stop/start it. Like, I will bear your children if you do this for me. This has me really excited in case you didn't notice.

Here is changed original script.

You can Pause/unPause animation, in Pause mode you can navigate through frames by Left/Right

Note: Moving of window is intentionally disabled in this example --> you can uncomment AdlibEnable("FlyUfo", 20) 

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
#include <Misc.au3>
#include <WinAPI.au3>

HotKeySet("{Esc}", "Quit")
HotKeySet("{Left}", "Left")
HotKeySet("{Right}", "Right")
HotKeySet("{Pause}", "Pause")

Global $Gif = @ScriptDir & "\gif-Green-UFO.gif"
Global $i = 0, $ApX, $ApY, $ApW, $ApH, $eX, $eY
Global $hImage, $hBitmap, $hScrDC, $hMemDC, $tSize, $pSize, $tSource, $pSource, $tBlend, $pBlend
Global $GFC, $GFDC, $pDimensionIDs, $tDL
Global $Pause

$hGUI = GUICreate("", 0, 0, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
GUISetState()

GifInit()

; move the window with animated GIF image
;~ AdlibEnable("FlyUfo", 20) 

While 1
    ; If $i = the frame count then reset $i to 0
    If $i = $GFC Then $i = 0

 GifDrawFrame($i)
 
    ; By rights I should be using the const PropertyTagFrameDelay and geting the properties time delay between frames.
    ; I cheated , so shoot me...lol
    Sleep(100)

    $i += 1
WEnd

Func Quit()
 ;Tidy Up at end of each frame, otherwise memory use gets excessive in a short time.
 _GDIPlus_ImageDispose($hImage)
 _WinAPI_ReleaseDC(0, $hScrDC)
 _WinAPI_DeleteObject($hBitmap)
 _WinAPI_DeleteDC($hMemDC)
 _GDIPlus_Shutdown()

 HotKeySet("{Esc}")
 HotKeySet("{Left}")
 HotKeySet("{Right}")
 HotKeySet("{Pause}")
 
 Exit
EndFunc

Func GifInit()
 _GDIPlus_Startup()
 ; Load your animated gif
 $hImage = _GDIPlus_ImageLoadFromFile($Gif)
 $ApW = _GDIPlus_ImageGetWidth($hImage)
 $ApH = _GDIPlus_ImageGetHeight($hImage)

 $hScrDC = _WinAPI_GetDC($hGUI)
 $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)

 $tSize = DllStructCreate($tagSIZE)
 DllStructSetData($tSize, "X", $ApW)
 DllStructSetData($tSize, "Y", $ApH)
 $pSize = DllStructGetPtr($tSize)

 $tSource = DllStructCreate($tagPOINT)
 $pSource = DllStructGetPtr($tSource)

 $tBlend = DllStructCreate($tagBLENDFUNCTION)
 DllStructSetData($tBlend, "Alpha", 255)
 DllStructSetData($tBlend, "Format", 1)
 $pBlend = DllStructGetPtr($tBlend)

 ; Create a struct to hold the GUID.
 $tDL = DllStructCreate($tagGUID)

 ; Get a pointer to the GUID struct.
 $pDimensionIDs = DllStructGetPtr($tDL)

 ; Get the FrameDimensionsCount of the loaded gif
 $GFDC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsCount", "ptr", $hImage, "int*", 0)

 ; Get the FrameDimensionsList , which fills the GUID struct by passing the GUID pointer and the FrameDimensionsCount.
 DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsList", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $GFDC[2])

 ; Get the FrameCount of the loaded gif by passing the GUID pointer
 $GFC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameCount", "int", $hImage, "ptr", $pDimensionIDs, "int*", 0)
 $GFC = $GFC[3]
EndFunc    

Func GifDrawFrame($i)
    ; Select the ActiveFrame in the loaded gif by telling it. The frame starts @ 0 ($i)
    DllCall($ghGDIPDll, "int", "GdipImageSelectActiveFrame", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $i)

    ; The rest is just a copy and paste on updating a layered window with the loaded gif.
    ; I tried using _GDIPlus_GraphicsDrawImage() but no matter what I did memory was getting chewed up. leak maybe?
    ; But using the Update layered window and memory use seems to level out and sit pretty idle 
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)

    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
EndFunc

; move the window with animated GIF image
Func FlyUfo() 
    Local $Speed = 0
    Do
        If $ApX >= (@DesktopWidth - $ApW) Then
            $eX = "$ApX - 1"
        ElseIf $ApX <= 0 Then
            $eX = "$ApX + 1"
        EndIf
        If $ApY >= (@DesktopHeight - $ApH) Then
            $eY = "$ApY - 1"
        ElseIf $ApY <= 0 Then
            $eY = "$ApY + 1"
        EndIf
        $ApX = Execute($eX)
        $ApY = Execute($eY)
        WinMove($hGUI, "", $ApX, $ApY)
        $Speed += 1
    Until $Speed = 5
EndFunc   ;==>FlyUfo

Func Left() 
 If WinActive($hGUI) Then
    If Not $Pause Then Return
    $i -= 1
    If $i = -1 Then $i = $GFC - 1
    GifDrawFrame($i)
 Else
    HotKeySet("{Left}")
    Send("{Left}")
    HotKeySet("{Left}", "Left")
 EndIf
EndFunc

Func Right() 
 If WinActive($hGUI) Then
    If Not $Pause Then Return
    $i += 1
    If $i = $GFC Then $i = 0
    GifDrawFrame($i)
 Else
    HotKeySet("{Right}")
    Send("{Right}")
    HotKeySet("{Right}", "Right")
 EndIf
EndFunc

Func Pause()
 If WinActive($hGUI) Then
    $Pause = Not $Pause
    If Not $Pause Then Return
     
;~      WinSetTitle($hGUI, '', $Gif & " PAUSED")
    While $Pause
        Sleep(100)
    WEnd
;~      WinSetTitle($hGUI, '', $Gif)
 Else
    HotKeySet("{PAUSE}")
    Send("{PAUSE}")
    HotKeySet("{PAUSE}", "Pause")
 EndIf
EndFunc
Link to comment
Share on other sites

Ok, thankyou very much Zedna. Your example helps me understand this alot better. I know enough now to bend it to my will.

[center][/center]Working on the next big thing.Currently Playing: Halo 4, League of LegendsXBL GT: iRememberYhslaw

Link to comment
Share on other sites

Here is changed original script.

You can Pause/unPause animation, in Pause mode you can navigate through frames by Left/Right

Note: Moving of window is intentionally disabled in this example --> you can uncomment AdlibEnable("FlyUfo", 20) 

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
#include <Misc.au3>
#include <WinAPI.au3>

HotKeySet("{Esc}", "Quit")
HotKeySet("{Left}", "Left")
HotKeySet("{Right}", "Right")
HotKeySet("{Pause}", "Pause")

Global $Gif = @ScriptDir & "\gif-Green-UFO.gif"
Global $i = 0, $ApX, $ApY, $ApW, $ApH, $eX, $eY
Global $hImage, $hBitmap, $hScrDC, $hMemDC, $tSize, $pSize, $tSource, $pSource, $tBlend, $pBlend
Global $GFC, $GFDC, $pDimensionIDs, $tDL
Global $Pause

$hGUI = GUICreate("", 0, 0, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
GUISetState()

GifInit()

; move the window with animated GIF image
;~ AdlibEnable("FlyUfo", 20) 

While 1
    ; If $i = the frame count then reset $i to 0
    If $i = $GFC Then $i = 0

 GifDrawFrame($i)
 
    ; By rights I should be using the const PropertyTagFrameDelay and geting the properties time delay between frames.
    ; I cheated , so shoot me...lol
    Sleep(100)

    $i += 1
WEnd

Func Quit()
 ;Tidy Up at end of each frame, otherwise memory use gets excessive in a short time.
 _GDIPlus_ImageDispose($hImage)
 _WinAPI_ReleaseDC(0, $hScrDC)
 _WinAPI_DeleteObject($hBitmap)
 _WinAPI_DeleteDC($hMemDC)
 _GDIPlus_Shutdown()

 HotKeySet("{Esc}")
 HotKeySet("{Left}")
 HotKeySet("{Right}")
 HotKeySet("{Pause}")
 
 Exit
EndFunc

Func GifInit()
 _GDIPlus_Startup()
 ; Load your animated gif
 $hImage = _GDIPlus_ImageLoadFromFile($Gif)
 $ApW = _GDIPlus_ImageGetWidth($hImage)
 $ApH = _GDIPlus_ImageGetHeight($hImage)

 $hScrDC = _WinAPI_GetDC($hGUI)
 $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)

 $tSize = DllStructCreate($tagSIZE)
 DllStructSetData($tSize, "X", $ApW)
 DllStructSetData($tSize, "Y", $ApH)
 $pSize = DllStructGetPtr($tSize)

 $tSource = DllStructCreate($tagPOINT)
 $pSource = DllStructGetPtr($tSource)

 $tBlend = DllStructCreate($tagBLENDFUNCTION)
 DllStructSetData($tBlend, "Alpha", 255)
 DllStructSetData($tBlend, "Format", 1)
 $pBlend = DllStructGetPtr($tBlend)

 ; Create a struct to hold the GUID.
 $tDL = DllStructCreate($tagGUID)

 ; Get a pointer to the GUID struct.
 $pDimensionIDs = DllStructGetPtr($tDL)

 ; Get the FrameDimensionsCount of the loaded gif
 $GFDC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsCount", "ptr", $hImage, "int*", 0)

 ; Get the FrameDimensionsList , which fills the GUID struct by passing the GUID pointer and the FrameDimensionsCount.
 DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsList", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $GFDC[2])

 ; Get the FrameCount of the loaded gif by passing the GUID pointer
 $GFC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameCount", "int", $hImage, "ptr", $pDimensionIDs, "int*", 0)
 $GFC = $GFC[3]
EndFunc    

Func GifDrawFrame($i)
    ; Select the ActiveFrame in the loaded gif by telling it. The frame starts @ 0 ($i)
    DllCall($ghGDIPDll, "int", "GdipImageSelectActiveFrame", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $i)

    ; The rest is just a copy and paste on updating a layered window with the loaded gif.
    ; I tried using _GDIPlus_GraphicsDrawImage() but no matter what I did memory was getting chewed up. leak maybe?
    ; But using the Update layered window and memory use seems to level out and sit pretty idle 
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)

    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
EndFunc

; move the window with animated GIF image
Func FlyUfo() 
    Local $Speed = 0
    Do
        If $ApX >= (@DesktopWidth - $ApW) Then
            $eX = "$ApX - 1"
        ElseIf $ApX <= 0 Then
            $eX = "$ApX + 1"
        EndIf
        If $ApY >= (@DesktopHeight - $ApH) Then
            $eY = "$ApY - 1"
        ElseIf $ApY <= 0 Then
            $eY = "$ApY + 1"
        EndIf
        $ApX = Execute($eX)
        $ApY = Execute($eY)
        WinMove($hGUI, "", $ApX, $ApY)
        $Speed += 1
    Until $Speed = 5
EndFunc   ;==>FlyUfo

Func Left() 
 If WinActive($hGUI) Then
    If Not $Pause Then Return
    $i -= 1
    If $i = -1 Then $i = $GFC - 1
    GifDrawFrame($i)
 Else
    HotKeySet("{Left}")
    Send("{Left}")
    HotKeySet("{Left}", "Left")
 EndIf
EndFunc

Func Right() 
 If WinActive($hGUI) Then
    If Not $Pause Then Return
    $i += 1
    If $i = $GFC Then $i = 0
    GifDrawFrame($i)
 Else
    HotKeySet("{Right}")
    Send("{Right}")
    HotKeySet("{Right}", "Right")
 EndIf
EndFunc

Func Pause()
 If WinActive($hGUI) Then
    $Pause = Not $Pause
    If Not $Pause Then Return
     
;~      WinSetTitle($hGUI, '', $Gif & " PAUSED")
    While $Pause
        Sleep(100)
    WEnd
;~      WinSetTitle($hGUI, '', $Gif)
 Else
    HotKeySet("{PAUSE}")
    Send("{PAUSE}")
    HotKeySet("{PAUSE}", "Pause")
 EndIf
EndFunc
Thank You.. saves me having to do it :P

I was actually writing a simple interface to create/add/remove/extract frames to and from a gif, so I didn't really feel like writing another breakdown.

You can actually use any format input images and it'll create a multi page gif.

But saving with a selected transparency is little bamboozling, it's like gdi+ save image refuses to save an image without screwing with the color palete.

I think I have to save the image into a stream, then set the pixels in the stream, then get a pointer to the stream and save the data from the stream to a file. urg, ms blows when it comes to ease of use in some functions!

Bit more research needed for me to finalize it.

Cheers

Link to comment
Share on other sites

Thank You.. saves me having to do it :P

I was actually writing a simple interface to create/add/remove/extract frames to and from a gif, so I didn't really feel like writing another breakdown.

You can actually use any format input images and it'll create a multi page gif.

But saving with a selected transparency is little bamboozling, it's like gdi+ save image refuses to save an image without screwing with the color palete.

I think I have to save the image into a stream, then set the pixels in the stream, then get a pointer to the stream and save the data from the stream to a file. urg, ms blows when it comes to ease of use in some functions!

Bit more research needed for me to finalize it.

Cheers

I like this your GDI+ GIF animation observation :-)

But I have no experiences with images transparency at all.

Link to comment
Share on other sites

Greate Work, smashly and Zedna :P I added handling for the Properties FrameDelay and LoopCount

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
#include <Misc.au3>
#include <WinAPI.au3>

HotKeySet("{Esc}", "Quit")
HotKeySet("{Left}", "Left")
HotKeySet("{Right}", "Right")
HotKeySet("{Pause}", "Pause")

Global $Gif = @ScriptDir & "\gif-Green-UFO.gif"
Global $i = 0, $ApX, $ApY, $ApW, $ApH, $eX, $eY
Global $hImage, $hBitmap, $hScrDC, $hMemDC, $tSize, $pSize, $tSource, $pSource, $tBlend, $pBlend
Global $GFC, $GFDC, $pDimensionIDs, $tDL
Global $Pause

Global $MadeLoops, $LoopCount
Global $FrameDelays


Global Const $tagPropertyItem = "long id; long length; int Type; ptr value"
Global Const $PropertyTagFrameDelay = 0x5100
Global Const $PropertyTagLoopCount = 0x5101

Global Const $PropertyTagTypeByte = 1
Global Const $PropertyTagTypeASCII = 2
Global Const $PropertyTagTypeShort = 3
Global Const $PropertyTagTypeLong = 4
Global Const $PropertyTagTypeRational = 5
Global Const $PropertyTagTypeUndefined = 7
Global Const $PropertyTagTypeSLong = 9
Global Const $PropertyTagTypeSRational = 10

$hGUI = GUICreate("", 0, 0, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
GUISetState()

GifInit()

; move the window with animated GIF image
;~ AdlibEnable("FlyUfo", 20)

While 1
    If $LoopCount = 0 Or ($LoopCount > $MadeLoops) Then
        ; If $i = the frame count then reset $i to 0
        If $i = $GFC Then $i = 0
        GifDrawFrame($i)
        If UBound($FrameDelays) > $i And $FrameDelays[$i] > 0 Then
            Sleep($FrameDelays[$i]) ; Sleep for Frame Delay :)
        Else
            Sleep(100)
            ConsoleWrite("Fallback, no time found :)" & @CRLF)
        EndIf
        $i += 1
    Else
        Sleep(100)
    EndIf
WEnd

Func Quit()
    ;Tidy Up at end of each frame, otherwise memory use gets excessive in a short time.
    _GDIPlus_ImageDispose($hImage)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_DeleteDC($hMemDC)
    _GDIPlus_Shutdown()

    HotKeySet("{Esc}")
    HotKeySet("{Left}")
    HotKeySet("{Right}")
    HotKeySet("{Pause}")

    Exit
EndFunc   ;==>Quit

Func GifInit()
    _GDIPlus_Startup()
    ; Load your animated gif
    $hImage = _GDIPlus_ImageLoadFromFile($Gif)
    $ApW = _GDIPlus_ImageGetWidth($hImage)
    $ApH = _GDIPlus_ImageGetHeight($hImage)

    $hScrDC = _WinAPI_GetDC($hGUI)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)

    $tSize = DllStructCreate($tagSIZE)
    DllStructSetData($tSize, "X", $ApW)
    DllStructSetData($tSize, "Y", $ApH)
    $pSize = DllStructGetPtr($tSize)

    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)

    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    DllStructSetData($tBlend, "Alpha", 255)
    DllStructSetData($tBlend, "Format", 1)
    $pBlend = DllStructGetPtr($tBlend)

    ; Create a struct to hold the GUID.
    $tDL = DllStructCreate($tagGUID)

    ; Get a pointer to the GUID struct.
    $pDimensionIDs = DllStructGetPtr($tDL)

    ; Get the FrameDimensionsCount of the loaded gif
    $GFDC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsCount", "ptr", $hImage, "int*", 0)

    ; Get the FrameDimensionsList , which fills the GUID struct by passing the GUID pointer and the FrameDimensionsCount.
    DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsList", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $GFDC[2])

    ; Get the FrameCount of the loaded gif by passing the GUID pointer
    $GFC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameCount", "int", $hImage, "ptr", $pDimensionIDs, "int*", 0)
    $GFC = $GFC[3]
    $FrameDelays = _GetFrameDelays($hImage, $GFC)
    $LoopCount = _GetGifLoopCount($hImage)
EndFunc   ;==>GifInit

Func GifDrawFrame($i)
    ; Select the ActiveFrame in the loaded gif by telling it. The frame starts @ 0 ($i)
    DllCall($ghGDIPDll, "int", "GdipImageSelectActiveFrame", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $i)

    ; The rest is just a copy and paste on updating a layered window with the loaded gif.
    ; I tried using _GDIPlus_GraphicsDrawImage() but no matter what I did memory was getting chewed up. leak maybe?
    ; But using the Update layered window and memory use seems to level out and sit pretty idle
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)

    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap) ; Needs to be deleted after each creation, not just in the end once :P
EndFunc   ;==>GifDrawFrame

; move the window with animated GIF image
Func FlyUfo()
    Local $Speed = 0
    Do
        If $ApX >= (@DesktopWidth - $ApW) Then
            $eX = "$ApX - 1"
        ElseIf $ApX <= 0 Then
            $eX = "$ApX + 1"
        EndIf
        If $ApY >= (@DesktopHeight - $ApH) Then
            $eY = "$ApY - 1"
        ElseIf $ApY <= 0 Then
            $eY = "$ApY + 1"
        EndIf
        $ApX = Execute($eX)
        $ApY = Execute($eY)
        WinMove($hGUI, "", $ApX, $ApY)
        $Speed += 1
    Until $Speed = 5
EndFunc   ;==>FlyUfo

Func Left()
    If WinActive($hGUI) Then
        If Not $Pause Then Return
        $i -= 1
        If $i = -1 Then $i = $GFC - 1
        GifDrawFrame($i)
    Else
        HotKeySet("{Left}")
        Send("{Left}")
        HotKeySet("{Left}", "Left")
    EndIf
EndFunc   ;==>Left

Func Right()
    If WinActive($hGUI) Then
        If Not $Pause Then Return
        $i += 1
        If $i = $GFC Then $i = 0
        GifDrawFrame($i)
    Else
        HotKeySet("{Right}")
        Send("{Right}")
        HotKeySet("{Right}", "Right")
    EndIf
EndFunc   ;==>Right

Func Pause()
    If WinActive($hGUI) Then
        $Pause = Not $Pause
        If Not $Pause Then Return

;~   WinSetTitle($hGUI, '', $Gif & " PAUSED")
        While $Pause
            Sleep(100)
        WEnd
;~   WinSetTitle($hGUI, '', $Gif)
    Else
        HotKeySet("{PAUSE}")
        Send("{PAUSE}")
        HotKeySet("{PAUSE}", "Pause")
    EndIf
EndFunc   ;==>Pause

; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
Func _GetFrameDelays(ByRef $hImage, $FrameCount)
    
    Local $lOutFrameDelay[$FrameCount]
    

;~  Dim lProp() As Byte
;~  Dim lPropCount As Long
    Local $lSize ;As Long
;~  Dim lPropSize As Long
;~  Dim tPropItem As PropertyItem
    Local $PropID = $PropertyTagFrameDelay
    If Not _CheckForProperty($hImage, $PropID) Then Return SetError(1, 0, 0)
    Local $PropertySize = DllCall($ghGDIPDll, "int", "GdipGetPropertyItemSize", "ptr", $hImage, "dword", $PropID, "uint*", 0)

;~     ' Datengröße vom EXIF-Tag
;~     ' "PropertyTagFrameDelay" ermitteln
    If $PropertySize[0] = 0 Then
        $tPropItem = DllStructCreate($tagPropertyItem & ";byte[" & $PropertySize[3] & "]"); Create Property-ItemStruct and save enough memory for its data
        Local $PropertyItem = DllCall($ghGDIPDll, "int", "GdipGetPropertyItem", "ptr", $hImage, "dword", $PropID, "dword", $PropertySize[3], "ptr", DllStructGetPtr($tPropItem))
        If $PropertyItem[0] <> 0 Then Return SetError(1, 0, 0)
        $lSize = DllStructGetData($tPropItem, "length")
;~          ' PropertyTyp ermitteln
        Switch DllStructGetData($tPropItem, "Type")
            Case $PropertyTagTypeByte
                $lProp = DllStructCreate("byte[" & $lSize & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "byte"
                ConsoleWrite("byte" & @CRLF)
            Case $PropertyTagTypeShort
                $lProp = DllStructCreate("short[" & Ceiling($lSize / 2) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "ushort"
                ConsoleWrite("short" & @CRLF)

            Case $PropertyTagTypeLong
                $lProp = DllStructCreate("long[" & Ceiling($lSize / 4) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "long"
                ConsoleWrite("long" & @CRLF)
        EndSwitch

;~                  ' Pausenzeiten der einzelnen
;~                  ' Bilder aus Bytearray kopieren
        For $lPropCount = 0 To $FrameCount - 1
            $lOutFrameDelay[$lPropCount] = DllStructGetData($lProp, 1, $lPropCount + 1) * 10


        Next
    EndIf
;~  _MemGlobalFree(DllStructGetData($tPropItem,"value"))
;~  _MemVirtualFree(DllStructGetData($tPropItem,"value"),$lSize,$MEM_RELEASE)
    Return $lOutFrameDelay
EndFunc   ;==>_GetFrameDelays

; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
;~ '------------------------------------------------------
;~ ' Funktion     : GetGifLoopCount
;~ ' Beschreibung : Auslesen der Wiederholungen
;~ ' Übergabewert : lInBitmap = GDI+ Bitmapobjekt
;~ '                lOutLoopCount = Anzahl der Wiederholungen
;~ '------------------------------------------------------
Func _GetGifLoopCount(ByRef $hImage)

    Local $tPropItem
    Local $lProp
    Local $lSize

;~     ' ist der EXIF-Tag
;~     ' "PropertyTagLoopCount" vorhanden
    If _CheckForProperty($hImage, _
            $PropertyTagLoopCount) = True Then

;~     ' Datengröße vom EXIF-Tag
;~     ' "PropertyTagLoopCount" ermitteln
        Local $PropertySize = DllCall($ghGDIPDll, "int", "GdipGetPropertyItemSize", "ptr", $hImage, "dword", $PropertyTagLoopCount, "uint*", 0)
        If $PropertySize[0] = 0 Then

;~             ' Daten auslesen
            
            $tPropItem = DllStructCreate($tagPropertyItem & ";byte[" & $PropertySize[3] & "]"); Create Property-ItemStruct and save enough memory for its data
            Local $PropertyItem = DllCall($ghGDIPDll, "int", "GdipGetPropertyItem", "ptr", $hImage, "dword", $PropertyTagLoopCount, "dword", $PropertySize[3], "ptr", DllStructGetPtr($tPropItem))
            If $PropertyItem[0] <> 0 Then Return SetError(1, 0, 0)


            $lSize = DllStructGetData($tPropItem, "length")
;~          ' PropertyTyp ermitteln
            Switch DllStructGetData($tPropItem, "Type")
                Case $PropertyTagTypeByte
                    $lProp = DllStructCreate("byte[" & $lSize & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "byte"
                    ConsoleWrite("byte" & @CRLF)
                Case $PropertyTagTypeShort
                    $lProp = DllStructCreate("short[" & Ceiling($lSize / 2) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "ushort"
                    ConsoleWrite("short" & @CRLF)

                Case $PropertyTagTypeLong
                    $lProp = DllStructCreate("long[" & Ceiling($lSize / 4) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "long"
                    ConsoleWrite("long" & @CRLF)
            EndSwitch
            Return DllStructGetData($lProp, 1, 1)

        EndIf

    Else
;~      ' wenn der EXIF-Tag
;~      ' "PropertyTagLoopCount" fehlt
        Return 0
    EndIf
EndFunc   ;==>_GetGifLoopCount


; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
;~ '------------------------------------------------------
;~ ' Funktion     : CheckLoopCount
;~ ' Beschreibung : Überprüft, ob der EXIF-Tag LoopCount
;~ '                vorhanden sind
;~ ' Übergabewert : lInBitmap = GDI+ Bitmapobjekt
;~ ' Rückgabewert : True = LoopCount vorhanden
;~ '                False = LoopCount nicht vorhanden
;~ '------------------------------------------------------
Func _CheckForProperty( _
        ByRef $hImage, _
        $PropertyID)

    Local $lPropItem
    Local $lPropList


    Local $bRet = False

;~     ' Anzahl der EXIF-Metatags ermitteln
    Local $PropertyCount = DllCall($ghGDIPDll, "int", "GdipGetPropertyCount", "ptr", $hImage, "long*", 0)
    If $PropertyCount[0] = 0 Then


;~         ' sind EXIF-Metatags vorhanden
        If ($PropertyCount[2] > 0) Then

;~             ' Array zur Aufnahme der
;~             ' EXIF-Metatags dimensionieren
            Local $lPropList = DllStructCreate("dword[" & $PropertyCount[2] & "]")

;~             ' Liste der EXIF-Metatags auslesen -> lPropList()
            Local $PropertyList = DllCall($ghGDIPDll, "int", "GdipGetPropertyIdList", "ptr", $hImage, "long", $PropertyCount[2], "ptr", DllStructGetPtr($lPropList))
            If $PropertyList[0] = 0 Then

;~              ' Liste der EXIF-Metatags durchlaufen
                For $lPropItem = 1 To $PropertyCount[2]

;~                     ' ist in der Liste der EXIF-Metatag
;~                     ' PropertyID vorhanden

                    If DllStructGetData($lPropList, $lPropItem) = $PropertyID Then

;~                         ' wenn ja, dann Rückgabewert setzen und
;~                         ' Schleife verlassen
                        $bRet = True
                        ExitLoop
                    EndIf

                Next

            EndIf
        EndIf
    EndIf

;~     ' Rückgabewert ausgeben
    Return $bRet
EndFunc   ;==>_CheckForProperty

//Edit BUGFIX from next post inlcuded.

Edited by ProgAndy

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

Greate Work, smashly and Zedna :P I added handling for the Properties FrameDelay and LoopCount

Thanks for correction of my memory leak and nice addition ProgAndy.

I see just little \"bug\" in main loop: GifDrawFrame($i) should be placed BEFORE Sleep() and not AFTER

While 1
    If $LoopCount = 0 Or ($LoopCount > $MadeLoops) Then
        ; If $i = the frame count then reset $i to 0
        If $i = $GFC Then $i = 0
        GifDrawFrame($i)
        If UBound($FrameDelays) > $i And $FrameDelays[$i] > 0 Then
            Sleep($FrameDelays[$i]) ; Sleep for Frame Delay 
        Else
            Sleep(100)
            ConsoleWrite(\"Fallback, no time found \" & @CRLF)
        EndIf
        $i += 1
    Else
        Sleep(100)
    EndIf
WEnd
Edited by Zedna
Link to comment
Share on other sites

Greate Work, smashly and Zedna :P I added handling for the Properties FrameDelay and LoopCount

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
#include <Misc.au3>
#include <WinAPI.au3>

HotKeySet("{Esc}", "Quit")
HotKeySet("{Left}", "Left")
HotKeySet("{Right}", "Right")
HotKeySet("{Pause}", "Pause")

Global $Gif = @ScriptDir & "\gif-Green-UFO.gif"
Global $i = 0, $ApX, $ApY, $ApW, $ApH, $eX, $eY
Global $hImage, $hBitmap, $hScrDC, $hMemDC, $tSize, $pSize, $tSource, $pSource, $tBlend, $pBlend
Global $GFC, $GFDC, $pDimensionIDs, $tDL
Global $Pause

Global $MadeLoops, $LoopCount
Global $FrameDelays


Global Const $tagPropertyItem = "long id; long length; int Type; ptr value"
Global Const $PropertyTagFrameDelay = 0x5100
Global Const $PropertyTagLoopCount = 0x5101

Global Const $PropertyTagTypeByte = 1
Global Const $PropertyTagTypeASCII = 2
Global Const $PropertyTagTypeShort = 3
Global Const $PropertyTagTypeLong = 4
Global Const $PropertyTagTypeRational = 5
Global Const $PropertyTagTypeUndefined = 7
Global Const $PropertyTagTypeSLong = 9
Global Const $PropertyTagTypeSRational = 10

$hGUI = GUICreate("", 0, 0, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
GUISetState()

GifInit()

; move the window with animated GIF image
;~ AdlibEnable("FlyUfo", 20)

While 1
    If $LoopCount = 0 Or ($LoopCount > $MadeLoops) Then
        ; If $i = the frame count then reset $i to 0
        If $i = $GFC Then $i = 0
        If UBound($FrameDelays) > $i And $FrameDelays[$i] > 0 Then
            Sleep($FrameDelays[$i]) ; Sleep for Frame Delay :)
        Else
            Sleep(100)
            ConsoleWrite("Fallback, no time found :)" & @CRLF)
        EndIf
        GifDrawFrame($i)
        $i += 1
    Else
        Sleep(100)
    EndIf
WEnd

Func Quit()
    ;Tidy Up at end of each frame, otherwise memory use gets excessive in a short time.
    _GDIPlus_ImageDispose($hImage)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_DeleteDC($hMemDC)
    _GDIPlus_Shutdown()

    HotKeySet("{Esc}")
    HotKeySet("{Left}")
    HotKeySet("{Right}")
    HotKeySet("{Pause}")

    Exit
EndFunc   ;==>Quit

Func GifInit()
    _GDIPlus_Startup()
    ; Load your animated gif
    $hImage = _GDIPlus_ImageLoadFromFile($Gif)
    $ApW = _GDIPlus_ImageGetWidth($hImage)
    $ApH = _GDIPlus_ImageGetHeight($hImage)

    $hScrDC = _WinAPI_GetDC($hGUI)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)

    $tSize = DllStructCreate($tagSIZE)
    DllStructSetData($tSize, "X", $ApW)
    DllStructSetData($tSize, "Y", $ApH)
    $pSize = DllStructGetPtr($tSize)

    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)

    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    DllStructSetData($tBlend, "Alpha", 255)
    DllStructSetData($tBlend, "Format", 1)
    $pBlend = DllStructGetPtr($tBlend)

    ; Create a struct to hold the GUID.
    $tDL = DllStructCreate($tagGUID)

    ; Get a pointer to the GUID struct.
    $pDimensionIDs = DllStructGetPtr($tDL)

    ; Get the FrameDimensionsCount of the loaded gif
    $GFDC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsCount", "ptr", $hImage, "int*", 0)

    ; Get the FrameDimensionsList , which fills the GUID struct by passing the GUID pointer and the FrameDimensionsCount.
    DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsList", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $GFDC[2])

    ; Get the FrameCount of the loaded gif by passing the GUID pointer
    $GFC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameCount", "int", $hImage, "ptr", $pDimensionIDs, "int*", 0)
    $GFC = $GFC[3]
    $FrameDelays = _GetFrameDelays($hImage, $GFC)
    $LoopCount = _GetGifLoopCount($hImage)
EndFunc   ;==>GifInit

Func GifDrawFrame($i)
    ; Select the ActiveFrame in the loaded gif by telling it. The frame starts @ 0 ($i)
    DllCall($ghGDIPDll, "int", "GdipImageSelectActiveFrame", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $i)

    ; The rest is just a copy and paste on updating a layered window with the loaded gif.
    ; I tried using _GDIPlus_GraphicsDrawImage() but no matter what I did memory was getting chewed up. leak maybe?
    ; But using the Update layered window and memory use seems to level out and sit pretty idle
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)

    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap) ; Needs to be deleted after each creation, not just in the end once :P
EndFunc   ;==>GifDrawFrame

; move the window with animated GIF image
Func FlyUfo()
    Local $Speed = 0
    Do
        If $ApX >= (@DesktopWidth - $ApW) Then
            $eX = "$ApX - 1"
        ElseIf $ApX <= 0 Then
            $eX = "$ApX + 1"
        EndIf
        If $ApY >= (@DesktopHeight - $ApH) Then
            $eY = "$ApY - 1"
        ElseIf $ApY <= 0 Then
            $eY = "$ApY + 1"
        EndIf
        $ApX = Execute($eX)
        $ApY = Execute($eY)
        WinMove($hGUI, "", $ApX, $ApY)
        $Speed += 1
    Until $Speed = 5
EndFunc   ;==>FlyUfo

Func Left()
    If WinActive($hGUI) Then
        If Not $Pause Then Return
        $i -= 1
        If $i = -1 Then $i = $GFC - 1
        GifDrawFrame($i)
    Else
        HotKeySet("{Left}")
        Send("{Left}")
        HotKeySet("{Left}", "Left")
    EndIf
EndFunc   ;==>Left

Func Right()
    If WinActive($hGUI) Then
        If Not $Pause Then Return
        $i += 1
        If $i = $GFC Then $i = 0
        GifDrawFrame($i)
    Else
        HotKeySet("{Right}")
        Send("{Right}")
        HotKeySet("{Right}", "Right")
    EndIf
EndFunc   ;==>Right

Func Pause()
    If WinActive($hGUI) Then
        $Pause = Not $Pause
        If Not $Pause Then Return

;~   WinSetTitle($hGUI, '', $Gif & " PAUSED")
        While $Pause
            Sleep(100)
        WEnd
;~   WinSetTitle($hGUI, '', $Gif)
    Else
        HotKeySet("{PAUSE}")
        Send("{PAUSE}")
        HotKeySet("{PAUSE}", "Pause")
    EndIf
EndFunc   ;==>Pause

; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
Func _GetFrameDelays(ByRef $hImage, $FrameCount)
    
    Local $lOutFrameDelay[$FrameCount]
    

;~  Dim lProp() As Byte
;~  Dim lPropCount As Long
    Local $lSize ;As Long
;~  Dim lPropSize As Long
;~  Dim tPropItem As PropertyItem
    Local $PropID = $PropertyTagFrameDelay
    If Not _CheckForProperty($hImage, $PropID) Then Return SetError(1, 0, 0)
    Local $PropertySize = DllCall($ghGDIPDll, "int", "GdipGetPropertyItemSize", "ptr", $hImage, "dword", $PropID, "uint*", 0)

;~     ' Datengröße vom EXIF-Tag
;~     ' "PropertyTagFrameDelay" ermitteln
    If $PropertySize[0] = 0 Then
        $tPropItem = DllStructCreate($tagPropertyItem & ";byte[" & $PropertySize[3] & "]"); Create Property-ItemStruct and save enough memory for its data
        Local $PropertyItem = DllCall($ghGDIPDll, "int", "GdipGetPropertyItem", "ptr", $hImage, "dword", $PropID, "dword", $PropertySize[3], "ptr", DllStructGetPtr($tPropItem))
        If $PropertyItem[0] <> 0 Then Return SetError(1, 0, 0)
        $lSize = DllStructGetData($tPropItem, "length")
;~          ' PropertyTyp ermitteln
        Switch DllStructGetData($tPropItem, "Type")
            Case $PropertyTagTypeByte
                $lProp = DllStructCreate("byte[" & $lSize & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "byte"
                ConsoleWrite("byte" & @CRLF)
            Case $PropertyTagTypeShort
                $lProp = DllStructCreate("short[" & Ceiling($lSize / 2) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "ushort"
                ConsoleWrite("short" & @CRLF)

            Case $PropertyTagTypeLong
                $lProp = DllStructCreate("long[" & Ceiling($lSize / 4) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "long"
                ConsoleWrite("long" & @CRLF)
        EndSwitch

;~                  ' Pausenzeiten der einzelnen
;~                  ' Bilder aus Bytearray kopieren
        For $lPropCount = 0 To $FrameCount - 1
            $lOutFrameDelay[$lPropCount] = DllStructGetData($lProp, 1, $lPropCount + 1) * 10


        Next
    EndIf
;~  _MemGlobalFree(DllStructGetData($tPropItem,"value"))
;~  _MemVirtualFree(DllStructGetData($tPropItem,"value"),$lSize,$MEM_RELEASE)
    Return $lOutFrameDelay
EndFunc   ;==>_GetFrameDelays

; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
;~ '------------------------------------------------------
;~ ' Funktion     : GetGifLoopCount
;~ ' Beschreibung : Auslesen der Wiederholungen
;~ ' Übergabewert : lInBitmap = GDI+ Bitmapobjekt
;~ '                lOutLoopCount = Anzahl der Wiederholungen
;~ '------------------------------------------------------
Func _GetGifLoopCount(ByRef $hImage)

    Local $tPropItem
    Local $lProp
    Local $lSize

;~     ' ist der EXIF-Tag
;~     ' "PropertyTagLoopCount" vorhanden
    If _CheckForProperty($hImage, _
            $PropertyTagLoopCount) = True Then

;~     ' Datengröße vom EXIF-Tag
;~     ' "PropertyTagLoopCount" ermitteln
        Local $PropertySize = DllCall($ghGDIPDll, "int", "GdipGetPropertyItemSize", "ptr", $hImage, "dword", $PropertyTagLoopCount, "uint*", 0)
        If $PropertySize[0] = 0 Then

;~             ' Daten auslesen
            
            $tPropItem = DllStructCreate($tagPropertyItem & ";byte[" & $PropertySize[3] & "]"); Create Property-ItemStruct and save enough memory for its data
            Local $PropertyItem = DllCall($ghGDIPDll, "int", "GdipGetPropertyItem", "ptr", $hImage, "dword", $PropertyTagLoopCount, "dword", $PropertySize[3], "ptr", DllStructGetPtr($tPropItem))
            If $PropertyItem[0] <> 0 Then Return SetError(1, 0, 0)


            $lSize = DllStructGetData($tPropItem, "length")
;~          ' PropertyTyp ermitteln
            Switch DllStructGetData($tPropItem, "Type")
                Case $PropertyTagTypeByte
                    $lProp = DllStructCreate("byte[" & $lSize & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "byte"
                    ConsoleWrite("byte" & @CRLF)
                Case $PropertyTagTypeShort
                    $lProp = DllStructCreate("short[" & Ceiling($lSize / 2) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "ushort"
                    ConsoleWrite("short" & @CRLF)

                Case $PropertyTagTypeLong
                    $lProp = DllStructCreate("long[" & Ceiling($lSize / 4) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "long"
                    ConsoleWrite("long" & @CRLF)
            EndSwitch
            Return DllStructGetData($lProp, 1, 1)

        EndIf

    Else
;~      ' wenn der EXIF-Tag
;~      ' "PropertyTagLoopCount" fehlt
        Return 0
    EndIf
EndFunc   ;==>_GetGifLoopCount


; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
;~ '------------------------------------------------------
;~ ' Funktion     : CheckLoopCount
;~ ' Beschreibung : Überprüft, ob der EXIF-Tag LoopCount
;~ '                vorhanden sind
;~ ' Übergabewert : lInBitmap = GDI+ Bitmapobjekt
;~ ' Rückgabewert : True = LoopCount vorhanden
;~ '                False = LoopCount nicht vorhanden
;~ '------------------------------------------------------
Func _CheckForProperty( _
        ByRef $hImage, _
        $PropertyID)

    Local $lPropItem
    Local $lPropList


    Local $bRet = False

;~     ' Anzahl der EXIF-Metatags ermitteln
    Local $PropertyCount = DllCall($ghGDIPDll, "int", "GdipGetPropertyCount", "ptr", $hImage, "long*", 0)
    If $PropertyCount[0] = 0 Then


;~         ' sind EXIF-Metatags vorhanden
        If ($PropertyCount[2] > 0) Then

;~             ' Array zur Aufnahme der
;~             ' EXIF-Metatags dimensionieren
            Local $lPropList = DllStructCreate("dword[" & $PropertyCount[2] & "]")

;~             ' Liste der EXIF-Metatags auslesen -> lPropList()
            Local $PropertyList = DllCall($ghGDIPDll, "int", "GdipGetPropertyIdList", "ptr", $hImage, "long", $PropertyCount[2], "ptr", DllStructGetPtr($lPropList))
            If $PropertyList[0] = 0 Then

;~              ' Liste der EXIF-Metatags durchlaufen
                For $lPropItem = 1 To $PropertyCount[2]

;~                     ' ist in der Liste der EXIF-Metatag
;~                     ' PropertyID vorhanden

                    If DllStructGetData($lPropList, $lPropItem) = $PropertyID Then

;~                         ' wenn ja, dann Rückgabewert setzen und
;~                         ' Schleife verlassen
                        $bRet = True
                        ExitLoop
                    EndIf

                Next

            EndIf
        EndIf
    EndIf

;~     ' Rückgabewert ausgeben
    Return $bRet
EndFunc   ;==>_CheckForProperty
Nice work ProgAndy, I got lazy when I got to the frame time delay hence me doing the dirty hard set sleep.

Now if we can work out why GraphicsDrawImage is chewing memory when used in a continuous loop then we could get rid of the update layered window for every frame. I tried a few things. Like setting GDI+ flush, delete graphics per cylce but _GDIPlus_GraphicsDrawImage() seems to just increase memory use every couple of cycles through the loop.

Other ways of drawing the frames could be use api SendMessage to display the frames on a control which also works ok.

Cheers

Link to comment
Share on other sites

OK, I tried it with _SetBitmapToCtrl from Zednas Resource UDf -> It works. Also, I used a Timer for drawing new frames :P -> No blocking of other Functions or slower drawing then :P. The time for the Timer is set each time to the new value, so this should be OK, too.

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
#include <Misc.au3>
#include <WinAPI.au3>
#include <Timers.au3>

HotKeySet("{Esc}", "Quit")
HotKeySet("{Left}", "Left")
HotKeySet("{Right}", "Right")
HotKeySet("{Pause}", "Pause")

Global $Gif = @ScriptDir & "\gif-Green-UFO.gif"
Global $i = 0, $ApX, $ApY, $ApW, $ApH, $eX, $eY
Global $hImage, $hBitmap, $hScrDC, $hMemDC, $tSize, $pSize, $tSource, $pSource, $tBlend, $pBlend
Global $GFC, $GFDC, $pDimensionIDs, $tDL
Global $Pause

Global $MadeLoops, $LoopCount
Global $FrameDelays
Global $GIF_TimerID = -1

Global Const $tagPropertyItem = "long id; long length; int Type; ptr value"
Global Const $PropertyTagFrameDelay = 0x5100
Global Const $PropertyTagLoopCount = 0x5101

Global Const $PropertyTagTypeByte = 1
Global Const $PropertyTagTypeASCII = 2
Global Const $PropertyTagTypeShort = 3
Global Const $PropertyTagTypeLong = 4
Global Const $PropertyTagTypeRational = 5
Global Const $PropertyTagTypeUndefined = 7
Global Const $PropertyTagTypeSLong = 9
Global Const $PropertyTagTypeSRational = 10

;~ $hGUI = GUICreate("", 0, 0, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
$hGUI = GUICreate("GIF Animation",300,300)
GUICtrlCreateLabel("text behind GIF",5,15,100,25)
Global $IMG_Ctrl= GUICtrlCreateLabel("",10,10,10,10) ; For Drawing :)
GUICtrlSetBkColor(-1,$GUI_BKCOLOR_TRANSPARENT) ; Transparency's working :)

GUISetState()

GifInit()

; move the window with animated GIF image
;~ AdlibEnable("FlyUfo", 20)
$GIF_TimerID = _Timer_SetTimer($hGUI, 100, "_Draw_Timer") ; Start Animation
While 1
    If GUIGetMsg() = -3 Then Quit()
WEnd

Func _Draw_Timer($hWnd, $Msg, $iIDTimer, $dwTime)
    If $LoopCount = 0 Or ($LoopCount > $MadeLoops) Then
        ; If $i = the frame count then reset $i to 0
        If $GIF_TimerID = 0 Then $GIF_TimerID = -1
        If $i = $GFC Then $i = 0
        If UBound($FrameDelays) > $i And $FrameDelays[$i] > 0 Then
            _Timer_SetTimer($hWnd, $FrameDelays[$i], "_Draw_Timer", $iIDTimer)
        Else
            _Timer_SetTimer($hWnd, 100, "_Draw_Timer", $iIDTimer)
            ConsoleWrite("Fallback, no time found :)" & @CRLF)
        EndIf
;~      GifDrawFrame($i)
        GifDrawFrame_Ctrl($i)
        $i += 1
    Else
        _Timer_KillTimer($hWnd, $iIDTimer)
    EndIf
EndFunc   ;==>_Draw_Timer
Func Quit()
    _Timer_KillAllTimers($hGUI)
    ;Tidy Up at end of each frame, otherwise memory use gets excessive in a short time.
    _GDIPlus_ImageDispose($hImage)

    _GDIPlus_Shutdown()

    HotKeySet("{Esc}")
    HotKeySet("{Left}")
    HotKeySet("{Right}")
    HotKeySet("{Pause}")

    Exit
EndFunc   ;==>Quit

Func GifInit()
    _GDIPlus_Startup()
    ; Load your animated gif
    $hImage = _GDIPlus_ImageLoadFromFile($Gif)
    If @error Then _WinAPI_FatalAppExit("Image " & $Gif & " not found!" & _GDIPlus_Shutdown())
    $ApW = _GDIPlus_ImageGetWidth($hImage)
    $ApH = _GDIPlus_ImageGetHeight($hImage)

    GUICtrlSetPos($IMG_Ctrl,Default,Default,$ApW,$ApH) ;Set Size of the Control :D

    ; Create a struct to hold the GUID.
    $tDL = DllStructCreate($tagGUID)

    ; Get a pointer to the GUID struct.
    $pDimensionIDs = DllStructGetPtr($tDL)

    ; Get the FrameDimensionsCount of the loaded gif
    $GFDC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsCount", "ptr", $hImage, "int*", 0)

    ; Get the FrameDimensionsList , which fills the GUID struct by passing the GUID pointer and the FrameDimensionsCount.
    DllCall($ghGDIPDll, "int", "GdipImageGetFrameDimensionsList", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $GFDC[2])

    ; Get the FrameCount of the loaded gif by passing the GUID pointer
    $GFC = DllCall($ghGDIPDll, "int", "GdipImageGetFrameCount", "int", $hImage, "ptr", $pDimensionIDs, "int*", 0)
    $GFC = $GFC[3]
    $FrameDelays = _GetFrameDelays($hImage, $GFC)
    $LoopCount = _GetGifLoopCount($hImage)
EndFunc   ;==>GifInit

Func GifDrawFrame_Ctrl($i)
    ; Select the ActiveFrame in the loaded gif by telling it. The frame starts @ 0 ($i)
    DllCall($ghGDIPDll, "int", "GdipImageSelectActiveFrame", "ptr", $hImage, "ptr", $pDimensionIDs, "int", $i)

    ; The rest is just a copy and paste on updating a layered window with the loaded gif.
    ; I tried using _GDIPlus_GraphicsDrawImage() but no matter what I did memory was getting chewed up. leak maybe?
    ; But using the Update layered window and memory use seems to level out and sit pretty idle
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    _SetBitmapToCtrl($IMG_Ctrl,$hBitmap)
    _WinAPI_DeleteObject($hBitmap)
EndFunc   ;==>GifDrawFrame

; internal helper function, out of reources UDf from Zedna
Func _SetBitmapToCtrl($CtrlId, $hBitmap)
    Local Const $STM_SETIMAGE = 0x0172
    Local Const $IMAGE_BITMAP = 0
    Local Const $SS_BITMAP = 0xE
    Local Const $GWL_STYLE = -16
    Local Const $SS_REALSIZEIMAGE = 0x800

    Local $hWnd = GUICtrlGetHandle($CtrlId)
    If $hWnd = 0 Then Return SetError(1, 0, 0)
        ; set SS_BITMAP style to control
    Local $oldStyle = DllCall("user32.dll", "long", "GetWindowLong", "hwnd", $hWnd, "int", $GWL_STYLE)
    If @error Then Return SetError(2, 0, 0)
    DllCall("user32.dll", "long", "SetWindowLong", "hwnd", $hWnd, "int", $GWL_STYLE, "long", BitOR($oldStyle[0], $SS_BITMAP,$SS_REALSIZEIMAGE))
    If @error Then Return SetError(3, 0, 0)
        Local $oldBmp = DllCall("user32.dll", "hwnd", "SendMessage", "hwnd", $hWnd, "int", $STM_SETIMAGE, "int", $IMAGE_BITMAP, "int", $hBitmap)
    If @error Then Return SetError(4, 0, 0)
    If $oldBmp[0] <> 0 Then _WinAPI_DeleteObject($oldBmp[0])
    Return 1
EndFunc

; move the window with animated GIF image
Func FlyUfo()
    Local $Speed = 0
    Do
        If $ApX >= (@DesktopWidth - $ApW) Then
            $eX = "$ApX - 1"
        ElseIf $ApX <= 0 Then
            $eX = "$ApX + 1"
        EndIf
        If $ApY >= (@DesktopHeight - $ApH) Then
            $eY = "$ApY - 1"
        ElseIf $ApY <= 0 Then
            $eY = "$ApY + 1"
        EndIf
        $ApX = Execute($eX)
        $ApY = Execute($eY)
        WinMove($hGUI, "", $ApX, $ApY)
        $Speed += 1
    Until $Speed = 5
EndFunc   ;==>FlyUfo

Func Left()
    If WinActive($hGUI) Then
        If Not $Pause Then Return
        $i -= 1
        If $i = -1 Then $i = $GFC - 1
;~      GifDrawFrame($i)
        GifDrawFrame_Ctrl($i)
    Else
        HotKeySet("{Left}")
        Send("{Left}")
        HotKeySet("{Left}", "Left")
    EndIf
EndFunc   ;==>Left

Func Right()
    If WinActive($hGUI) Then
        If Not $Pause Then Return
        $i += 1
        If $i = $GFC Then $i = 0
;~      GifDrawFrame($i)
        GifDrawFrame_Ctrl($i)
    Else
        HotKeySet("{Right}")
        Send("{Right}")
        HotKeySet("{Right}", "Right")
    EndIf
EndFunc   ;==>Right

Func Pause()
    If WinActive($hGUI) Then
        $Pause = Not $Pause
        if $Pause Then
            _Timer_KillTimer($hGUI,$GIF_TimerID)
        Else
            $GIF_TimerID = _Timer_SetTimer($hGUI, 100, "_Draw_Timer") ; Start Animation
        EndIf

    Else
        HotKeySet("{PAUSE}")
        Send("{PAUSE}")
        HotKeySet("{PAUSE}", "Pause")
    EndIf
EndFunc   ;==>Pause

; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
Func _GetFrameDelays(ByRef $hImage, $FrameCount)
    
    Local $lOutFrameDelay[$FrameCount]
    

;~  Dim lProp() As Byte
;~  Dim lPropCount As Long
    Local $lSize ;As Long
;~  Dim lPropSize As Long
;~  Dim tPropItem As PropertyItem
    Local $PropID = $PropertyTagFrameDelay
    If Not _CheckForProperty($hImage, $PropID) Then Return SetError(1, 0, 0)
    Local $PropertySize = DllCall($ghGDIPDll, "int", "GdipGetPropertyItemSize", "ptr", $hImage, "dword", $PropID, "uint*", 0)

;~     ' Datengröße vom EXIF-Tag
;~     ' "PropertyTagFrameDelay" ermitteln
    If $PropertySize[0] = 0 Then
        $tPropItem = DllStructCreate($tagPropertyItem & ";byte[" & $PropertySize[3] & "]"); Create Property-ItemStruct and save enough memory for its data
        Local $PropertyItem = DllCall($ghGDIPDll, "int", "GdipGetPropertyItem", "ptr", $hImage, "dword", $PropID, "dword", $PropertySize[3], "ptr", DllStructGetPtr($tPropItem))
        If $PropertyItem[0] <> 0 Then Return SetError(1, 0, 0)
        $lSize = DllStructGetData($tPropItem, "length")
;~          ' PropertyTyp ermitteln
        Switch DllStructGetData($tPropItem, "Type")
            Case $PropertyTagTypeByte
                $lProp = DllStructCreate("byte[" & $lSize & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "byte"
                ConsoleWrite("byte" & @CRLF)
            Case $PropertyTagTypeShort
                $lProp = DllStructCreate("short[" & Ceiling($lSize / 2) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "ushort"
                ConsoleWrite("short" & @CRLF)

            Case $PropertyTagTypeLong
                $lProp = DllStructCreate("long[" & Ceiling($lSize / 4) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "long"
                ConsoleWrite("long" & @CRLF)
        EndSwitch

;~                  ' Pausenzeiten der einzelnen
;~                  ' Bilder aus Bytearray kopieren
        For $lPropCount = 0 To $FrameCount - 1
            $lOutFrameDelay[$lPropCount] = DllStructGetData($lProp, 1, $lPropCount + 1) * 10


        Next
    EndIf
;~  _MemGlobalFree(DllStructGetData($tPropItem,"value"))
;~  _MemVirtualFree(DllStructGetData($tPropItem,"value"),$lSize,$MEM_RELEASE)
    Return $lOutFrameDelay
EndFunc   ;==>_GetFrameDelays

; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
;~ '------------------------------------------------------
;~ ' Funktion     : GetGifLoopCount
;~ ' Beschreibung : Auslesen der Wiederholungen
;~ ' Übergabewert : lInBitmap = GDI+ Bitmapobjekt
;~ '                lOutLoopCount = Anzahl der Wiederholungen
;~ '------------------------------------------------------
Func _GetGifLoopCount(ByRef $hImage)

    Local $tPropItem
    Local $lProp
    Local $lSize

;~     ' ist der EXIF-Tag
;~     ' "PropertyTagLoopCount" vorhanden
    If _CheckForProperty($hImage, _
            $PropertyTagLoopCount) = True Then

;~     ' Datengröße vom EXIF-Tag
;~     ' "PropertyTagLoopCount" ermitteln
        Local $PropertySize = DllCall($ghGDIPDll, "int", "GdipGetPropertyItemSize", "ptr", $hImage, "dword", $PropertyTagLoopCount, "uint*", 0)
        If $PropertySize[0] = 0 Then

;~             ' Daten auslesen
            
            $tPropItem = DllStructCreate($tagPropertyItem & ";byte[" & $PropertySize[3] & "]"); Create Property-ItemStruct and save enough memory for its data
            Local $PropertyItem = DllCall($ghGDIPDll, "int", "GdipGetPropertyItem", "ptr", $hImage, "dword", $PropertyTagLoopCount, "dword", $PropertySize[3], "ptr", DllStructGetPtr($tPropItem))
            If $PropertyItem[0] <> 0 Then Return SetError(1, 0, 0)


            $lSize = DllStructGetData($tPropItem, "length")
;~          ' PropertyTyp ermitteln
            Switch DllStructGetData($tPropItem, "Type")
                Case $PropertyTagTypeByte
                    $lProp = DllStructCreate("byte[" & $lSize & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "byte"
                    ConsoleWrite("byte" & @CRLF)
                Case $PropertyTagTypeShort
                    $lProp = DllStructCreate("short[" & Ceiling($lSize / 2) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "ushort"
                    ConsoleWrite("short" & @CRLF)

                Case $PropertyTagTypeLong
                    $lProp = DllStructCreate("long[" & Ceiling($lSize / 4) & "]", DllStructGetData($tPropItem, "value"))
;~                  $lSize = "long"
                    ConsoleWrite("long" & @CRLF)
            EndSwitch
            Return DllStructGetData($lProp, 1, 1)

        EndIf

    Else
;~      ' wenn der EXIF-Tag
;~      ' "PropertyTagLoopCount" fehlt
        Return 0
    EndIf
EndFunc   ;==>_GetGifLoopCount


; Author: Prog@ndy (converted from VB :) )
; VB-Source: http://www.activevb.de/tipps/vb6tipps/tipp0675.html
;~ '------------------------------------------------------
;~ ' Funktion     : CheckLoopCount
;~ ' Beschreibung : Überprüft, ob der EXIF-Tag LoopCount
;~ '                vorhanden sind
;~ ' Übergabewert : lInBitmap = GDI+ Bitmapobjekt
;~ ' Rückgabewert : True = LoopCount vorhanden
;~ '                False = LoopCount nicht vorhanden
;~ '------------------------------------------------------
Func _CheckForProperty( _
        ByRef $hImage, _
        $PropertyID)

    Local $lPropItem
    Local $lPropList


    Local $bRet = False

;~     ' Anzahl der EXIF-Metatags ermitteln
    Local $PropertyCount = DllCall($ghGDIPDll, "int", "GdipGetPropertyCount", "ptr", $hImage, "long*", 0)
    If $PropertyCount[0] = 0 Then


;~         ' sind EXIF-Metatags vorhanden
        If ($PropertyCount[2] > 0) Then

;~             ' Array zur Aufnahme der
;~             ' EXIF-Metatags dimensionieren
            Local $lPropList = DllStructCreate("dword[" & $PropertyCount[2] & "]")

;~             ' Liste der EXIF-Metatags auslesen -> lPropList()
            Local $PropertyList = DllCall($ghGDIPDll, "int", "GdipGetPropertyIdList", "ptr", $hImage, "long", $PropertyCount[2], "ptr", DllStructGetPtr($lPropList))
            If $PropertyList[0] = 0 Then

;~              ' Liste der EXIF-Metatags durchlaufen
                For $lPropItem = 1 To $PropertyCount[2]

;~                     ' ist in der Liste der EXIF-Metatag
;~                     ' PropertyID vorhanden

                    If DllStructGetData($lPropList, $lPropItem) = $PropertyID Then

;~                         ' wenn ja, dann Rückgabewert setzen und
;~                         ' Schleife verlassen
                        $bRet = True
                        ExitLoop
                    EndIf

                Next

            EndIf
        EndIf
    EndIf

;~     ' Rückgabewert ausgeben
    Return $bRet
EndFunc   ;==>_CheckForProperty

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

OK, I tried it with _SetBitmapToCtrl from Zednas Resource UDf -> It works. Also, I used a Timer for drawing new frames :P -> No blocking of other Functions or slower drawing then :P . The time for the Timer is set each time to the new value, so this should be OK, too.

It's awesome!

Just look at my previous last post - your little bug is not still corrected.

Link to comment
Share on other sites

In the one with timers, this bug doesn't exist :P And the other will be corrected in a minute :P

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

So with a bit more work we'll hopefully soon get to see animated gif support in Zedna's Resources.au3

YaY :P

Edit: Also forgot to mention with minimal code change you can use the same routine to display multi page tif files

Edited by smashly
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...