Jump to content

GIF Animation (cached)


Nine
 Share

Recommended Posts

I had recognized this function in the past (2014) and wrote an example but I couldn't think of any real usage of it.

Good catch... :thumbsup:

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

  • 6 months later...

Hi Nine,
Long post coming (who said "as usual") ;)

I really like the way you scripted this GIF animation because you separate clearly :
* all the functions in the UDF...
* ...leaving very few code in the example file, which is great for the final user !

But I had an issue (from time to time) with the example file, i.e. if I drag too quickly one of the 4 gif's, then keying Esc to end the script, or in several other cases. Then sometimes a bad message appears "AutoIt must end etc..." with an exit code error in the console (always the same error code) : 3221225477

I think this error is an Access violation (Indicates that the executed program has terminated abnormally or crashed). Good news is I found why it crashed, we'll discuss it later.

As you know, we got a saying "à quelque chose malheur est bon" which could translate in english by (thx google) "every cloud has a silver lining" or however unpleasant an experience can be, you will probably learn from it.

And that happened to me because this error made me investigate into other scripts related to animated gifs, hoping I'll find something there that would show how these other scripts display the several frames of a gif file and compare them to your code etc...

UEZ's "Play GIF Anim from memory" found in this pastebin link was a big help, because it allowed me to change 3 parts in your UDF with his code, then I never got anymore the error 3221225477 even if I run the example script dozen of times.

Lines changed in your UDF : 1st part

; Global $hGIF_Animation_AdLib ; commented

2nd part :

; If Not $iIdx Then $hGIF_Animation_AdLib = AdlibRegister(__GIF_Animation_DrawTimer, 10) ; commented and replaced by :

If Not $iIdx Then
    ; $iFPS = 25 ; UEZ's way (33 would be more precise for the GIF ballerina dancer in UEZ's script)
    Local $iFPS = 100 ; to match Nine's Adlib each 10ms (as 1000 / 100 = 10 in next DllCall)
    GUIRegisterMsg($WM_TIMER, "__GIF_Animation_DrawTimer")
    DllCall("user32.dll", "int", "SetTimer", "hwnd", $hGUI, "int", 0, "int", Int(1000 / $iFPS), "int", 0)
    ; gladly $hGUI is declared global in Nine's example file
EndIf

3rd part :

; AdlibUnRegister($hGIF_Animation_AdLib) ; commented  and replaced by :

GUIRegisterMsg($WM_TIMER, "")

After these 3 changes were applied, everything constantly worked fine, so it was now easier to find the issue :

; AdlibUnRegister($hGIF_Animation_AdLib) ; wrong statement because $hGIF_Animation_AdLib = 1
AdlibUnRegister(__GIF_Animation_DrawTimer) ; yes !

Then just changing one line in your UDF should fix it, in case other users got the same problem.
Thanks for reading and I hope you enjoyed the journey :)

Edited by pixelsearch
typo
Link to comment
Share on other sites

You're welcome !
I was just thinking... now that the global variable $hGIF_Animation_AdLib becomes useless, can't we get totally rid of it ?

Something like this :

Global $hGIF_Animation_AdLib

; If Not $iIdx Then $hGIF_Animation_AdLib = AdlibRegister(__GIF_Animation_DrawTimer, 10); replaced with :

If Not $iIdx Then AdlibRegister(__GIF_Animation_DrawTimer, 10)

You know, sometimes I think of how many times you help people by quickly proposing them a script that solves their issue, or give them a good advice which leads them to the right direction, AutoIt staff took a good decision on May 11, 2020 when they promoted you as MVP (same goes for the incredible Subz and Danyfirex). We are lucky :)

Link to comment
Share on other sites

On 5/8/2020 at 7:17 PM, Nine said:

I enclosed the GIF, the UDF and the example into the zip file. 

Currently I do not see any example as OP do not contain ZIP any more.
Could you create/attach one ?

 

Edited by mLipok

Signature beginning:
Please remember: "AutoIt"..... *  Wondering who uses AutoIt and what it can be used for ? * Forum Rules *
ADO.au3 UDF * POP3.au3 UDF * XML.au3 UDF * IE on Windows 11 * How to ask ChatGPT for AutoIt Codefor other useful stuff click the following button:

Spoiler

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind. 

