Jump to content

GIF Animation


trancexx
 Share

Recommended Posts

It's 19:00 hours.

I've lost TerminateThread function. Instead I'm allowing GIF's thread to exit gracefully. This means there is absolutely no chance for some handle to stay opened because of forceful termination of the thread. Few more little things and that's it for now as far as this script is concerned.

Updated UDF.

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

Great UDF, and nice examples :blink:

Link to comment
Share on other sites

Have you tried following the code inside? If yes, how hard it is with this UDF?

Hmm, never been asked about legibility or aesthetics of another person's code. But taking a quick look now, it seems to be fine, for your style. Its not how I would do it - but that's what makes each programmer/scripter different. For example - I wouldn't recreate structs each call, I would return an array that the caller must use which contains the structures... and I'd use VirtualAlloc instead of having to reset the protection type with VirtualProtect... but again, this is just how I'd do it. You seem to aim for legibility of code, which is fine - in fact - you document a LOT which is great.

There is one problem with your Assembly code though - in x64 code you need the stack 16-byte aligned before a call. While you subtract 80 bytes at the top, this isn't correct alignment - you need 88 bytes added.

This has to do with the way x64 code is always misaligned on procedure entry. In other words - the stack is aligned *before* the call, then the RIP address is pushed, which misaligns it. And yes, this includes threads, which are called from a Thunk somewhere in NTDLL.

The fix? Just change it to 88. However, when you get to more than 4 parameters in API calls you make, things get tricky, since you need the register back-store for the API function (32 bytes), in addition to the stack variables which appear beyond the back-store. I recently coded a call using 10 parameters and it took me a bit of calculating to see where the stack was, where it will be, etc. before I compiled it to binary.

Something interesting I learned is that most C/C++ compilers for 64-bit code just allocate an entire chunk of stack space on procedure entry and use 'mov [rsp+##], xx' instead of 'push rax' for >4 params. However, for me that would require a lot of backwards thinking.. I prefer old-style push's.. but anyway, I'm off topic. I tend to wander :blink:

Anyway, aside from the x64 issue, I don't see any probs with your code.

Link to comment
Share on other sites

Good. Something to discuss.

Structures aren't made to be used as data storages. In fact, they shouldn't be used for that except in some special cases (one I can think now).

I needed something that can be accessed both from regular code and from threads I make for GIFs. To me it was obvious what should I use.

VirtualAlloc allocates in pages. That would likely produce larger memory requirement than functions I use currently. Further more it would require additional overhead because VirtualFree requires size of the region of memory to be specified while calling. Plus that, documentation for VirtualAlloc clearly says that in cases when it's used to allocate for executing dynamically generated code, VirtualProtect function is to be used for setting PAGE_EXECUTE.

I have used VirtualAlloc for the job before, but considering requirements of the GIFAnimation.au3 and written above, combination of GlobalAlloc/VirtualProtect/GlobalFree sounds like a better solution.

Function I'm using in the code, ImageList_DrawEx, takes 10 parameters. I did all the calculations and combinations, peaked to see what's happening in ImageList_DrawEx, and my comments in the code regarding the stack size for the x64 code are correct. Originally I was using the value of 120 bytes (that would be ok in all cases), but wanted to get it more precisely. 80 may sound weird, but it's ok.

I have this in there:

sub rsp, 80d
;...
mov [rsp+56], eax  ; rgbBk
;...
mov [rsp+72], eax  ; fStyle
;...
mov r8, rax  ; hdcDst
mov r9, 0 ; x
mov edx, esi ; i
mov rcx,  $hImageList ; himl
mov rax, $pImageList_DrawEx ; function
call rax
;...
add rsp, 80d
For
BOOL  ImageList_DrawEx(
    HIMAGELIST himl,
    int i,
    HDC hdcDst,
    int x,
    int y,
    int dx,
    int dy,
    COLORREF rgbBk,
    COLORREF rgbFg,
    UINT fStyle
);

push rax is something I wish you haven't written.

x86 is always push, push, push, regardless of number of parameters, nothing new there.

Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

Good. Something to discuss.

Structures aren't made to be used as data storages. In fact, they shouldn't be used for that except in some special cases (one I can think now).

The structures you create take pointers, which means the structures themselves don't take up extra memory. However, they do have an internal structure representing what data is there that is set up within AutoIt. That's the reason I would keep them in an array, so that AutoIt doesn't have to keep recreating the internal representative structures (for lack of a better description).

