Jump to content

Smooth Color Transistions


Go to solution Solved by MattyD,

Recommended Posts

Posted

Recently, I have been experimenting with enabling a random color border coloring transitions (rainbow) effect in external processes with my Immersive UX (DwmColorBlurMica) project.

The idea (and code) comes from the great @argumentum's Win11myOwnBorderColor project.

Since my project would color the borders of whichever happens to be the active window at the time and therefore working with separate processes, I don't believe that I can use _Timer_SetTimer() and related functions because "This window must be owned by the calling thread", unfortunately.

Therefore in my example, I have had to resort to using AdlibRegister.

#include <GUIConstantsEx.au3>

Global $hGUI

Example()

Func Example()
    ; Create a GUI with various controls.
    $hGUI = GUICreate("Example", 400, 400)
    Local $idBtn_OK = GUICtrlCreateButton("OK", 310, 370, 85, 25)

    ; dark titlebar
    _WinAPI_DwmSetWindowAttribute__($hGUI, 20, 1)

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)
    AdlibRegister("BorderMeRandomColorViaTimer", 300)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idBtn_OK
                ExitLoop

        EndSwitch
    WEnd

    ; Delete the previous GUI and all controls.
    AdlibUnRegister("BorderMeRandomColorViaTimer")
    GUIDelete($hGUI)
EndFunc   ;==>Example


Func BorderMeRandomColorViaTimer()
    Local $color = '0x' & Hex(Random(2, 13, 1), 1) & Hex(Random(2, 13, 1), 1) & Hex(Random(2, 13, 1), 1) & Hex(Random(2, 13, 1), 1) & Hex(Random(2, 13, 1), 1) & Hex(Random(2, 13, 1), 1)
    DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $hGUI, 'dword', 34, 'dword*', $color, 'dword', 4) ; border
    ;DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $g_hForm, 'dword', 35, 'dword*', $color, 'dword', 4) ; caption
EndFunc   ;==>BorderMeRandomColorViaTimer

Func _WinAPI_DwmSetWindowAttribute__($hwnd, $attribute = 34, $value = 0x00FF00, $valLen = 4)
    Local $aCall = DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', $attribute, 'dword*', $value, 'dword', $valLen)
    If @error Then Return SetError(@error, @extended, 0)
    If $aCall[0] Then Return SetError(10, $aCall[0], 0)
    Return 1
EndFunc   ;==>_WinAPI_DwmSetWindowAttribute__

 

Now the question that I have and what I am asking for help with is making those color transitions smoother.

There is a recent example in a program that I follow that is in C# that has achieved a very smooth transition of colors. But since I am not very familiar with C#, I don't understand exactly how it is being done. Particularly, the transition of one color to the next.

Link: https://github.com/HotCakeX/Harden-Windows-Security/blob/4966307bc06257b37307d0660e3d821b9944d67b/AppControl Manager/CustomUIElements/AppWindowBorderCustomization.cs#L211-L264

	private static void TickUpdate()
	{
		long currentTimestamp = Stopwatch.GetTimestamp();
		long deltaTicks = currentTimestamp - LastTimestamp;
		LastTimestamp = currentTimestamp;

		// Convert ticks to seconds.
		float deltaSeconds = deltaTicks * TickToSeconds;

		// Increment hue based on elapsed time and speed.
		Hue += deltaSeconds * InverseSpeed;

		// Wrap hue to [0,1) without using modulo.
		if (Hue >= 1f)
		{
			// (int)Hue removes the integer portion (if a long pause made it exceed by more than 1).
			Hue -= (int)Hue;
		}

		#region Convert hue to RGB border color.

		float t = Hue * 6f;

		float r = MathF.Abs(t - 3f) - 1f;
		float g = 2f - MathF.Abs(t - 2f);
		float b = 2f - MathF.Abs(t - 4f);

		r = r < 0f ? 0f : (r > 1f ? 1f : r);
		g = g < 0f ? 0f : (g > 1f ? 1f : g);
		b = b < 0f ? 0f : (b > 1f ? 1f : b);

		byte rr = (byte)(r * 255f);
		byte gg = (byte)(g * 255f);
		byte bb = (byte)(b * 255f);

		// https://learn.microsoft.com/windows/win32/gdi/colorref
		// COLORREF format expected: 0x00BBGGRR
		uint computedBorderColor = (uint)((bb << 16) | (gg << 8) | rr);

		#endregion

		// Main Apply
		int result = NativeMethods.DwmSetWindowAttribute(GlobalVars.hWnd, DWMWA_BORDER_COLOR, ref computedBorderColor, sizeof(uint));
		if (result != 0)
		{
			// If setting the border color failed, stop the timer to avoid further errors.
			if (Timer is not null && Timer.IsRunning)
			{
				Timer.Stop();
			}

			Logger.Write($"Failed to set window border color. DwmSetWindowAttribute returned error code: {result}", LogTypeIntel.Error);
		}
	}

 

The app is in the Microsoft Store in case anyone wants to see the smooth transitions. It's called AppControl Manager and the rainbow border effect is in the Settings for it. Although many of you will probably understand better what is happening in the C# code.