My contribution (my own projects): * Debenu Quick PDF Library - UDF * Debenu PDF Viewer SDK - UDF * Acrobat Reader - ActiveX Viewer * UDF for PDFCreator v1.x.x * XZip - UDF * AppCompatFlags UDF * CrowdinAPI UDF * _WinMergeCompare2Files() * _JavaExceptionAdd() * _IsBeta() * Writing DPI Awareness App - workaround * _AutoIt_RequiredVersion() * Chilkatsoft.au3 UDF * TeamViewer.au3 UDF * JavaManagement UDF * VIES over SOAP * WinSCP UDF * GHAPI UDF - modest begining - comunication with GitHub REST APIErrorLog.au3 UDF - A logging Library * Include Dependency Tree (Tool for analyzing script relations) * Show_Macro_Values.au3 *

 

My contribution to others projects or UDF based on  others projects: * _sql.au3 UDF  * POP3.au3 UDF *  RTF Printer - UDF * XML.au3 UDF * ADO.au3 UDF SMTP Mailer UDF * Dual Monitor resolution detection * * 2GUI on Dual Monitor System * _SciLexer.au3 UDF * SciTE - Lexer for console pane

Useful links: * Forum Rules * Forum etiquette *  Forum Information and FAQs * How to post code on the forum * AutoIt Online Documentation * AutoIt Online Beta Documentation * SciTE4AutoIt3 getting started * Convert text blocks to AutoIt code * Games made in Autoit * Programming related sites * Polish AutoIt Tutorial * DllCall Code Generator * 

Wiki: Expand your knowledge - AutoIt Wiki * Collection of User Defined Functions * How to use HelpFile * Good coding practices in AutoIt * 

OpenOffice/LibreOffice/XLS Related: WriterDemo.au3 * XLS/MDB from scratch with ADOX

IE Related:  * How to use IE.au3  UDF with  AutoIt v3.3.14.x * Why isn't Autoit able to click a Javascript Dialog? * Clicking javascript button with no ID * IE document >> save as MHT file * IETab Switcher (by LarsJ ) * HTML Entities * _IEquerySelectorAll() (by uncommon) * IE in TaskSchedulerIE Embedded Control Versioning (use IE9+ and HTML5 in a GUI) * PDF Related:How to get reference to PDF object embeded in IE * IE on Windows 11

I encourage you to read: * Global Vars * Best Coding Practices * Please explain code used in Help file for several File functions * OOP-like approach in AutoIt * UDF-Spec Questions *  EXAMPLE: How To Catch ConsoleWrite() output to a file or to CMD *

I also encourage you to check awesome @trancexx code:  * Create COM objects from modules without any demand on user to register anything. * Another COM object registering stuffOnHungApp handlerAvoid "AutoIt Error" message box in unknown errors  * HTML editor

winhttp.au3 related : * https://www.autoitscript.com/forum/topic/206771-winhttpau3-download-problem-youre-speaking-plain-http-to-an-ssl-enabled-server-port/

"Homo sum; humani nil a me alienum puto" - Publius Terentius Afer
"Program are meant to be read by humans and only incidentally for computers and execute" - Donald Knuth, "The Art of Computer Programming"
:naughty:  :ranting:, be  :) and       \\//_.

Anticipating Errors :  "Any program that accepts data from a user must include code to validate that data before sending it to the data store. You cannot rely on the data store, ...., or even your programming language to notify you of problems. You must check every byte entered by your users, making sure that data is the correct type for its field and that required fields are not empty."

Signature last update: 2023-04-24

Link to comment
Share on other sites

2 hours ago, pixelsearch said:

now that the global variable $hGIF_Animation_AdLib becomes useless, can't we get totally rid of it ?

Yep, done.  And thanks for the kind words...

Link to comment
Share on other sites

Seems to me like you still mix adlibregister and timers.

Use standard _Timer_SetTimer() in 2nd part to get timer ID, and _Timer_KillTimer() after 3rd part, delete all adlibregister() and adlibunregister() calls. I guess the GUIRegisterMsg() part is then also unnecessary, as the callback function can go directly into the settimer function.

Edited by KaFu
Link to comment
Share on other sites

4 hours ago, KaFu said:

Seems to me like you still mix adlibregister and timers

Not at all.  @pixelsearch was explaining how he found out the issue with my code.  By using a different approach, he was able to eliminate the crash of the UDF.  And he finally traced the culprit in the AdLibUnRegister function.  Problem is now solved.

 

Link to comment
Share on other sites

Hi Nine,
Here are some thoughts, in case one day you would like to extend your UDF functionalities. For example, maybe the user wants to delete an animated gif at a certain moment of his script, because it's not needed anymore.