I needed something that can be accessed both from regular code and from threads I make for GIFs. To me it was obvious what should I use.

VirtualAlloc allocates in pages. That would likely produce larger memory requirement than functions I use currently.

This bit I didn't know about, but in testing, you are correct - VirtualAlloc does create in pages no matter how small the requested storage size.

Further more it would require additional overhead because VirtualFree requires size of the region of memory to be specified while calling.

Using MEM_RELEASE, you don't specify a size - just put 0.

Plus that, documentation for VirtualAlloc clearly says that in cases when it's used to allocate for executing dynamically generated code, VirtualProtect function is to be used for setting PAGE_EXECUTE.

I have just noticed that now, but to tell you the truth that makes very little sense, as using VirtualAlloc already sets the protection based on what you pass. And in testing it in multiple O/S's, it has never failed to work.

In pondering it, I bet its precisely because Microsoft wants to enforce 'good programming practices' - where you set it up initially with 'write' access, write to it, then set it to 'execute' access to allow it to run without DEP issues while keeping anyone from reading/writing to it. Maybe it might stop some AntiVirus warnings too?

Also, an interesting note on VirtualProtect from MSDN you might want to look at regarding HeapAlloc/GlobalAlloc:

It is best to avoid using VirtualProtect to change page protections on memory blocks allocated by GlobalAlloc, HeapAlloc, or LocalAlloc, because multiple memory blocks can exist on a single page. The heap manager assumes that all pages in the heap grant at least read and write access.

(I'm not sure if setting PAGE_EXECUTE_READWRITE will alter the settings of other allocations in the same page? I guess it really doesn't matter since you aren't taking away read or write privileges)

Function I'm using in the code, ImageList_DrawEx, takes 10 parameters. I did all the calculations and combinations, peaked to see what's happening in ImageList_DrawEx, and my comments in the code regarding the stack size for the x64 code are correct. Originally I was using the value of 120 bytes (that would be ok in all cases), but wanted to get it more precisely. 80 may sound weird, but it's ok.

Ahh, okay, I missed where you had a function call with more than 4 params. You use basically the same technique as C compilers then - but again, you still need to align the stack properly before the call. (its on an 8-byte alignment on Thread entry, it needs to be on 16-byte alignment before a call). While I'm not sure it will cause a crash or exception, the information on x64 stack alignment in Windows is very well defined in many places across the web.

push rax is something I wish you haven't written.

x86 is always push, push, push, regardless of number of parameters, nothing new there.

A 'push rax' is 1 byte whereas a 'mov [rsp+##],rax' is 4 bytes. Push's are smaller, so thats why I like to go with them. There's no perceptible difference in performance, so if you dislike pushes, just go with what you like.

*edit: note on VirtualProtect

Edited by Ascend4nt
Link to comment
Share on other sites

Trancexx, you should join the DEV team and write a better image control for built-in AutoIt. this is amazing.

Edited by corgano

0x616e2069646561206973206c696b652061206d616e20776974686f7574206120626f64792c20746f206669676874206f6e6520697320746f206e657665722077696e2e2e2e2e

Link to comment
Share on other sites

Here's some information to help you better understand how the stack should be set up in x64 Assembly:

From Microsoft, Stack Allocation:

The stack will always be maintained 16-byte aligned, except within the prolog (for example, after the return address is pushed)...

From NTCore, Moving to Windows Vista x64:

Now, the most important thing to understand how the space is provided in the stack frame is that the stack has to be 16-byte aligned. In fact, the return address has to be aligned to 16 bytes. So, the stack space will always be something like 16n + 8, where n depends on the number of parameters

A nice diagram of the stack and where 16-byte alignment occurs appears here in the YASM manual, 15.2 win64 Structured Exception Handling, as well as other information on x64 calling convention.

From the MSDN blog 'The Old New Thing', The history of calling conventions, part 5: amd64:

The stack must be kept 16-byte aligned. Since the "call" instruction pushes an 8-byte return address, this means that every non-leaf function is going to adjust the stack by a value of the form 16n+8 in order to restore 16-byte alignment.

From 'Writing 64-bit programs' [web archive link, originally at http://www.jorgon.freeserve.co.uk/GoasmHelp/64bits.htm]:

The stack pointer (RSP) must be 16-byte aligned when making a call to an API. With some APIs this does not matter, but with other APIs wrong stack alignment will cause an exception. Some APIs will handle the exception themselves and align the stack as required (this will, however, cause performance to suffer). Other APIs (at least on early builds of x64) cannot handle the exception and unless you are running the application under debug control, it will exit.

Link to comment
Share on other sites

Thank you Ascend4nt for the links. They are certainly must_read and those that I already haven't bookmarked I will.

For some reason I think you think I don't (who thinks what??) understand stack handling in x64. I said I have peaked inside ImageList_DrawEx and I know how to handle stack.

I did make this post 17 March 2010. It would be bad if I haven't made progress since then.

Edited by trancexx

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

As I have previously pointed out, you don't align the stack properly. 'sub rsp, 80d' keeps the stack misaligned.

Link to comment
Share on other sites

The 'code' doesn't align it for you, but I'm assuming thats a joke and not ignorance. But whatever, I tried to point out the obvious enough times. Call it luck that there aren't any exceptions thrown - or if there are, they are handled by the O/S or a DLL you call.

For those that don't want to risk any ill effects, just change the 'SwapEndian(80, 1)' to 'SwapEndian(88, 1)' in the code (two locations). Or ignore me (after all, I've only been programming in Assembly language since 1991).:blink:

*edit: Just an FYI - I tested code I have here that uses remote code injection (using 64-bit calc.exe) - and by forcing misalignment before an API call, the program crashes due to (presumably) an unhandled exception. So yeah, stack alignment in x64 is *crucial*

Edited by Ascend4nt
Link to comment
Share on other sites

  • 2 months later...

Hi to all,

I'm currently developing a script and I'm trying to create a nice looking gui for it. I'm using gdiplus functions to create a transparent layer and put guictrls on it. Here is a little part of my script:

_GDIPlus_Startup()

Opt("GUIOnEventMode",1)

$Background_PNG = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Background.png")
$EXIT_NORM = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Exit.png")
$MIN_NORM = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Minimize.png")

$GUI = GUICreate("TryOUT GUI", 450, 750, -1, -1, $WS_POPUP, $WS_EX_LAYERED)
SetBitmap($GUI, $Background_PNG, 255)

$CONTROL_GUI = GUICreate("Deneme Child", 470, 270, 5,7, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_MDICHILD), $GUI)

