GUI Background Scrolling

Get help with using AutoHotkey and its commands and hotkeys
User avatar
TheDewd
Posts: 1496
Joined: 19 Dec 2013, 11:16
Location: USA

GUI Background Scrolling

28 Sep 2015, 10:32

I had the idea to create a scrolling background, and I quickly created this test script, but it has the unwanted side effect of flickering...

Is there a better method to achieve this?

Note: I haven't edited the code so that the background scroll repeats yet...

Code: Select all

; Global =======================================================================
#SingleInstance, Force ; Allow only one running instance of script
#Persistent ; Keep script permanently running until terminated
#NoEnv ; Avoid checking empty variables to see if they're environment variables
#Warn ; Enable warnings to assist with detecting common errors
#NoTrayIcon ; Disable the tray icon of the script
SendMode, Input ; Method for sending keystrokes and mouse clicks
SetWorkingDir, %A_ScriptDir% ; Set the working directory of the script
SetBatchLines, -1 ; Run script at maximum speed
SetControlDelay, -1 ; Sets the delay that will occur after modifying controls
OnExit, ExitSub ; Run a subroutine or function when the script exits
; ==============================================================================

; Script =======================================================================
GUIW := 512, GUIH := 512

Gui, +LastFound -Resize
Gui, Color, 000000
Gui, Margin, 10, 10

Gui, Add, Picture, % " x" 0 " y" 0 " w" 512 " h" 512 " vBackground1", % "stars.png"
Gui, Add, Picture, % " x" 512 " y" 512 " w" 512 " h" 512 " vBackground2", % "stars.png"
Gui, Add, Picture, % " x" 512 " y" 0 " w" 512 " h" 512 " vBackground3", % "stars.png"
Gui, Add, Picture, % " x" 0 " y" 512 " w" 512 " h" 512 " vBackground4", % "stars.png"

Gui, Font, cFFFFFF s26, Arial
Gui, Add, Text, % " x" 0 " y" 0 " w" GUIW " h" GUIH " +0x0201 +BackgroundTrans vText", % "Background Scrolling"

Gui, Show, % " w" GUIW " h" GUIH, % "Application"
GoSub, Scroll
Return ; End automatic execution
; ==============================================================================

; Labels =======================================================================
Scroll:
	Loop
	{
		Speed := 1
		GuiControlGet, BackgroundPos1, Pos, Background1
		GuiControlGet, BackgroundPos2, Pos, Background2
		GuiControlGet, BackgroundPos3, Pos, Background3
		GuiControlGet, BackgroundPos4, Pos, Background4
		GuiControl, MoveDraw, Background1, % " x" BackgroundPos1X-Speed " y" BackgroundPos1Y-Speed
		GuiControl, MoveDraw, Background2, % " x" BackgroundPos2X-Speed " y" BackgroundPos2Y-Speed
		GuiControl, MoveDraw, Background3, % " x" BackgroundPos3X-Speed " y" BackgroundPos3Y-Speed
		GuiControl, MoveDraw, Background4, % " x" BackgroundPos4X-Speed " y" BackgroundPos4Y-Speed
		Sleep, 40
	}
Return

GuiEscape:
GuiClose:
ExitSub:
	ExitApp ; Terminate the script unconditionally
Return
; ==============================================================================
Attachments
stars.png
Resource required for test script
iPhilip
Posts: 658
Joined: 02 Oct 2013, 12:21

Re: GUI Background Scrolling

29 Sep 2015, 14:12

The flickering is caused by several draw commands (in your case the MoveDraw commands) in a rapid sequence and is a common problem when managing Guis and their controls. The solution is double buffering. Here is a quote from Microsoft's page on the topic:
Double buffering uses a memory buffer to address the flicker problems associated with multiple paint operations. When double buffering is enabled, all paint operations are first rendered to a memory buffer instead of the drawing surface on the screen. After all paint operations are completed, the memory buffer is copied directly to the drawing surface associated with it. Because only one graphics operation is performed on the screen, the image flickering associated with complex painting operations is eliminated.
szujeq in this post offers a double buffering solution but it requires aero effects to be disabled. Here is a version of what you posted that uses his RedrawDB.ahk function:

Code: Select all

; Global =======================================================================
#SingleInstance, Force ; Allow only one running instance of script
#Persistent ; Keep script permanently running until terminated
#NoEnv ; Avoid checking empty variables to see if they're environment variables
#Warn ; Enable warnings to assist with detecting common errors
#NoTrayIcon ; Disable the tray icon of the script
SendMode, Input ; Method for sending keystrokes and mouse clicks
SetWorkingDir, %A_ScriptDir% ; Set the working directory of the script
SetBatchLines, -1 ; Run script at maximum speed
SetControlDelay, -1 ; Sets the delay that will occur after modifying controls
OnExit, ExitSub ; Run a subroutine or function when the script exits
; ==============================================================================
 