I added a little function named _GUICtrlDeleteAnimGIF() in the example script below (though its final place shouldn't be in the example script at all, it's just a very quick try, not fully tested and without error checking)

Of course the user could simply hide the image, without deleting it, as you'll notice when you click on the test button in the GUI, but well... having the AdlibRegister function constantly animating a hidden picture...

Other vague thoughts concern the animated gif's whose delays aren't constant between each frame (edited: sorry, the UDF already takes care of this point, tested right now)

Another thought in case there are many calculations during the script, then maybe 10ms (the delay between each call to the Adlib function) could be crucial and should be replaced with, for example, the minimal delay found between all gif's (though this could create a new problem, for example a 1st gif having a 30ms delay between each frame, a 2nd gif having a 40ms delay, then if you call the Adlib each 30ms, the 2nd gif will display its 2nd frame a bit late, only after 60ms, i.e 2*30ms) . So in the end your 10ms parameter isn't bad at all as it covers all cases.

Anyway, these are just open thoughts exchanged here, without any request, just to share them with you :)

#include <Array.au3>
#include <GUIConstants.au3>
#include "Cached GIF Animation.au3"
#include <WinAPISysWin.au3>

OnAutoItExitRegister (_GIF_Animation_Quit)

Global $hGUI = GUICreate("GIF Animation", 500, 500, -1, -1, $WS_POPUP, $WS_EX_LAYERED)
GUISetBkColor (0xFFFFFF)
Global $IMG_Ctrl1 = _GUICtrlCreateAnimGIF ("9.gif", 150, 0, 192, 172, -1, $GUI_WS_EX_PARENTDRAG)
Global $IMG_Ctrl2 = _GUICtrlCreateAnimGIF ("gif-Green-UFO.gif", 0, 180, 150, 80, -1, $GUI_WS_EX_PARENTDRAG)
Global $IMG_Ctrl3 = _GUICtrlCreateAnimGIF ("Catalog.gif", 160, 180, 320, 184, -1, $GUI_WS_EX_PARENTDRAG)
Global $IMG_Ctrl4 = _GUICtrlCreateAnimGIF ("Kafu.gif", 30, 260, 90, 90, -1, $GUI_WS_EX_PARENTDRAG)

Local $idButton = GUICtrlCreateButton("Hide", 10, 10, 80, 30)
Local $idQuit = GUICtrlCreateButton("Quit", 10, 50, 80, 30)

_WinAPI_SetLayeredWindowAttributes ($hGUI, 0xFFFFFF)

GUISetState(@SW_SHOW, $hGUI)

Local $aCaption[4] = ["Show", "Delete", "Recreate", "Hide"], $iCounter = -1

While True
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE, $idQuit
            Exit

        Case $idButton
            $iCounter += 1
            If $iCounter = 4 Then $iCounter = 0
            GUICtrlSetData($idButton, $aCaption[$iCounter])
            Switch $iCounter
                Case 0 ; hide
                    GUICtrlSetState($IMG_Ctrl4, $GUI_HIDE)
                Case 1 ; show
                    GUICtrlSetState($IMG_Ctrl4, $GUI_SHOW)
                Case 2 ; delete
                    _GUICtrlDeleteAnimGIF($IMG_Ctrl4)
                Case 3 ; recreate
                    $IMG_Ctrl4 = _GUICtrlCreateAnimGIF ("Kafu.gif", 30, 260, 90, 90, -1, $GUI_WS_EX_PARENTDRAG)
                    _WinAPI_SetLayeredWindowAttributes ($hGUI, 0xFFFFFF)
            EndSwitch

    EndSwitch
WEnd

;=============================================
Func _GUICtrlDeleteAnimGIF($idPic)
    AdlibUnRegister(__GIF_Animation_DrawTimer)
    For $iIdx = 0 To UBound($aGIF_Animation) - 1
        If $aGIF_Animation[$iIdx][0] = $idPic Then
            _ArrayDelete($aGIF_Animation, $iIdx)
            GUICtrlDelete($idPic)
            ExitLoop
        EndIf
    Next
    If UBound($aGIF_Animation) > 0 Then AdlibRegister(__GIF_Animation_DrawTimer, 10)
EndFunc   ;==>_GUICtrlDeleteAnimGIF

 

Edited by pixelsearch
Strikethrough 1 line
Link to comment
Share on other sites

Thanks again @PixelSearch.  I will implement your proposal.  However I am a tad reluctant at using your solution in the case of a delete.  By stopping and restarting the adlib function, I am afraid it may cause some unintended flicker (or if you prefer draw delay).  By putting most of the logic inside the adlib function (which is uninterruptible by itself), it will reduce the chance of a side effect (but maybe I am making this a bit too much of a concern).