$EXIT_AREA = GUICtrlCreateLabel("",429, 12, 20, 20)
GUICtrlSetCursor(-1, 0)
GUICtrlSetOnEvent(-1, "_Halt")
$MIN_AREA = GUICtrlCreateLabel("",409, 12, 20, 20)
GUICtrlSetCursor(-1, 0)
GUICtrlSetOnEvent(-1, "_Minimize")


GUISetBkColor(0x121314)
_WinAPI_SetLayeredWindowAttributes($CONTROL_GUI, 0x121314)
$ZEROGraphic = _GDIPlus_GraphicsCreateFromHWND($CONTROL_GUI)

$EXIT_BUTTON = _GDIPlus_GraphicsDrawImageRectRect($ZEROGraphic, $EXIT_NORM, 0, 0, 20, 20, 429, 12, 20, 20)
$MIN_BUTTON = _GDIPlus_GraphicsDrawImageRectRect($ZEROGraphic, $MIN_NORM, 0, 0, 20, 20, 409, 12, 20, 20)

GUISetBkColor(0x121314)
_WinAPI_SetLayeredWindowAttributes($CONTROL_GUI, 0x121314)
$ZEROGraphic = _GDIPlus_GraphicsCreateFromHWND($CONTROL_GUI)

$EXIT_BUTTON = _GDIPlus_GraphicsDrawImageRectRect($ZEROGraphic, $EXIT_NORM, 0, 0, 20, 20, 429, 12, 20, 20)
$MIN_BUTTON = _GDIPlus_GraphicsDrawImageRectRect($ZEROGraphic, $MIN_NORM, 0, 0, 20, 20, 409, 12, 20, 20)


GUIRegisterMsg($WM_PAINT, "DrawAll")

GUISetState($GUI_SHOW, $CONTROL_GUI)
GUISetState(@SW_SHOW, $GUI)
GUISetState(@SW_SHOW, $CONTROL_GUI)


While 1
    Sleep(10)
WEnd



Func _Halt()
_GDIPlus_Shutdown()
GUIDelete($CONTROL_GUI)
GUIDelete($GUI)
Exit
EndFunc

Func _Minimize()
GUISetState(@SW_MINIMIZE, $GUI)
EndFunc