; Script =======================================================================
GUIW := 512, GUIH := 512
 
Gui, +LastFound -Resize +HwndhWnd   ; Added +HwndhWnd
Gui, Color, 000000
Gui, Margin, 10, 10
 
Gui, Add, Picture, % " x" 0 " y" 0 " w" 512 " h" 512 " vBackground1", % "stars.png"
Gui, Add, Picture, % " x" 512 " y" 512 " w" 512 " h" 512 " vBackground2", % "stars.png"
Gui, Add, Picture, % " x" 512 " y" 0 " w" 512 " h" 512 " vBackground3", % "stars.png"
Gui, Add, Picture, % " x" 0 " y" 512 " w" 512 " h" 512 " vBackground4", % "stars.png"
 
Gui, Font, cFFFFFF s26, Arial
Gui, Add, Text, % " x" 0 " y" 0 " w" GUIW " h" GUIH " +0x0201 +BackgroundTrans vText", % "Background Scrolling"
 
Gui, Show, % " w" GUIW " h" GUIH, % "Application"
DllCall("dwmapi\DwmEnableComposition", "uint", 0)   ; Disable aero effects
GoSub, Scroll
Return ; End automatic execution
; ==============================================================================
 
; Labels =======================================================================
Scroll:
	Loop
	{
		Speed := 1
		GuiControlGet, BackgroundPos1, Pos, Background1
		GuiControlGet, BackgroundPos2, Pos, Background2
		GuiControlGet, BackgroundPos3, Pos, Background3
		GuiControlGet, BackgroundPos4, Pos, Background4
;		GuiControl, MoveDraw, Background1, % " x" BackgroundPos1X-Speed " y" BackgroundPos1Y-Speed
;		GuiControl, MoveDraw, Background2, % " x" BackgroundPos2X-Speed " y" BackgroundPos2Y-Speed
;		GuiControl, MoveDraw, Background3, % " x" BackgroundPos3X-Speed " y" BackgroundPos3Y-Speed
;		GuiControl, MoveDraw, Background4, % " x" BackgroundPos4X-Speed " y" BackgroundPos4Y-Speed
; Added code from here ...
      Gui, -0x10000000
		GuiControl, Move, Background1, % " x" BackgroundPos1X-Speed " y" BackgroundPos1Y-Speed
		GuiControl, Move, Background2, % " x" BackgroundPos2X-Speed " y" BackgroundPos2Y-Speed
		GuiControl, Move, Background3, % " x" BackgroundPos3X-Speed " y" BackgroundPos3Y-Speed
		GuiControl, Move, Background4, % " x" BackgroundPos4X-Speed " y" BackgroundPos4Y-Speed
      Gui, +0x10000000
      RedrawDB(hWnd)
; ... to here
		Sleep, 40
	}
Return
 
GuiEscape:
GuiClose:
ExitSub:
	DllCall("dwmapi\DwmEnableComposition", "uint", 1)   ; Enable aero effects
	ExitApp ; Terminate the script unconditionally
Return
; ==============================================================================

; http://www.autohotkey.com/board/topic/95930-window-double-buffering-redraw-gdi-avoid-flickering/