Link to comment
Share on other sites

2 hours ago, Nine said:

By putting most of the logic inside the adlib function...

Good idea, it works fine too !
For users who are curious, here is a display of the array $aGIF_Animation found in the UDF file, when the example script deletes Kafu's avatar twice (id #6) and recreates it a 3rd time. Rows 3 & 4 (which once had their Col0 = id #6) contain now a Col0 = 0, which means that these 2 rows won't be processed anymore during the Adlib function.

aGIF_Animation.png.13c3c58fe395a86b4a14c40a3607f32a.png

@Nine : do you think it could be interesting to avoid copying each 10ms several arrays into other ones, as this line does in the UDF :

Func __GIF_Animation_DrawTimer()
  Local $aTime, $iNum = 0
  For $i = 0 To UBound($aGIF_Animation) - 1
    ...
    $aTime = $aGIF_Animation[$i][3] ; <======================
    If TimerDiff($aGIF_Animation[$i][5]) >= $aTime[$aGIF_Animation[$i][4] + 1] * 10 Then
    ...

Maybe we could get totally rid of this local $aTime array by doing what follows ?

Func __GIF_Animation_DrawTimer()
  Local $iNum = 0
  For $i = 0 To UBound($aGIF_Animation) - 1
    ...
    If TimerDiff($aGIF_Animation[$i][5]) >= ($aGIF_Animation[$i][3])[$aGIF_Animation[$i][4] + 1] * 10 Then
    ...

Sure the script would be (a bit) less readable, but maybe it's the price to pay for not duplicating each 10ms several arrays. Final word will be yours... and sorry if I constantly comment on this script, it's because I like it! :)

Link to comment
Share on other sites

Interesting.  I made a statistical average of the timerDiff for the overall loop over a period of 10-15 secs.  Both approach are very similar but without the temp array it is slower in general by a few hundreds of ms (around 0.03ms).  Tested on Win7.  Kind of unexpected results to say the least.   However, after thinking about it, It may be explain by the fact that your approach creates internally a copy of the array at every single loop instance ?

Edited by Nine
Link to comment
Share on other sites

Thanks for your last test, I guess only Dev's have the answer to this unexpected result.

I'll try to do soon a test based on a couple of animated gif's having plenty of frames each, then I'll report here to indicate if the array sizes increased (or not) the overall TimerDiff

But it's not really important as for now, your UDF works really fine !

Link to comment
Share on other sites

In case where a script would be creating/deleting a large number of GIF controls, the  UDF will (sort of) start "leaking" some memory that could impact the overall performance.  I have made a modification in order to recuperate slots left empty by the deletion of GIF controls.

New version available :)

Link to comment
Share on other sites

Unfortunately there are some issues now in the UDF, please try these 2 things in it :

#include <Array.au3> ; temporarily for Debug

Func _GUICtrlCreateAnimGIF(...)
  ...
  ...
  _ArrayDisplay($aGIF_Animation, $iIdx)
  Return $idPic
EndFunc   ;==>_GUICtrlCreateAnimGIF

And in the example script, hide, show, delete & recreate $IMG_Ctrl2 instead of $IMG_Ctrl4
(the recreate part of course is  $IMG_Ctrl2 = _GUICtrlCreateAnimGIF("gif-Green-UFO.gif"...)

Now you'll notice a truncated array in ArrayDisplay, with the animated gifs > 2 not animated anymore. Maybe the ReDim $aGIF_Animation[] based on $iIdx creates this issue ? (not tested more)

Also, I'm afraid that if we delete the 1st image, then recreate another one ($iIdx = 0)  it will AdlibRegister again though there are other animated displayed gifs, because  of this line :

If Not $iIdx Then AdlibRegister(...)

Knowing your skill, I'm sure you'll fix this easily :)

Link to comment
Share on other sites

We're not done my friend ;)
I just tested what I wrote at the end of my precedent message : deleting the 1st image & (re)creating a pic at its place in the array will AdlibRegister again. It happens if we work on $IMG_Ctrl1 in the example file, instead of $IMG_Ctrl2 :

; if first GIF, start the timer
If Not $iIdx Then
    ConsoleWrite("AdlibRegister" & @lf)
    AdlibRegister(__GIF_Animation_DrawTimer, 10)
EndIf

Console :
AdlibRegister
AdlibRegister
AdlibRegister
>Exit code: 0    Time: 13.94

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...