Does anybody have any ideas on what can be done to make the color transition appear smoother?

I may be somewhat limited since I don't think I can use timers in external processes.

Thank you for your time. :)

Posted

8:00 here. Working on a Sunday :(
Anywayz,
.. float t = Hue * 6f;    is the same in AutoIt,   $t = $Hue * 0x6f
.. float r = MathF.Abs(t - 3f) - 1f;   would be   $r = Abs($t - 0x3f)

if nobody does it, I'll do it ... next week ? 😅

PS: you should be able to do it by throwing code at it. I have faith in you :cheer:

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted

I'm sorry to hear that you got stuck working early on a Sunday of all days.

Thank you, I appreciate it. I will see what I can figure out with the color transitions. But I also don't really understand the timing of it either.

Other users requested this feature, so it's not something I have thought much about. I did not even think that it could potentially look nice. But after seeing how smooth it is in that C# program, I can understand it can look nice if done right. It's really quite beautiful, surprisingly.

Posted
1 hour ago, argumentum said:

.. float t = Hue * 6f;    is the same in AutoIt,   $t = $Hue * 0x6f
.. float r = MathF.Abs(t - 3f) - 1f;   would be   $r = Abs($t - 0x3f)

f in C# means float, but you are treating them as Hex.

So it's just $t =$Hue * 6 in AU3, removing the f.

Some guy's script + some other guy's script = my script!

Posted

So I talked to the developer of that program to get an understanding of what is happening in that code since I don't understand C# at all.

Even though I somehow magically committed a bunch of C# code to Microsoft which Microsoft has actually shipped. Yeah they shipped my C# code and I had no idea what I was doing. 😆

Anyway, she said that when it colors the border with a main color, let's say blue, it will then hit every shade of blue on the way to the next color, let's say green. And then it will hit every shade of green on the way to the next color and so on.

So that is how the border color transitions appear so smooth. And somehow she manages to do it very efficiently as well which is nice.

I'm still not 100% sure that I can do this properly with only AdlibRegister() and not have access to _Timer_*() functions.

Posted (edited)
1 hour ago, WildByDesign said:

I'm still not 100% sure that I can do this properly with only AdlibRegister() and not have access to _Timer_*() functions.

..what are the options: run in same process or span another process. Make faster small updates or make longer taking jump from color to color.
I would span another process as the rainbow feature is independent of the main script.
IPC seems like OMG !. About this IPC:

#include <GUIConstantsEx.au3>

Exit main()
Func main()
    If StringInStr($CmdLineRaw, "/rainbowThing") Then Return rainbow()
    Opt("GUIOnEventMode", 1)
    GUICreate(@ScriptName & " - main", 300, 200)
    GUISetOnEvent($GUI_EVENT_CLOSE, "SpecialEvents")
    GUICtrlCreateButton("  stop the other process  ", 10, 10)
    GUICtrlSetOnEvent(-1, main_BttnStop)
    GUICtrlCreateInput("  something from the other process  ", 10, 40, 280)
    GUISetState()
    ShellExecute(@AutoItExe, '"' & @ScriptFullPath & '" /rainbowThing')
    While Sleep(300)
    WEnd
EndFunc   ;==>main

Func main_BttnStop()
    ConsoleWrite('+ Func main_BttnStop()' & @CRLF)
    WinClose(@ScriptName & " - rainbow")
EndFunc   ;==>main_BttnStop

Func SpecialEvents()
    Switch @GUI_CtrlId
        Case $GUI_EVENT_CLOSE
            ControlSetText(@ScriptName & " - main", "", "[CLASS:Edit; INSTANCE:1]", "good bye world @" & @HOUR & ":" & @MIN & ":" & @SEC & "." & @MSEC)
            GUIDelete()
            Exit
    EndSwitch
EndFunc   ;==>SpecialEvents

Func rainbow()
    Opt("GUIOnEventMode", 1)
    GUICreate(@ScriptName & " - rainbow", 300, 100)
    GUISetOnEvent($GUI_EVENT_CLOSE, "SpecialEvents")
    Local $idBttn = GUICtrlCreateButton("  my rainbow process  ", 10, 10)
;~  GUISetState() ; or not. Is not needed
    While Sleep(300)
        GUICtrlSetData($idBttn, @HOUR & ":" & @MIN & ":" & @SEC & "." & @MSEC)
    WEnd
EndFunc   ;==>rainbow

it does Interprocess communication via something else but the use is the same. You'll do fancy IPC when you get more experience :)

Edited by argumentum
better

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
8 hours ago, argumentum said:

..what are the options: run in same process or span another process. Make faster small updates or make longer taking jump from color to color.
I would span another process as the rainbow feature is independent of the main script.

I think that you are right about keeping the rainbow thing independent from the main script. Thanks for this example script of passing information between scripts. It's definitely an area that I need to learn more about. And your example script is a perfect starting point.

  • Solution
Posted (edited)

This is quite a clever bit of code!

The internal timer is a reference, so doesn't matter when you call the function - it'll always calculate the "correct" colour based on when it was called. The function was being called periodically based on a second external timer. But adlib should happily work for us, or we can just call it in a loop.

A hue value is then calculated based on that internal timer. It is a value between 0 and 1, then the RGB values are based on that. We can see this in action...

For $i = 0 To 9
    WriteVals($i/10)
Next

Func WriteVals($fHue)
    ;T = presumably "transition" value.  So RGB values are all based on this.
    Local $fT = 6 * $fHue
    Local $nR = Abs($fT - 3) - 1
    Local $nG = 2 - Abs($fT - 2)
    Local $nB = 2 - Abs($fT - 4)

    ;Max out at 0 and 1.
    $nR = ($nR < 0) ? 0 : ($nR > 1) ? 1 : $nR
    $nG = ($nG < 0) ? 0 : ($nG > 1) ? 1 : $nG
    $nB = ($nB < 0) ? 0 : ($nB > 1) ? 1 : $nB

    ConsoleWrite(StringFormat("Hue: %1.2f", $fHue, $nR, $nG, $nB)& @CRLF)
    ConsoleWrite(StringFormat("R:%1.2f G:%1.2f B:%1.2f", $nR, $nG, $nB)& @CRLF)

    ;make percentage of max values!
    $nR *= 255
    $nG *= 255
    $nB *= 255

    ConsoleWrite(StringFormat("R:%4d G:%4d B:%4d" , $nR, $nG, $nB)& @CRLF & @CRLF)
EndFunc

And here's that implemented based on time:

#include <GUIConstantsEx.au3>

Global $hGUI, $fSpeed = 4

Example()

Func Example()
    ; Create a GUI with various controls.
    $hGUI = GUICreate("Example", 400, 400)
    Local $idBtn_OK = GUICtrlCreateButton("OK", 310, 370, 85, 25)

    ; dark titlebar
    _WinAPI_DwmSetWindowAttribute__($hGUI, 20, 1)

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idBtn_OK
                ExitLoop

        EndSwitch
        DoBorder($hGUI)
    WEnd

    GUIDelete($hGUI)
EndFunc   ;==>Example

Func DoBorder($hWnd)
    Local Const $fInverseSpeed = 1/$fSpeed
    Local Static $fHue

    ;the border col will be calculated based on the time elapsed from a single timer.
    Local Static $hTimer = TimerInit()
    Local Static $fLastTime
    Local $fCurTime = TimerDiff($hTimer)
    Local $fTimerSec = ($fCurTime - $fLastTime)/1000
    If $fTimerSec < 1/25 Then Return ;Slow down processing (do not needlessly refresh faster than 25hz)
    $fLastTime = $fCurTime

    ;Hue is a value between 0 and 1.
    ;Its value is based on the internal timer..
    $fHue += ($fTimerSec * $fInverseSpeed)
    If $fHue >= 1 Then $fHue -= Int($fHue)

    ;T = transition value.  So RGB values are all based on this.
    ;We want RGB values to be between 0 and 1.
    Local $fT = 6 * $fHue
    Local $fR = Abs($fT - 3) - 1
    Local $fG = 2 - Abs($fT - 2)
    Local $fB = 2 - Abs($fT - 4)

    ;Max values out at 0 and 1.
    $fR = ($fR < 0) ? 0 : ($fR > 1) ? 1 : $fR
    $fG = ($fG < 0) ? 0 : ($fG > 1) ? 1 : $fG
    $fB = ($fB < 0) ? 0 : ($fB > 1) ? 1 : $fB

    ;RGB is now a percentage of max values
    $fR *= 255
    $fG *= 255
    $fB *= 255

    ;Construct RGB Value. (reverse order due to endianness!)
    Local $iRGB = BitOr(BitShift(Int($fB), -16), BitShift(Int($fG), -8), Int($fR))

    DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', 34, 'dword*', $iRGB, 'dword', 4)
EndFunc   ;==>BorderMeRandomColorViaTimer

Func _WinAPI_DwmSetWindowAttribute__($hwnd, $attribute = 34, $value = 0x00FF00, $valLen = 4)
    Local $aCall = DllCall('dwmapi.dll', 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', $attribute, 'dword*', $value, 'dword', $valLen)
    If @error Then Return SetError(@error, @extended, 0)
    If $aCall[0] Then Return SetError(10, $aCall[0], 0)
    Return 1
EndFunc   ;==>_WinAPI_DwmSetWindowAttribute__

Edit: I'm a bit confused as to why we're jumping into IPC stuff? - was the problem with adlib that you can't supply the window handle as a param? I'd just  store the target window handle as a global var to do it that way... - anyway FWIW I've updated the example so you can specify a target window.

Edited by MattyD
Posted
1 hour ago, MattyD said:

Edit: I'm a bit confused as to why we're jumping into IPC stuff?

I think that, if the rainbow effect is too fast and/or free the menus and what not, from freezing the effect, forking would work.
It it was my toy to play with, given the extent of the features, I'd make a script just for menus and option, and IPC everything to other concurrent scripts.

But is a trial and error thing. If it all works fine within the same instance, keep it simple, and use just one instance.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
2 hours ago, MattyD said:

The internal timer is a reference, so doesn't matter when you call the function - it'll always calculate the "correct" colour based on when it was called. The function was being called periodically based on a second external timer. But adlib should happily work for us, or we can just call it in a loop.

Matty, this is the second time this month that you have left me speechless. I can't thank you enough. The smoothness of the color transition really is a beautiful thing. It kind of reminds of those LED lights around some TVs these days. Very smooth.

2 hours ago, MattyD said:

Edit: I'm a bit confused as to why we're jumping into IPC stuff? - was the problem with adlib that you can't supply the window handle as a param? I'd just  store the target window handle as a global var to do it that way... - anyway FWIW I've updated the example so you can specify a target window.

Thank you for the update. Being able to supply the window handle is definitely a bonus.

So I did end up having some trouble with the smoothness of the transitions in the project that I am aiming to place it in which is Immersive UX. Since it uses a SetWinEventHook and therefore hooking all processes on the machine, it is really a fine-tuned project. It has to be, of course.

I tried using AdlibRegister and even matched approx. 15ms timing for the adlib which I measured from your While loop. The problem is not with your code, it's just that there is way too much going on in Immersive UX that the hook gets in the way of the timing needed for the code that you shared.

@argumentum was right and predicted (before I even realized it) that I would likely need to run this border color transition functionality in a separate process. Therefore, tomorrow I will spend some time creating a separate process with a mini version of the hook that only deals with the border color transition stuff.

It only has to target whatever the current Active process is. I can probably do that without a hook in the second process as well. I'll try a few things tomorrow and see which is the most efficient.

Anyway, I will follow up tomorrow. Thank you so much. I really could not comprehend that C# code and you ported it over to AutoIt and it works exquisitely.

Posted (edited)

My pleasure mate :)

1 hour ago, argumentum said:

I think that, if the rainbow effect is too fast and/or free the menus and what not, from freezing the effect, forking would work.

Ok fair enough. 

Just as an eye test, we probably on need to refresh this every 100ms or so to keep the animation relatively smooth. At that pace I wouldn't think we'd run into responsiveness issues caused by too much border-ing.  But yes, if the process is too busy to call DoBorder() then the  animation would obviously pause. 

35 minutes ago, WildByDesign said:

tried using AdlibRegister and even matched approx. 15ms timing for the adlib which I measured from your While loop

That function shouldn't really do anything until that "1/25" seconds has elapsed.. so would adlib a bit slower and save some resources ;) Try 100ms to start, and if it looks janky you can speed it up a little bit from there.

Adlibing super fast can also cause issues, this is because the adlib gap could be smaller than the available idle time.. In that case you'll just endup with a backlog of adlib tasks that can't be processed, and you'll probably ramp the cpu. To give you an idea, even doing a Sleep(1) will take around 10 - 15ms by the time the process goes to sleep and wakes up again.

Edited by MattyD
Posted
8 hours ago, MattyD said:

Just as an eye test, we probably on need to refresh this every 100ms or so to keep the animation relatively smooth. At that pace I wouldn't think we'd run into responsiveness issues caused by too much border-ing.  But yes, if the process is too busy to call DoBorder() then the  animation would obviously pause. 

I ended up getting it working smoothly in a separate process (that works together with the main process) without ending up needing to use the event hook or adlib which is nice, less overhead that way. I just ended up using a 50ms loop which was smoother than a 100ms loop that I also tested. It basically just checks for the handle of the active window and keeps track of the previous window handle as well. So when a new window becomes the active window, it will remove the border color from the previous window that is no longer active and the border coloring continues on the newly active window.

Can I have your permission to use that function in my Immersive UX project?

I assume that you likely don't mind, but I prefer to ask anyway. :)

Posted (edited)

experimentally:

#include <GUIConstantsEx.au3>
#include <WinAPIGdi.au3>
#include <WindowsConstants.au3>
#include <WinAPIEx.au3>
#include <Timers.au3>


Global $hGUI, $idColor1, $idColor2, $idColor3

Example()

Func Example()
    ; Create a GUI.
    $hGUI = GUICreate("HLS Rainbow Controls Example", 500, 300, -1, -1, $WS_POPUP)
    $idColor1 = GUICtrlCreateLabel("", 0, 0, 500, 30, -1, $GUI_WS_EX_PARENTDRAG)
    $idColor2 = GUICtrlCreateLabel("", 0, 100, 500, 30)
    $idColor3 = GUICtrlCreateLabel("", 0, 200, 500, 30)
    Local $idBtn = GUICtrlCreateButton("CLOSE", 400, 250, 85, 30)

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    ; set timer to check every nnn
    Local $i_TimerInterval = 40
    _Timer_SetTimer($hGUI, $i_TimerInterval, "_TimerCheck")

    ; Register the time-based color update function.
    AdlibRegister("RandomColorViaTimer", 40)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idBtn
                ExitLoop
        EndSwitch
        DoBorder()
    WEnd

    ; Cleanup
    ConsoleWrite("Killed All Timers? " & _Timer_KillAllTimers($hGUI) & @CRLF)
    AdlibUnRegister("RandomColorViaTimer")
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func RandomColorViaTimer()
    Local Const $iHueSpeed = 3
    Local Const $iMaxHLS = 240
    Local Const $iLuminance = 120
    Local Const $iSaturation = 240

    Local Static $iHue = 0
    Local Static $hTimer = TimerInit()

    ; Calculate Hue
    $iHue = Mod($iHue + $iHueSpeed, $iMaxHLS)

    ; Time for a full Hue cycle
    If $iHue = 0 Then
        ConsoleWrite("<< << << Adlib: Time for a full Hue cycle: " & Round(TimerDiff($hTimer) / 1000, 3) & " seconds >> >> >>" & @LF)
        $hTimer = TimerInit()
    EndIf

    ; Convert HLS (0-240) to RGB
    Local $iColorRef = _WinAPI_ColorHLSToRGB($iHue, $iLuminance, $iSaturation)
    Local $sRGB = '0x' & Hex(_WinAPI_ColorHLSToRGB($iHue, $iLuminance, $iSaturation), 6)

    ; Apply Colors
    GUICtrlSetBkColor($idColor1, $sRGB)

    Local $sRef = _ColorRef($sRGB)
    ConsoleWrite("Adlib: " & $sRef & @CRLF)

EndFunc   ;==>RandomColorViaTimer

; Timer call back function
Func _TimerCheck($hWnd, $iMsg, $iIDTimer, $iTime)
    #forceref $hWnd, $iMsg, $iIDTimer, $iTime

    Local Const $iHueSpeed = 3
    Local Const $iMaxHLS = 240
    Local Const $iLuminance = 120
    Local Const $iSaturation = 240

    Local Static $iHue = 160
    Local Static $hTimer = TimerInit()

    ; Calculate Hue (reverse to fit with DoBorder)
    $iHue -= $iHueSpeed
    If $iHue < 0 Then $iHue = $iMaxHLS

    ; Time for a full Hue cycle
    If $iHue = $iMaxHLS Then
        ConsoleWrite("<< << << Timer: Time for a full Hue cycle: " & Round(TimerDiff($hTimer) / 1000, 3) & " seconds >> >> >>" & @LF)
        $hTimer = TimerInit()
    EndIf

    ; Convert HLS (0-240) to RGB
    Local $iColorRef = _WinAPI_ColorHLSToRGB($iHue, $iLuminance, $iSaturation)
    Local $sRGB = '0x' & Hex(_WinAPI_ColorHLSToRGB($iHue, $iLuminance, $iSaturation), 6)

    ; Apply Colors
    GUICtrlSetBkColor($idColor2, $sRGB)

    Local $sRef = _ColorRef($sRGB)
    ConsoleWrite("Timer: " & $sRef & @CRLF)

EndFunc   ;==>_TimerCheck

Func DoBorder()
    Local Const $fSpeed = 4
    Local Const $fInverseSpeed = 1 / $fSpeed
    Local Static $fHue

    ;the border col will be calculated based on the time elapsed from a single timer.
    Local Static $hTimer = TimerInit()
    Local Static $hTimer1 = TimerInit()
    Local Static $fLastTime
    Local $fCurTime = TimerDiff($hTimer)
    Local $fTimerSec = ($fCurTime - $fLastTime) / 1000
    If $fTimerSec < 1 / 25 Then Return ;Slow down processing (do not needlessly refresh faster than 25hz)
    $fLastTime = $fCurTime

    ;Hue is a value between 0 and 1.
    ;Its value is based on the internal timer..
    $fHue += ($fTimerSec * $fInverseSpeed)
    If $fHue >= 1 Then
        $fHue -= Int($fHue)
        ConsoleWrite("<< << << Bordr: Time for a full Hue cycle: " & Round(TimerDiff($hTimer1) / 1000, 3) & " seconds >> >> >>" & @LF)
        $hTimer1 = TimerInit()
    EndIf

    ;T = transition value.  So RGB values are all based on this.
    ;We want RGB values to be between 0 and 1.
    Local $fT = 6 * $fHue
    Local $fR = Abs($fT - 3) - 1
    Local $fG = 2 - Abs($fT - 2)
    Local $fB = 2 - Abs($fT - 4)

    ;Max values out at 0 and 1.
    $fR = ($fR < 0) ? 0 : ($fR > 1) ? 1 : $fR
    $fG = ($fG < 0) ? 0 : ($fG > 1) ? 1 : $fG
    $fB = ($fB < 0) ? 0 : ($fB > 1) ? 1 : $fB

    ;RGB is now a percentage of max values
    $fR *= 255
    $fG *= 255
    $fB *= 255

    ;Construct RGB Value. (reverse order due to endianness!)
    Local $iRGB = BitOR(BitShift(Int($fB), -16), BitShift(Int($fG), -8), Int($fR))

    GUICtrlSetBkColor($idColor3, $iRGB)

    Local $sRef = _ColorRef($iRGB)
    ConsoleWrite("Bordr: " & $sRef & @CRLF)

EndFunc   ;==>DoBorder

Func _ColorRef($sRGB)
    ; Optionaly RGB analysis
    Local $iHue, $iLuminance, $iSaturation
    _WinAPI_ColorRGBToHLS($sRGB, $iHue, $iLuminance, $iSaturation)
    Local $R = _WinAPI_GetRValue($sRGB)
    Local $G = _WinAPI_GetGValue($sRGB)
    Local $B = _WinAPI_GetBValue($sRGB)
    Local $iAutoItColor = _WinAPI_RGB($R, $G, $B)
    Local $sHexColor = "0x" & Hex($iAutoItColor, 6)
    Local $sRef = "HLS(" & $iHue & "," & $iLuminance & "," & $iSaturation & ") => RGB(" & $R & "," & $G & "," & $B & ") => " & $sHexColor
    Return $sRef
EndFunc   ;==>_ColorRef

 

Edited by ioa747

I know that I know nothing

Posted (edited)

Here is a fun example that does the smooth color animation for the border and for the blur behind blend color as well.

Mesmerizing! 🤩 

 

Spoiler
#include <GUIConstantsEx.au3>
#include <WinAPIHObj.au3>
#include <WinAPIGdi.au3>

Global $hGUI, $fSpeed = 4
Global $hDwmapi = DllOpen('dwmapi.dll')
Global $hUser32 = DllOpen('user32.dll')
Global $hGdi = DllOpen('gdi32.dll')

Global $WCA_ACCENT_POLICY = 19
Global $ACCENT_DISABLED = 0
Global $ACCENT_ENABLE_GRADIENT = 1
Global $ACCENT_ENABLE_TRANSPARENTGRADIENT = 2
Global $ACCENT_ENABLE_BLURBEHIND = 3
Global $ACCENT_ENABLE_ACRYLICBLURBEHIND = 4
Global $ACCENT_ENABLE_HOSTBACKDROP = 5
Global $ACCENT_INVALID_STATE = 6

DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext" , "HWND", "DPI_AWARENESS_CONTEXT" -4)

Example()

Func Example()
    ; Create a GUI with various controls.
    $hGUI = GUICreate("Example", 400, 400)
    Local $idBtn_OK = GUICtrlCreateButton("OK", 310, 370, 85, 25)

    ; dark titlebar
    _WinAPI_DwmSetWindowAttribute__($hGUI, 20, 1)

    $hRgn = _WinAPI_CreateEllipticRgn(_WinAPI_CreateRectEx(0, 0, 0, 0))
    _WinAPI_DwmEnableBlurBehindWindow_mod($hGUI, 1, 0, $hRgn)
    _WinAPI_DwmEnableBlurBehindWindow_mod($hGUI, 1, 0, 0)
    If $hRgn Then
        _WinAPI_DeleteObject($hRgn)
    EndIf

    _WinAPI_DwmExtendFrameIntoClientArea_mod($hGUI, _WinAPI_CreateMargins(-1, -1, -1, -1))

    ; Display the GUI.
    GUISetState(@SW_SHOW, $hGUI)

    ; Loop until the user exits.
    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idBtn_OK
                ExitLoop

        EndSwitch

        DoBorder($hGUI)

    WEnd

    GUIDelete($hGUI)
EndFunc   ;==>Example

Func DoBorder($hWnd)
    Local Const $fInverseSpeed = 1/$fSpeed
    Local Static $fHue

    ;the border col will be calculated based on the time elapsed from a single timer.
    Local Static $hTimer = TimerInit()
    Local Static $fLastTime
    Local $fCurTime = TimerDiff($hTimer)
    Local $fTimerSec = ($fCurTime - $fLastTime)/1000
    If $fTimerSec < 1/25 Then Return ;Slow down processing (do not needlessly refresh faster than 25hz)
    $fLastTime = $fCurTime

    ;Hue is a value between 0 and 1.
    ;Its value is based on the internal timer..
    $fHue += ($fTimerSec * $fInverseSpeed)
    If $fHue >= 1 Then $fHue -= Int($fHue)

    ;T = transition value.  So RGB values are all based on this.
    ;We want RGB values to be between 0 and 1.
    Local $fT = 6 * $fHue
    Local $fR = Abs($fT - 3) - 1
    Local $fG = 2 - Abs($fT - 2)
    Local $fB = 2 - Abs($fT - 4)

    ;Max values out at 0 and 1.
    $fR = ($fR < 0) ? 0 : ($fR > 1) ? 1 : $fR
    $fG = ($fG < 0) ? 0 : ($fG > 1) ? 1 : $fG
    $fB = ($fB < 0) ? 0 : ($fB > 1) ? 1 : $fB

    ;RGB is now a percentage of max values
    $fR *= 255
    $fG *= 255
    $fB *= 255

    ;Construct RGB Value. (reverse order due to endianness!)
    Local $iRGB = BitOr(BitShift(Int($fB), -16), BitShift(Int($fG), -8), Int($fR))

    DllCall($hDwmapi, 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', 34, 'dword*', $iRGB, 'dword', 4)

    _WinAPI_DwmEnableBlurBehindWindow11($hWnd, $ACCENT_ENABLE_ACRYLICBLURBEHIND, True, $iRGB, 50)

EndFunc   ;==>BorderMeRandomColorViaTimer

Func _WinAPI_DwmSetWindowAttribute__($hwnd, $attribute = 34, $value = 0x00FF00, $valLen = 4)
    Local $aCall = DllCall($hDwmapi, 'long', 'DwmSetWindowAttribute', 'hwnd', $hWnd, 'dword', $attribute, 'dword*', $value, 'dword', $valLen)
    If @error Then Return SetError(@error, @extended, 0)
    If $aCall[0] Then Return SetError(10, $aCall[0], 0)
    Return 1
EndFunc   ;==>_WinAPI_DwmSetWindowAttribute__

Func _WinAPI_DwmEnableBlurBehindWindow_mod($hWnd, $bEnable = True, $bTransition = False, $hRgn = 0)
    Local $tBLURBEHIND = DllStructCreate('dword;bool;handle;bool')
    Local $iFlags = 0

    If $hRgn Then
        $iFlags += 2
        DllStructSetData($tBLURBEHIND, 3, $hRgn)
    EndIf

    DllStructSetData($tBLURBEHIND, 1, BitOR($iFlags, 0x05))
    DllStructSetData($tBLURBEHIND, 2, $bEnable)
    DllStructSetData($tBLURBEHIND, 4, $bTransition)

    Local $aCall = DllCall($hDwmapi, 'long', 'DwmEnableBlurBehindWindow', 'hwnd', $hWnd, 'struct*', $tBLURBEHIND)
    If @error Then Return SetError(@error, @extended, 0)
    If $aCall[0] Then Return SetError(10, $aCall[0], 0)

    Return 1
EndFunc   ;==>_WinAPI_DwmEnableBlurBehindWindow_mod

Func _WinAPI_DwmExtendFrameIntoClientArea_mod($hWnd, $tMARGINS = 0)
    DllCall($hDwmapi, 'long', 'DwmExtendFrameIntoClientArea', 'hwnd', $hWnd, 'struct*', $tMARGINS)
EndFunc   ;==>_WinAPI_DwmExtendFrameIntoClientArea_mod

; Function by argumentum
Func _percentageOfHex($iVal)
    $iVal = Int($iVal > 99 ? 100 : ($iVal < 1 ? 0 : $iVal)) ; no more than 100% or less than 0%
    Return Hex(Ceiling(($iVal * 100) * (2.55 * 100) / (100 * 100)), 2) ; calculate in integers, as floating point numbers suck in a CPU
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: _WinAPI_DwmEnableBlurBehindWindow11
; Description ...: Enables Aero-like blurred background in Windows 11.
; Syntax ........: _WinAPI_DwmEnableBlurBehindWindow11($hWnd[, $AccentState, $BlendColor, $ColorOpacity, $AccentFlags, $AnimationId])
; Parameters ....: $hWnd                - Window handle
;                  $AccentState         - [optional] Enable or disable the blur effect
;                  $IncludeCaption      - [optional] Extend effects to the titlebar
;                  $BlendColor          - [optional] Sets GradientColor
;                  $ColorOpacity        - [optional] Sets blending color opacity value
;                  $AccentFlags         - [optional] Sets AccentFlags value
;                  $AnimationId         - [optional] Sets AnimationId value
; Return values .: 1 on success, 0 otherwise. Call _WinAPI_GetLastError on failure for more information.
; Author ........: scintilla4evr
; Modified ......: WildByDesign - added $AccentState, $BlendColor, $ColorOpacity, $AccentFlags and $AnimationId
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: Yes
;    ACCENT_DISABLED = 0
;    ACCENT_ENABLE_GRADIENT = 1
;    ACCENT_ENABLE_TRANSPARENTGRADIENT = 2
;    ACCENT_ENABLE_BLURBEHIND = 3
;    ACCENT_ENABLE_ACRYLICBLURBEHIND = 4
;    ACCENT_ENABLE_HOSTBACKDROP = 5
;    ACCENT_INVALID_STATE = 6
; ===============================================================================================================================
Func _WinAPI_DwmEnableBlurBehindWindow11($hWnd, $AccentState = $ACCENT_ENABLE_ACRYLICBLURBEHIND, $IncludeCaption = True, $BlendColor = "", $ColorOpacity = "", $AccentFlags = 0, $AnimationId = 0)
    If $AccentState And $IncludeCaption Then
        Local $hRgn = _WinAPI_CreateEllipticRgn_mod(_WinAPI_CreateRectEx(0, 0, 0, 0))
        _WinAPI_DwmEnableBlurBehindWindow_mod($hWnd, 1, 0, $hRgn)
        If $hRgn Then _WinAPI_DeleteObject($hRgn)
    EndIf
    If Not $AccentState Then _WinAPI_DwmEnableBlurBehindWindow_mod($hWnd, 0)
    Local $tAccentPolicy = DllStructCreate("int AccentState; int AccentFlags; int GradientColor; int AnimationId")
    Local $tAttrData = DllStructCreate("dword Attribute; ptr DataBuffer; ulong Size")
    $tAccentPolicy.AccentState = $AccentState
    If $AccentState = $ACCENT_ENABLE_TRANSPARENTGRADIENT And $AccentFlags = 0 Then $AccentFlags = 2
    $tAccentPolicy.AccentFlags = $AccentFlags
    If $BlendColor Then
        Local $iVal = Int($ColorOpacity > 99 ? 100 : ($ColorOpacity < 1 ? 0 : $ColorOpacity)) ; no more than 100% or less than 0%
        Local $sTransparencyHex = Hex(Ceiling(($iVal * 100) * (2.55 * 100) / (100 * 100)), 2)
        $tAccentPolicy.GradientColor = '0x' & $sTransparencyHex & Hex($BlendColor, 6)
    EndIf
    $tAccentPolicy.AnimationId = $AnimationId
    $tAttrData.Attribute = $WCA_ACCENT_POLICY
    $tAttrData.DataBuffer = DllStructGetPtr($tAccentPolicy)
    $tAttrData.Size = DllStructGetSize($tAccentPolicy)

    Local $aResult = DllCall($hUser32, "bool", "SetWindowCompositionAttribute", "hwnd", $hWnd, "ptr", DllStructGetPtr($tAttrData))
    If @error Then Return SetError(@error, @extended, 0)

    If $AccentState And $IncludeCaption Then _WinAPI_DwmExtendFrameIntoClientArea_mod($hWnd, _WinAPI_CreateMargins(-1, -1, -1, -1))
    If Not $AccentState And $IncludeCaption Then
        Local $sClassName = _WinAPI_GetClassName_mod($hWnd)
        If $sClassName <> "CabinetWClass" Then _WinAPI_DwmExtendFrameIntoClientArea_mod($hWnd, _WinAPI_CreateMargins(0, 0, 0, 0))
        If $sClassName = "CabinetWClass" Then _WinAPI_DwmExtendFrameIntoClientArea_mod($hWnd, _WinAPI_CreateMargins(-1, -1, -1, -1))
    EndIf

    Return $aResult[0]
EndFunc   ;==>_WinAPI_DwmEnableBlurBehindWindow11

Func _WinAPI_GetClassName_mod($hWnd)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    Local $aCall = DllCall($hUser32, "int", "GetClassNameW", "hwnd", $hWnd, "wstr", "", "int", 4096)
    If @error Or Not $aCall[0] Then Return SetError(@error, @extended, '')

    Return SetExtended($aCall[0], $aCall[2])
EndFunc   ;==>_WinAPI_GetClassName_mod

Func _WinAPI_CreateEllipticRgn_mod($tRECT)
    Local $aCall = DllCall($hGdi, 'handle', 'CreateEllipticRgnIndirect', 'struct*', $tRECT)
    If @error Then Return SetError(@error, @extended, False)
    ; If Not $aCall[0] Then Return SetError(1000, 0, 0)

    Return $aCall[0]
EndFunc   ;==>_WinAPI_CreateEllipticRgn_mod

Func _WinAPI_SwitchColor_mod($iColor)
    Return BitOR(BitAND($iColor, 0x00FF00), BitShift(BitAND($iColor, 0x0000FF), -16), BitShift(BitAND($iColor, 0xFF0000), 16))
EndFunc

 

 

Edited by WildByDesign
Posted
2 hours ago, WildByDesign said:

Mesmerizing! 🤩 

...
    #include <WindowsConstants.au3> ; $WS_*
...
    $hGUI = GUICreate("Example", 400, 400, -1, -1, BitOR($GUI_SS_DEFAULT_GUI,$WS_MAXIMIZEBOX,$WS_SIZEBOX,$WS_THICKFRAME,$WS_TABSTOP))
...

should try it in full screen 😍

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted (edited)
12 hours ago, argumentum said:
contents

image.png.28bd78e0b3670d3dc88cd374593c50c1.png

.. a bit off-topic. Did you try "My fine tuned High Contrast theme editor" ? Bet you'll enjoy it :). 

Yes, absolutely. I tested it for a few days earlier this year. I was amazed at how thorough you could customize HC themes because I hadn’t generally used HC themes before trying you app. I think your GUI is very well organized.

The reason why I did not continue with HC themes after that was because of MS limitations. It was not possible to apply things like Win11 backdrop materials (Mica, Acrylic, etc.) or blur behind or really any DWM customizations.

That is why I still use Rectify11’s Black (Immersive) msstyles.

However, I still seek the ability to change all apps backgrounds to black that would allow those customizations with default Windows Dark theme. That way it would not be needed to use R11 msstyles.

 Unfortunately still no success with that adventure. I had tried things like _WinAPI_SetLayeredWindowAttributes() and _WinAPI_SetSysColors() but no luck.

EDIT: Actually there was some success with SetSysColors. It worked for all apps to make black background. But I think problem was I could not change menubar or scrollbar to black.

Edited by WildByDesign

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
×
×
  • Create New...