Func DrawAll()
_WinAPI_RedrawWindow($CONTROL_GUI, 0, 0, $RDW_UPDATENOW)
$EXIT_BUTTON = _GDIPlus_GraphicsDrawImageRectRect($ZEROGraphic, $EXIT_NORM, 0, 0, 20, 20, 429, 12, 20, 20)
$MIN_BUTTON = _GDIPlus_GraphicsDrawImageRectRect($ZEROGraphic, $MIN_NORM, 0, 0, 20, 20, 409, 12, 20, 20)
EndFunc

Func SetBitmap($hGUI, $hImage, $iOpacity)
    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend,$AC_SRC_ALPHA = 1

    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, "X", _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, "Y", _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, "Alpha", $iOpacity)
    DllStructSetData($tBlend, "Format", $AC_SRC_ALPHA)
    _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)
    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)
EndFunc   ;==>SetBitmap

I want to add an animated progress bar. So I thought I could use trancexx UDF. It works great actually but here is the thing. I need to display only a part of my animated gif as it is the complete progress bar. Here is a basic example for what I'm trying to acheive(Imagine the progress bar is animated :graduated:):

post-41652-12887034066538_thumb.jpg

So does anybody have an idea how to do it?

Thanks everybody in advance.

Sincerely

Tip

-----------------------------------------------------------------------------------------------------------------------------------------------------

P.S.: trancexx, this is a fantastic udf. I really appreciate your efforts to making and sharing it. I'm also very impressed with your coding skills :(... thank you very much...

P.S.2: I've looked at other gif UDFs also but in general "creating the animation" parts are kind of above my understanding. Since I create my other ctrls with this method,

$EXIT_NORM = $EXIT_NORM = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Exit.png")(@ScriptDir & "\Exit.png")

$EXIT_BUTTON = _GDIPlus_GraphicsDrawImageRectRect($ZEROGraphic, $EXIT_NORM, 0, 0, 20, 20, 429, 12, 20, 20),

I thought I could create a progress bar if a function can create an animated gif and return an image object like _GDIPlus_ImageLoadFromFile() function. I couldn't manage to do it but it might a starting point...

Edited by tip

[center]MsgBox_Tipped: Eye candy msgboxes/inputboxes/loginboxes. | CreateBlankBox: Semi-transparent layers with borders and rounded corners.[/center]

Link to comment
Share on other sites

  • 1 month later...

I've updated the code.

It's now possible to specify the size of the control other than the size of the image. Image will be resized accordingly. Also two additional and optional parameters are added. They are for advanced users with special needs. One parameter is to specify rendering style (to apply some graphics effects) and other is for when image alpha channel is specific.

With this new version there is no need to use nor pass so called pGIF by reference to _GUICtrlCreateGIF function. UDF code will do that for you internally. Syntax is very clean and familiar now. Plenty of the examples included in the package.

You can, of course, use it for all types of images, not only gifs.

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

Cool stuff trancexx. GIF animation is also working properly from binary string!

One thing is not working properly in example 6: large images will not shown properly in GUI.

Br,

UEZ

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

  • 1 month later...

Hello @trancexx,

It would be possible to reuse the "ControlID of the new control" returned by "_GUICtrlCreateGIF()" example:

; PE Resource (file "explorer.exe", Type: 2, Name: 6801)
$iCtrlID = _GUICtrlCreateGIF("explorer.exe", "2;6801", 10, 10) ;<- BITMAP
Sleep(2000)
_GUICtrlSetNewGIF($iCtrlID, "GIF.dll", "GIF;4")
It would be great, because there would need to create another "CtrlID"

Thank you for your attention...

http://forum.autoitbrasil.com/ (AutoIt v3 Brazil!!!)

Somewhere Out ThereJames Ingram

somewh10.png

dropbo10.pngDownload Dropbox - Simplify your life!
Your virtual HD wherever you go, anywhere!

Link to comment
Share on other sites

Hello @trancexx,

It would be possible to reuse the "ControlID of the new control" returned by "_GUICtrlCreateGIF()" example:

; PE Resource (file "explorer.exe", Type: 2, Name: 6801)
$iCtrlID = _GUICtrlCreateGIF("explorer.exe", "2;6801", 10, 10) ;<- BITMAP
Sleep(2000)
_GUICtrlSetNewGIF($iCtrlID, "GIF.dll", "GIF;4")
It would be great, because there would need to create another "CtrlID"

Thank you for your attention...

Yes, I'll add something like that for the next time the file is updated. Thanks for suggesting.

♡♡♡

.

eMyvnE

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