RedrawDB(hWnd) {
    ;==========================================================================
    ; Get required coordinates
    ;==========================================================================
    Static SizeOfWINDOWINFO := 60
    VarSetCapacity(WINDOWINFO, SizeOfWINDOWINFO, 0)
    NumPut(SizeOfWINDOWINFO, WINDOWINFO, "UInt")
    DllCall("GetWindowInfo", "Ptr",hWnd, "Ptr",&WINDOWINFO, "UInt")
    WindowX := NumGet(WINDOWINFO,  4, "Int")                    ; X coordinate of the window
    WindowY := NumGet(WINDOWINFO,  8, "Int")                    ; Y coordinate of the window
    WindowW := NumGet(WINDOWINFO, 12, "Int") - WindowX            ; Width of the window
    WindowH := NumGet(WINDOWINFO, 16, "Int") - WindowY            ; Height of the window
    ClientX := NumGet(WINDOWINFO, 20, "Int")                    ; X coordinate of the client area
    ClientY := NumGet(WINDOWINFO, 24, "Int")                    ; Y coordinate of the client area
    ClientW := NumGet(WINDOWINFO, 28, "Int") - ClientX            ; Width of the client area
    ClientH := NumGet(WINDOWINFO, 32, "Int") - ClientY            ; Height of the client area
    
    ;==========================================================================
    ; Create Buffer
    ;==========================================================================
    hdcDest :=         DllCall("GetDC", "Ptr",hWnd)
    hdcSrc :=         DllCall("CreateCompatibleDC", "Ptr",hdcDest)  ; buffer
    hbm_buffer :=     DllCall("CreateCompatibleBitmap", "Ptr",hdcDest, "Int",WindowW, "Int",WindowH)
                    DllCall("SelectObject", "Ptr",hdcSrc, "Ptr",hbm_buffer)
    
    ;==========================================================================
    ; Capture - PrintWindow
    ;==========================================================================
    DllCall("PrintWindow", "Ptr",hwnd, "Ptr",hdcSrc, "uint",0) ; PW_CLIENTONLY bugged on XP so GetWindowInfo() or MapWindowPoints() required to capture client area.
    
    ;==========================================================================
    ; Draw - StretchBlt
    ;==========================================================================    
    DllCall("StretchBlt"
    , "Ptr", hdcDest
    , "Int", 0
    , "Int", 0
    , "Int", ClientW
    , "Int", ClientH
    , "Ptr", hdcSrc
    , "Int", ClientX - WindowX
    , "Int", ClientY - WindowY
    , "Int", ClientW
    , "Int", ClientH
    ,"UInt", 0xCC0020) ; SRCCOPY  
    
    ;==========================================================================
    ; Clear
    ;==========================================================================
    DllCall("DeleteDC", "Ptr",hdcDest)
    DllCall("DeleteDC", "Ptr",hdcSrc)
    DllCall("DeleteObject", "Ptr",hbm_buffer)
Return TRUE
}
Another way to implement double buffering without disabling aero effects is to use tic's GDI+ library, which you can find here. Here is a version that works (it requires that you have Gdip.ahk in your library):

Code: Select all

#SingleInstance, Force
#NoEnv
SetBatchLines, -1

File = stars.jpg
Font = Arial                             ; Text font
Options = Center vCenter s26 cffffffff   ; Text options

if !pToken := Gdip_Startup()
{  MsgBox, 48, Gdiplus Error, Gdiplus failed to start. Please ensure you have Gdiplus on your system.
   ExitApp
}
if !Gdip_FontFamilyCreate(Font)
{  MsgBox, 48, Font Error, The font you have specified does not exist on the system.
   ExitApp
}

OnExit, Exit
Gui, -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs +Hwndhwnd
Gui, Show, NA

pBitmap := Gdip_CreateBitmapFromFile(File)   ; Create a bitmap from the file
Width   := Gdip_GetImageWidth(pBitmap)       ; Get the width of the bitmap
Height  := Gdip_GetImageHeight(pBitmap)      ; Get the height of the bitmap

hbm := CreateDIBSection(Width, Height)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)

Loop % Width > Height ? Height+1 : Width+1                                                               ; Loop so that the full image scrolls once
{  Gdip_DrawImage(G, pBitmap, 1-A_Index, 1-A_Index, Width, Height, 0, 0, Width, Height)                  ; Draw the upper-left image
   Gdip_DrawImage(G, pBitmap, Width+1-A_Index, 1-A_Index, Width, Height, 0, 0, Width, Height)            ; Draw the upper-right image
   Gdip_DrawImage(G, pBitmap, Width+1-A_Index, Height+1-A_Index, Width, Height, 0, 0, Width, Height)     ; Draw the lower-right image
   Gdip_DrawImage(G, pBitmap, 1-A_Index, Height+1-A_Index, Width, Height, 0, 0, Width, Height)           ; Draw the lower-left image
   Gdip_TextToGraphics(G, "Background Scrolling", Options, Font, Width, Height)                          ; Draw the text on top of the image
   UpdateLayeredWindow(hwnd, hdc, (A_ScreenWidth-Width)//2, (A_ScreenHeight-Height)//2, Width, Height)   ; Update the window
   Sleep, 40
   Gdip_GraphicsClear(G)
}
Return

Esc::
Exit:
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(G)
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)
ExitApp
Return
I hope this helps.
Windows 10 Pro (64 bit) - AutoHotkey v1.1+ (Unicode 32-bit)

Return to “Ask For Help”

Who is online

Users browsing this forum: Google [Bot], GraffFort, Smile_, wetware05 and 44 guests