Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround? Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

18 Dec 2018, 01:27

So i tracked the source of catastrophic decline in the original topic, DESKTOP COMPOSITION, which i normally disable.

With Desktop Composition Disabled,My Usual Choice.
  • 0.000057
    0.066316
With Desktop Composition Enabled
  • 0.023350
    50.509178
I' would very much line to know if there is a work around? As I hope to use this function beyond my own pc.

Code: Select all

#SingleInstance, Force
#NoTrayIcon
#KeyHistory, 0
#MaxThreadsPerHotkey, 1
#Persistent
#MaxMem 1000
ListLines, Off
SetBatchLines, -1
SetWinDelay, -1
SetMouseDelay, -1
SetKeyDelay, -1, -1
SetTitleMatchMode, 3
DetectHiddenWindows, On
Process, Priority,, High		; Have the script set itself to high priority. Use this setting with caution. It's been known to cause problems with lower end PC's


/*
With Desktop Composition Disabled,My Usual Choice.
0.000057
0.066316
With Desktop Composition Enabled
0.023350
50.509178
*/

MsgBox % Clipboard := QPXF(1,"GetPixelColor",0,0) "`n" QPXF(1,"GetPixelMonochromeText",,,20,20)





trimWhiteSpace := false	;removes all lines that contain only a single character,such as blank lines in output

;create monospaced font,attach to never displayed static object and set font to active ToolTip
;credit: uname	;forgot the post where he/she posted it at.
text :="sometext"
Gui Font,, Lucida Console	;a monospaced font
Gui, Add, Text, HwndhwndStatic, % text
SendMessage, 0x31,,,, ahk_id %hwndStatic%
font := ErrorLevel, clipboard := font
;SendMessage, 0x30, font, 1,, ahk_class tooltips_class32 ahk_exe AutoHotkey.exe	;set ToolTip font to that of gui object.

Loop{
	ToolTip % txt := !trimWhiteSpace ? GetPixelMonochromeText(,,40,20) : StringRemoveSingleCharLine(GetPixelMonochromeText(,,40,20))
	SendMessage, 0x30, font, 1,, ahk_class tooltips_class32 ahk_exe AutoHotkey.exe	;set ToolTip font to monospace
}

GetPixelMonochromeText(x:="",y:="",w:=50,h:=50){
	threshold := 65, invert := false, dark := !invert ? "0" : "-", light := !invert ? "-" : "0"
	,invertOutput:=true	;invert any output to have a high contrast image
	If !x
		MouseGetPos, x, y
	x := x-(w/2), y:= y-(h/2)
	Loop % Round(h){
		thisY := A_Index
		Loop % Round(w){
			rgb := GetPixelColor(x+A_Index,y+thisY)
			,imageMatrix .= (rgb.Red > threshold AND rgb.Green > threshold AND rgb.Blue > threshold ? light : dark)
		}
		imageMatrix .= "`n"
	}
	;if dominant char is 0, invert char set for better contrast
	( StringCharCount(imageMatrix,dark)/StrLen(imageMatrix) > 0.5  ? (imageMatrix := StrReplace(imageMatrix,light,"~")) & (imageMatrix := StrReplace(imageMatrix,dark,light))  & (imageMatrix := StrReplace(imageMatrix,"~",dark)) : "" )
	Return imageMatrix
}

Loop{
	MouseGetPos, x, y
	rgb := GetPixelColor(x,y)
	ToolTip % whiteShade := ColorDistance(rgb.Red . rgb.Green . rgb.Blue,255255255), 0,0,2	;white
	ToolTip % blackShade := ColorDistance(rgb.Red . rgb.Green . rgb.Blue,000), 0,30,3	;black
	ToolTip % rgb.Red "`n" rgb.Green "`n" rgb.Blue
	;where true black = 0B & 65W and true white = 495B & 0W
	ToolTip % ( whiteShade < 200 AND blackShade > 200 ? "LIGHT" : "DARK" ), 0,50,4
}

GetPixelColor(x,y){
	PixelGetColor, color, %x%, %y%
	Blue:="0x" SubStr(color,3,2) ;substr is to get the piece
	,Blue:=Blue+0 ;add 0 is to convert it to the current number format
	,Green:="0x" SubStr(color,5,2)
	,Green:=Green+0
	,Red:="0x" SubStr(color,7,2)
	,Red:=Red+0
	Return {Red:Red, Green:Green, Blue:Blue}
}


ColorDistance(rbg1, rbg2)	;color range measurement
{
	return abs((rbg1 & 0xff) - (rbg2 & 0xff)
	+ abs(((rbg1 >> 8) & 0xff) - ((rbg2 >> 8) & 0xff))
	+ abs(((rbg1 >> 16) & 0xff) - ((rbg2 >> 16) & 0xff))) ;
}

;Returns the number of ooccurrences of a character in a string
StringCharCount(string, char){
	StringReplace, string, string, %char%, %char%, UseErrorLevel
	Return ErrorLevel
}

StringRemoveSingleCharLine(ByRef list, delim:="`n"){	;remove lines that contain only a single character
	For k,v in StrSplit(list, delim)
		nList .= StringCharCount(v,SubStr(v,1,1)) = StrLen(v) ? "" : (!nList ?  v : "`n" v)
	Return nList
}











; QPXF(<passes>, <function name>, <parameter 1>, ..., <parameter 8>)
QPXF(_P, _F, _1=" ", _2=" ", _3=" ", _4=" ", _5=" ", _6=" ", _7=" ", _8=" "){
	If not (IsFunc(_F) || _P > 0)
		Return
	Loop, %_P%
	{
		If (_1 = " ")
			QPX(True), %_F%()
		else If (_8 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7,_8)
		else If (_7 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7)
		else If (_6 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6)
		else If (_5 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5)
		else If (_4 != " ")
			QPX(True), %_F%(_1,_2,_3,_4)
		else If (_3 != " ")
			QPX(True), %_F%(_1,_2,_3)
		else If (_2 != " ")
			QPX(True), %_F%(_1,_2)
		else If (_1 != " ")
			QPX(True), %_F%(_1)
		T += QPX(False)
	}
	Return T / _P
}



/*
	;;** Basic Usage **
	QPX( True ) ; Initialise Counter
	Sleep 1000
	Ti := QPX( False ) ; Retrieve Time consumed ( & reset internal vars )
	
	MsgBox, 0, Sleep 1000, %Ti% seconds
	
	;;** Extended Usage **
	While QPX( 1000 ) ; Loops 1000 times and keeps internal track of the total time
		Tooltip %A_Index%
	Ti := QPX() ; Retrieve Avg time consumed per iteration ( & reset internal vars )
	
	MsgBox, 0, Avg Time Taken for ToolTip, %Ti% Seconds / Iteration
	
	
	MsgBox, % Delay( 0.008 ) ; Delay for 8ms
*/
QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
	Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
	If	( N && !P )
		Return	DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
	DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
	Return	( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}

Last edited by CyL0N on 18 Dec 2018, 23:34, edited 4 times in total.
live ? long & prosper : regards
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Function Worked Fine, Every Call Now Needs Over 50 Seconds !?!

18 Dec 2018, 18:13

what has changed between "when it used to work just fine" and now?
repeated PixelGetColor() calls have always been slow for me on win10
CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Re: Function Worked Fine(70ms per call), Every Call Now Requires Over 50 Seconds !?!

18 Dec 2018, 23:04

swagfag wrote:
18 Dec 2018, 18:13
what has changed between "when it used to work just fine" and now?
repeated PixelGetColor() calls have always been slow for me on win10
Used to work seamlessly in realtime, ~700ms per call WITHOUT SetBatchLines -1, now it averages 60seconds per call. I either made an idiotic mistake,which is unlikely because i hadn't modified it. Or i'm missing something,because it' doesn't even work on the version of ahk i even wrote it for (v 1.26.01).

I didn't even know what title to use for this post, it's such a bizarre thing.

I'd appreciate any help.
live ? long & prosper : regards
CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Re: Function Worked Fine(70ms per call), Every Call Now Requires Over 50 Seconds !?!

18 Dec 2018, 23:30

cnikitopoulos94 wrote:
18 Dec 2018, 23:22
Maybe something minor? did you check playback speed?

I've tracked the changes to Desktop Composition as being the culprit. I'm now looking for workarounds... It seems every PixelGetColor function in the forums I've tested runs absurdly slower with Desktop Composition enabled.... I have no idea why...
live ? long & prosper : regards
just me
Posts: 9466
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?  Topic is solved

19 Dec 2018, 05:56

Not sure if this will work as you want in every case:

Code: Select all

#NoEnv
SetBatchLines, -1

W := 40
H := 20

CoordMode, Mouse, Screen
ToolTipFont("s10", "Lucida Console")

ToolTip, % GetPixelMonochromeText( , , W, H)
MsgBox % QPXF(1, "GetPixelMonochromeText", , , W, H)

ExitApp

GetPixelMonochromeText(X := "", Y := "", W := 50, H := 50){
   threshold := 65
   invert := False
   dark := !invert ? "0" : "-"
   light := !invert ? "-" : "0"
   invertOutput := True ; invert any output to have a high contrast image
   If (X = "")
      MouseGetPos, X, Y
   X := X - (W / 2)
   Y := Y - (H / 2)
   HDC := DllCall("GetDC", "Ptr", 0, "UPtr")
   MDC := DllCall("CreateCompatibleDC", "Ptr", HDC, "UPtr")
   MBM := DllCall("CreateCompatibleBitmap", "Ptr", HDC, "Int", W, "Int", H, "UPtr")
   OBM := DllCall("SelectObject", "Ptr", MDC, "Ptr", MBM)
   DllCall("BitBlt", "Ptr", MDC, "Int", 0, "Int", 0, "Int", W, "Int", H, "Ptr", HDC, "Int", X, "Int", Y, "UInt", 0x40CC0020)
   ImageMatrix := ""
   Loop % Round(H) {
      ThisY := A_Index - 1
      Loop % Round(W) {
         ; rgb := GetPixelColor(X+A_Index,Y+thisY)
         RGB := DllCall("GetPixel", "Ptr", MDC, "Int", A_Index - 1, "Int", ThisY, "UInt")
         Red := RGB & 0xFF, Green := (RGB >> 8) & 0xFF, Blue := (RGB >> 16) & 0xff
         ImageMatrix .= (Red > threshold && Green > threshold && Blue > threshold ? light : dark)
      }
      ImageMatrix .= "`n"
   }

   DllCall("SelectObject", "Ptr", MDC, "Ptr", OBM)
   DllCall("DeleteDC", "Ptr", MDC)
   DllCall("ReleaseDC", "Ptr", 0, "Ptr", HDC)
   ; if dominant char is 0, invert char set for better contrast
   ; (StringCharCount(ImageMatrix,dark)/StrLen(ImageMatrix) > 0.5 ? (ImageMatrix := StrReplace(ImageMatrix,light,"~"))
   ;                                                                & (ImageMatrix := StrReplace(ImageMatrix,dark,light))
   ;                                                                & (ImageMatrix := StrReplace(ImageMatrix,"~",dark))
   ;                                                              : "")
   Return ImageMatrix
}
; QPXF(<passes>, <function name>, <parameter 1>, ..., <parameter 8>)
QPXF(_P, _F, _1=" ", _2=" ", _3=" ", _4=" ", _5=" ", _6=" ", _7=" ", _8=" "){
	If not (IsFunc(_F) || _P > 0)
		Return
	Loop, %_P%
	{
		If (_1 = " ")
			QPX(True), %_F%()
		else If (_8 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7,_8)
		else If (_7 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7)
		else If (_6 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6)
		else If (_5 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5)
		else If (_4 != " ")
			QPX(True), %_F%(_1,_2,_3,_4)
		else If (_3 != " ")
			QPX(True), %_F%(_1,_2,_3)
		else If (_2 != " ")
			QPX(True), %_F%(_1,_2)
		else If (_1 != " ")
			QPX(True), %_F%(_1)
		T += QPX(False)
	}
	Return T / _P
}
/*
	;;** Basic Usage **
	QPX( True ) ; Initialise Counter
	Sleep 1000
	Ti := QPX( False ) ; Retrieve Time consumed ( & reset internal vars )

	MsgBox, 0, Sleep 1000, %Ti% seconds

	;;** Extended Usage **
	While QPX( 1000 ) ; Loops 1000 times and keeps internal track of the total time
		Tooltip %A_Index%
	Ti := QPX() ; Retrieve Avg time consumed per iteration ( & reset internal vars )

	MsgBox, 0, Avg Time Taken for ToolTip, %Ti% Seconds / Iteration


	MsgBox, % Delay( 0.008 ) ; Delay for 8ms
*/
QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
	Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
	If	( N && !P )
		Return	DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
	DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
	Return	( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}

; ToolTipOpt v1.004 by lexikos
; Changes:
;  v1.001 - Pass "Default" to restore a setting to default
;  v1.002 - ANSI compatibility
;  v1.003 - Added workarounds for ToolTip's parameter being overwritten
;           by code within the message hook.
;  v1.004 - Fixed text colour.
ToolTipFont(Options := "", Name := "", hwnd := "") {
    static hfont := 0
    if (hwnd = "")
        hfont := Options="Default" ? 0 : _TTG("Font", Options, Name), _TTHook()
    else
        DllCall("SendMessage", "ptr", hwnd, "uint", 0x30, "ptr", hfont, "ptr", 0)
}
ToolTipColor(Background := "", Text := "", hwnd := "") {
    static bc := "", tc := ""
    if (hwnd = "") {
        if (Background != "")
            bc := Background="Default" ? "" : _TTG("Color", Background)
        if (Text != "")
            tc := Text="Default" ? "" : _TTG("Color", Text)
        _TTHook()
    }
    else {
        VarSetCapacity(empty, 2, 0)
        DllCall("UxTheme.dll\SetWindowTheme", "ptr", hwnd, "ptr", 0
            , "ptr", (bc != "" && tc != "") ? &empty : 0)
        if (bc != "")
            DllCall("SendMessage", "ptr", hwnd, "uint", 1043, "ptr", bc, "ptr", 0)
        if (tc != "")
            DllCall("SendMessage", "ptr", hwnd, "uint", 1044, "ptr", tc, "ptr", 0)
    }
}
_TTHook() {
    static hook := 0
    if !hook
        hook := DllCall("SetWindowsHookExW", "int", 4
            , "ptr", RegisterCallback("_TTWndProc"), "ptr", 0
            , "uint", DllCall("GetCurrentThreadId"), "ptr")
}
_TTWndProc(nCode, _wp, _lp) {
    Critical 999
   ;lParam  := NumGet(_lp+0*A_PtrSize)
   ;wParam  := NumGet(_lp+1*A_PtrSize)
    uMsg    := NumGet(_lp+2*A_PtrSize, "uint")
    hwnd    := NumGet(_lp+3*A_PtrSize)
    if (nCode >= 0 && (uMsg = 1081 || uMsg = 1036)) {
        _hack_ = ahk_id %hwnd%
        WinGetClass wclass, %_hack_%
        if (wclass = "tooltips_class32") {
            ToolTipColor(,, hwnd)
            ToolTipFont(,, hwnd)
        }
    }
    return DllCall("CallNextHookEx", "ptr", 0, "int", nCode, "ptr", _wp, "ptr", _lp, "ptr")
}
_TTG(Cmd, Arg1, Arg2 := "") {
    static htext := 0, hgui := 0
    if !htext {
        Gui _TTG: Add, Text, +hwndhtext
        Gui _TTG: +hwndhgui +0x40000000
    }
    Gui _TTG: %Cmd%, %Arg1%, %Arg2%
    if (Cmd = "Font") {
        GuiControl _TTG: Font, %htext%
        SendMessage 0x31, 0, 0,, ahk_id %htext%
        return ErrorLevel
    }
    if (Cmd = "Color") {
        hdc := DllCall("GetDC", "ptr", htext, "ptr")
        SendMessage 0x138, hdc, htext,, ahk_id %hgui%
        clr := DllCall("GetBkColor", "ptr", hdc, "uint")
        DllCall("ReleaseDC", "ptr", htext, "ptr", hdc)
        return clr
    }
}
CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

19 Dec 2018, 11:37

just me wrote:
19 Dec 2018, 05:56
:dance: Beautiful stuff. Thanks much dude. :salute: Merry Christmas :xmas:

Could you possibly wrap up a separate PixelGetColor dll function(with verbose commentary), my attempts seem to produce bad output,making a silly mistake somewhere...
Last edited by CyL0N on 20 Dec 2018, 08:42, edited 3 times in total.
live ? long & prosper : regards
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

19 Dec 2018, 17:10

bench:

Code: Select all

#NoEnv
#SingleInstance Force
SetBatchLines -1

#Include Gdip_All.ahk

pToken := Gdip_Startup()

x := y := 0
for each, Res in [[640, 480]
				, [1024, 576]
				, [1152, 648]
				, [1280, 720]
				, [1366, 768]
				, [1600, 900]
				, [1920, 1080]]
{
	w := Res[1]
	h := Res[2]

	results .= bench(Func("bitblit"), "bitblit", x, y, w, h) "`n"
	results .= bench(Func("lockbit"), "lockbit", x, y, w, h) "`n`n"
}

MsgBox % Clipboard := results

/*
bitblit() @ 640x480 executed in 498 ms.
lockbit() @ 640x480 executed in 177 ms.

bitblit() @ 1024x576 executed in 1005 ms.
lockbit() @ 1024x576 executed in 354 ms.

bitblit() @ 1152x648 executed in 1230 ms.
lockbit() @ 1152x648 executed in 559 ms.

bitblit() @ 1280x720 executed in 1463 ms.
lockbit() @ 1280x720 executed in 601 ms.

bitblit() @ 1366x768 executed in 1769 ms.
lockbit() @ 1366x768 executed in 586 ms.

bitblit() @ 1600x900 executed in 2417 ms.
lockbit() @ 1600x900 executed in 990 ms.

bitblit() @ 1920x1080 executed in 3340 ms.
lockbit() @ 1920x1080 executed in 1216 ms.
*/

Gdip_Shutdown(pToken)
ExitApp
Esc::ExitApp

bench(fn, fnName, x, y, w, h, iterations := 1) {
	DllCall("QueryPerformanceFrequency", "Int64*", frequency)
	DllCall("QueryPerformanceCounter", "Int64*", start)

	Loop % iterations
		fn.Call(x, y, w, h)

	DllCall("QueryPerformanceCounter", "Int64*", stop)
	elapsed := (stop - start) * 1000
	elapsed /= frequency
	result := Format("{}() @ {}x{} executed in {} ms.", fnName, w, h, elapsed)

	return result
}

bitblit(x, y, w, h) {
	hDC := DllCall("GetDC", "Ptr", 0, "UPtr")
	memDC := DllCall("CreateCompatibleDC", "Ptr", hDC, "UPtr")
	memBM := DllCall("CreateCompatibleBitmap", "Ptr", hDC, "Int", w, "Int", h, "UPtr")
	oBM := DllCall("SelectObject", "Ptr", memDC, "Ptr", memBM)
	DllCall("BitBlt", "Ptr", memDC, "Int", x, "Int", y, "Int", w, "Int", h, "Ptr", hDC, "Int", X, "Int", Y, "UInt", 0x40CC0020)

	Loop % h
	{
		y := A_Index - 1

		Loop % w
		{
			x := A_Index - 1

			RGB := DllCall("GetPixel", "Ptr", memDC, "Int", x, "Int", y, "UInt")
			; Red := RGB & 0xFF, Green := (RGB >> 8) & 0xFF, Blue := (RGB >> 16) & 0xff
		}
	}

	DllCall("SelectObject", "Ptr", memDC, "Ptr", oBM)
	DllCall("DeleteDC", "Ptr", memDC)
	DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
}


lockbit(x, y, w, h) {
	pBitmap := Gdip_BitmapFromScreen(x "|" y "|" w "|" h)

	Gdip_LockBits(pBitmap, 0, 0, w, h, stride, scan0, bmData)

	Loop % h
	{
		y := A_Index - 1

		Loop % w
		{
			x := A_Index - 1

			ARGB := Gdip_GetLockBitPixel(scan0, x, y, stride)
		}
	}

	Gdip_UnlockBits(pBitmap, bmData)
	Gdip_DisposeImage(pBitmap)
}
iseahound
Posts: 1447
Joined: 13 Aug 2016, 21:04
Contact:

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

19 Dec 2018, 19:13

@just me
That's pretty clever to store the image data as a string. AHK arrays are just too slow.
CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

19 Dec 2018, 19:20

swagfag wrote:
19 Dec 2018, 17:10
bench:

Code: Select all

#NoEnv
#SingleInstance Force
SetBatchLines -1

#Include Gdip_All.ahk

pToken := Gdip_Startup()

x := y := 0
for each, Res in [[640, 480]
				, [1024, 576]
				, [1152, 648]
				, [1280, 720]
				, [1366, 768]
				, [1600, 900]
				, [1920, 1080]]
{
	w := Res[1]
	h := Res[2]

	results .= bench(Func("bitblit"), "bitblit", x, y, w, h) "`n"
	results .= bench(Func("lockbit"), "lockbit", x, y, w, h) "`n`n"
}

MsgBox % Clipboard := results

/*
bitblit() @ 640x480 executed in 498 ms.
lockbit() @ 640x480 executed in 177 ms.

bitblit() @ 1024x576 executed in 1005 ms.
lockbit() @ 1024x576 executed in 354 ms.

bitblit() @ 1152x648 executed in 1230 ms.
lockbit() @ 1152x648 executed in 559 ms.

bitblit() @ 1280x720 executed in 1463 ms.
lockbit() @ 1280x720 executed in 601 ms.

bitblit() @ 1366x768 executed in 1769 ms.
lockbit() @ 1366x768 executed in 586 ms.

bitblit() @ 1600x900 executed in 2417 ms.
lockbit() @ 1600x900 executed in 990 ms.

bitblit() @ 1920x1080 executed in 3340 ms.
lockbit() @ 1920x1080 executed in 1216 ms.
*/

Gdip_Shutdown(pToken)
ExitApp
Esc::ExitApp

bench(fn, fnName, x, y, w, h, iterations := 1) {
	DllCall("QueryPerformanceFrequency", "Int64*", frequency)
	DllCall("QueryPerformanceCounter", "Int64*", start)

	Loop % iterations
		fn.Call(x, y, w, h)

	DllCall("QueryPerformanceCounter", "Int64*", stop)
	elapsed := (stop - start) * 1000
	elapsed /= frequency
	result := Format("{}() @ {}x{} executed in {} ms.", fnName, w, h, elapsed)

	return result
}

bitblit(x, y, w, h) {
	hDC := DllCall("GetDC", "Ptr", 0, "UPtr")
	memDC := DllCall("CreateCompatibleDC", "Ptr", hDC, "UPtr")
	memBM := DllCall("CreateCompatibleBitmap", "Ptr", hDC, "Int", w, "Int", h, "UPtr")
	oBM := DllCall("SelectObject", "Ptr", memDC, "Ptr", memBM)
	DllCall("BitBlt", "Ptr", memDC, "Int", x, "Int", y, "Int", w, "Int", h, "Ptr", hDC, "Int", X, "Int", Y, "UInt", 0x40CC0020)

	Loop % h
	{
		y := A_Index - 1

		Loop % w
		{
			x := A_Index - 1

			RGB := DllCall("GetPixel", "Ptr", memDC, "Int", x, "Int", y, "UInt")
			; Red := RGB & 0xFF, Green := (RGB >> 8) & 0xFF, Blue := (RGB >> 16) & 0xff
		}
	}

	DllCall("SelectObject", "Ptr", memDC, "Ptr", oBM)
	DllCall("DeleteDC", "Ptr", memDC)
	DllCall("ReleaseDC", "Ptr", 0, "Ptr", hDC)
}


lockbit(x, y, w, h) {
	pBitmap := Gdip_BitmapFromScreen(x "|" y "|" w "|" h)

	Gdip_LockBits(pBitmap, 0, 0, w, h, stride, scan0, bmData)

	Loop % h
	{
		y := A_Index - 1

		Loop % w
		{
			x := A_Index - 1

			ARGB := Gdip_GetLockBitPixel(scan0, x, y, stride)
		}
	}

	Gdip_UnlockBits(pBitmap, bmData)
	Gdip_DisposeImage(pBitmap)
}
My gaming PC eats up the bench, but my main i7 machine runs it painfully slower (exponentially horrible) than your benchmark results....
  • Intel(R) Core(TM) i7-2630QM CPU @ 2.5GHZ
    x64-based PC, Win7 Ultimate X64
    16384 MB
    Intel(R) HD Graphics 3000 v9.17.10.4229 @ -1988.00 MB RAM
    NVIDIA GeForce GT 720M v22.21.13.8528 @ -2048.00 MB RAM
Not much difference, with desktop composition enabled or disabled though...unlike the built in PixelGetColor.

Code: Select all

bitblit() @ 640x480 executed in 1047 ms.
lockbit() @ 640x480 executed in 502 ms.

bitblit() @ 1024x576 executed in 2031 ms.
lockbit() @ 1024x576 executed in 971 ms.

bitblit() @ 1152x648 executed in 2530 ms.
lockbit() @ 1152x648 executed in 1219 ms.

bitblit() @ 1280x720 executed in 3120 ms.
lockbit() @ 1280x720 executed in 1521 ms.

bitblit() @ 1366x768 executed in 3535 ms.
lockbit() @ 1366x768 executed in 1695 ms.

bitblit() @ 1600x900 executed in 4866 ms.
lockbit() @ 1600x900 executed in 2337 ms.

bitblit() @ 1920x1080 executed in 7118 ms.
lockbit() @ 1920x1080 executed in 3374 ms.
live ? long & prosper : regards
CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

20 Dec 2018, 01:53

@just me, never mind the function... It seems the DllCall used screen coords & i failed to notice...hence why i thought it gave wrong values... Cheers.

This Dll wrap,works but doesn't improve on the performance of built in PixelGetColor, So IGNORE THIS POST. My Bad.

Code: Select all

MsgBox % QPXF(5, "GetPixelColor", 100, 100) "`n" QPXF(5, "GetPixelColorDll", 100, 100)


CoordMode, Pixel, Screen
For k,v in GetPixelColor(100,100)
	rgb .= k " : " v "`n"
rgb .= "`n`n"
For k,v in GetPixelColorDll(100,100)
	rgb .= k " : " v "`n"

MsgBox % rgb

GetPixelColorDll(x,y){
	; Get the color of the center.
	hdc := DllCall("GetDC", "uint", 0)
	if hdc
		RGB := DllCall("GetPixel", "Ptr", HDC, "Int", x, "Int", y, "UInt")
		,Red := RGB & 0xFF, Green := (RGB >> 8) & 0xFF, Blue := (RGB >> 16) & 0xff
	
	Return {Red:Red, Green:Green, Blue:Blue}
}

GetPixelColor(x,y){
	PixelGetColor, color, %x%, %y%
	Blue:="0x" SubStr(color,3,2) ;substr is to get the piece
	,Blue:=Blue+0 ;add 0 is to convert it to the current number format
	,Green:="0x" SubStr(color,5,2)
	,Green:=Green+0
	,Red:="0x" SubStr(color,7,2)
	,Red:=Red+0
	Return {Red:Red, Green:Green, Blue:Blue}
}



; QPXF(<passes>, <function name>, <parameter 1>, ..., <parameter 8>)
QPXF(_P, _F, _1=" ", _2=" ", _3=" ", _4=" ", _5=" ", _6=" ", _7=" ", _8=" "){
	If not (IsFunc(_F) || _P > 0)
		Return
	Loop, %_P%
	{
		If (_1 = " ")
			QPX(True), %_F%()
		else If (_8 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7,_8)
		else If (_7 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7)
		else If (_6 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6)
		else If (_5 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5)
		else If (_4 != " ")
			QPX(True), %_F%(_1,_2,_3,_4)
		else If (_3 != " ")
			QPX(True), %_F%(_1,_2,_3)
		else If (_2 != " ")
			QPX(True), %_F%(_1,_2)
		else If (_1 != " ")
			QPX(True), %_F%(_1)
		T += QPX(False)
	}
	Return T / _P
}



/*
	;;** Basic Usage **
	QPX( True ) ; Initialise Counter
	Sleep 1000
	Ti := QPX( False ) ; Retrieve Time consumed ( & reset internal vars )
	
	MsgBox, 0, Sleep 1000, %Ti% seconds
	
	;;** Extended Usage **
	While QPX( 1000 ) ; Loops 1000 times and keeps internal track of the total time
		Tooltip %A_Index%
	Ti := QPX() ; Retrieve Avg time consumed per iteration ( & reset internal vars )
	
	MsgBox, 0, Avg Time Taken for ToolTip, %Ti% Seconds / Iteration
	
	
	MsgBox, % Delay( 0.008 ) ; Delay for 8ms
*/

QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
	Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
	If	( N && !P )
		Return	DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
	DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
	Return	( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}


Delay( D=0.001 ) { ; High Resolution Delay ( High CPU Usage ) by SKAN | CD: 13/Jun/2009
	Static F ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 13/Jun/2009
	Critical
	F ? F : DllCall( "QueryPerformanceFrequency", Int64P,F )
	DllCall( "QueryPerformanceCounter", Int64P,pTick ), cTick := pTick
	While( ( (Tick:=(pTick-cTick)/F)) <D ) {
		DllCall( "QueryPerformanceCounter", Int64P,pTick )
		Sleep -1
	}
	Return Round( Tick,3 )
}
Last edited by CyL0N on 20 Dec 2018, 07:15, edited 4 times in total.
live ? long & prosper : regards
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

20 Dec 2018, 05:39

how is this dll wrapped function any different to what PixelGetColor does natively already
CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

20 Dec 2018, 07:34

swagfag wrote:
20 Dec 2018, 05:39
how is this dll wrapped function any different to what PixelGetColor does natively already
I've edited my post to signify it offer's no benefits apart from insignificant performance gains...

Though @justme's solution has it's kinks it's a viable workaround for PixelGetColor to the extent of my intended use... I've also attempted some of the methods as they were utilised in the benchmark you posted, but i've hit a rock wall, so if you could perhaps fix my error,that would be brilliant, because @just me's approach is in the sub 1ms range per RGB retrieval... which i'm simply yet unable to replicate in a standalone function, thusfar.

This is as fast as i've been able to get it... Not much better than the built in one,certainly due to my errors.

Code: Select all

SetBatchLines, -1

CoordMode, Pixel, Screen

Loop{
MouseGetPos, X, Y
For k,v in GetPixelColor(X,Y)
	rgb .= k " : " v "`n"
rgb.= "`n" QPXF(1, "GetPixelColor", X, Y)
rgb .= "`n`n`n`n"

For k,v in GetPixelColorDll( X, Y)
	rgb .= k " : " v "`n"
rgb.= "`n" QPXF(1, "GetPixelColorDll", X, Y)

Tooltip % clipboard := rgb

rgb := ""
}

GetPixelColorDll(x,y){
	W := 1, H := 1
	,HDC := DllCall("GetDC", "Ptr", 0, "UPtr")
	,MDC := DllCall("CreateCompatibleDC", "Ptr", HDC, "UPtr")
	,MBM := DllCall("CreateCompatibleBitmap", "Ptr", HDC, "Int", W, "Int", H, "UPtr")
	,OBM := DllCall("SelectObject", "Ptr", MDC, "Ptr", MBM)
	,DllCall("BitBlt", "Ptr", MDC, "Int", 0, "Int", 0, "Int", W, "Int", H, "Ptr", HDC, "Int", X, "Int", Y, "UInt", 0x40CC0020)
	,RGB := DllCall("GetPixel", "Ptr", MDC, "Int",, "Int",, "UInt")
	,Red := RGB & 0xFF, Green := (RGB >> 8) & 0xFF, Blue := (RGB >> 16) & 0xff
	,DllCall("SelectObject", "Ptr", MDC, "Ptr", OBM)
	,DllCall("DeleteDC", "Ptr", MDC)
	,DllCall("ReleaseDC", "Ptr", 0, "Ptr", HDC)

	Return {Red:Red, Green:Green, Blue:Blue}
}

GetPixelColor(x,y){
	PixelGetColor, color, %x%, %y%
	Blue:="0x" SubStr(color,3,2) ;substr is to get the piece
	,Blue:=Blue+0 ;add 0 is to convert it to the current number format
	,Green:="0x" SubStr(color,5,2)
	,Green:=Green+0
	,Red:="0x" SubStr(color,7,2)
	,Red:=Red+0
	Return {Red:Red, Green:Green, Blue:Blue}
}



; QPXF(<passes>, <function name>, <parameter 1>, ..., <parameter 8>)
QPXF(_P, _F, _1=" ", _2=" ", _3=" ", _4=" ", _5=" ", _6=" ", _7=" ", _8=" "){
	If not (IsFunc(_F) || _P > 0)
		Return
	Loop, %_P%
	{
		If (_1 = " ")
			QPX(True), %_F%()
		else If (_8 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7,_8)
		else If (_7 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6,_7)
		else If (_6 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5,_6)
		else If (_5 != " ")
			QPX(True), %_F%(_1,_2,_3,_4,_5)
		else If (_4 != " ")
			QPX(True), %_F%(_1,_2,_3,_4)
		else If (_3 != " ")
			QPX(True), %_F%(_1,_2,_3)
		else If (_2 != " ")
			QPX(True), %_F%(_1,_2)
		else If (_1 != " ")
			QPX(True), %_F%(_1)
		T += QPX(False)
	}
	Return T / _P
}



/*
	;;** Basic Usage **
	QPX( True ) ; Initialise Counter
	Sleep 1000
	Ti := QPX( False ) ; Retrieve Time consumed ( & reset internal vars )
	
	MsgBox, 0, Sleep 1000, %Ti% seconds
	
	;;** Extended Usage **
	While QPX( 1000 ) ; Loops 1000 times and keeps internal track of the total time
		Tooltip %A_Index%
	Ti := QPX() ; Retrieve Avg time consumed per iteration ( & reset internal vars )
	
	MsgBox, 0, Avg Time Taken for ToolTip, %Ti% Seconds / Iteration
	
	
	MsgBox, % Delay( 0.008 ) ; Delay for 8ms
*/

QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
	Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
	If	( N && !P )
		Return	DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
	DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
	Return	( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}


Delay( D=0.001 ) { ; High Resolution Delay ( High CPU Usage ) by SKAN | CD: 13/Jun/2009
	Static F ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 13/Jun/2009
	Critical
	F ? F : DllCall( "QueryPerformanceFrequency", Int64P,F )
	DllCall( "QueryPerformanceCounter", Int64P,pTick ), cTick := pTick
	While( ( (Tick:=(pTick-cTick)/F)) <D ) {
		DllCall( "QueryPerformanceCounter", Int64P,pTick )
		Sleep -1
	}
	Return Round( Tick,3 )
}
live ? long & prosper : regards
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

20 Dec 2018, 07:59

what again is ur goal here?
if u seek to speed up a single pixel retrieval, ure better off just using PixelGetColor, it wont get any faster than that
if u seek to speed up multiple consecutive pixel retrievals, ure better off just using PixelGetColor for small n. Once the multiple PixelGetColor calls' execution time exceeds the upfront cost of caching the screengrab, u should switch to justme's bitblit approach or lockbits
CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

20 Dec 2018, 08:38

swagfag wrote:
20 Dec 2018, 07:59
what again is ur goal here?
if u seek to speed up a single pixel retrieval, ure better off just using PixelGetColor, it wont get any faster than that
if u seek to speed up multiple consecutive pixel retrievals, ure better off just using PixelGetColor for small n. Once the multiple PixelGetColor calls' execution time exceeds the upfront cost of caching the screengrab, u should switch to justme's bitblit approach or lockbits


Your absolutely right @swagfag, i've come to the conclusion, i don't need it wrapped in a function...

Thanks again for everyone who helped, @just me, you rock dude....
live ? long & prosper : regards
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

20 Dec 2018, 09:14

iseahound wrote:
19 Dec 2018, 19:13
@just me
That's pretty clever to store the image data as a string. AHK arrays are just too slow.
eh, slower sure. "too slow", are they really though? if u account for the needed capacity, i find it to be manageable. nonetheless, it is a trade off between convenience and speed
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

20 Dec 2018, 10:19

from my memory, PixelGetColor will not allow you to get pixels from windows that are hidden behind other windows. it will only allow you to get pixels from the topmost visible portions of the screen

to get pixels from windows that are hidden behind others, desktop composition MUST be enabled

the absolute fastest that i have found is also the simplest:

Code: Select all

DwmGetPixel(x, y, hwnd)
{
   ;// input: x,y coordinates relative to the full window, including borders and caption bar
   ;// returns: pixel color in BGR hex

/*
   ;// to test for Aero theme
   DllCall("dwmapi\DwmIsCompositionEnabled","int*",DwmIsEnabled)
   if (!DwmIsEnabled)
   {
      ;msgbox, aero theme off
      ;return 0
   }
*/

   ;// GetDC only grabs client area, GetDCEx with DCX_WINDOW flag grabs full window
   hDC := DllCall("user32.dll\GetDCEx", "UInt", hwnd, "UInt", 0, "UInt", 1|2)
                                                               ; DCX_CACHE = 0x2
                                                               ; DCX_WINDOW = 0x1

   pix := DllCall("gdi32.dll\GetPixel", "UInt", hDC, "Int", x, "Int", y, "UInt")

   DllCall("user32.dll\ReleaseDC", "UInt", hwnd, "UInt", hDC)
   DllCall("gdi32.dll\DeleteDC", "UInt", hDC)

   pix := DecToHex(pix)
   
   return pix
}


DecToHex(dec)
{
   oldfrmt := A_FormatInteger
   hex := dec
   SetFormat, IntegerFast, hex
   hex += 0
   hex .= ""
   SetFormat, IntegerFast, %oldfrmt%
   return hex
}


CyL0N
Posts: 211
Joined: 27 Sep 2018, 09:58

Re: Desktop Composition Slowing PixelGetColor Function From 70ms To 50sec. Any Workaround?

20 Dec 2018, 10:49

guest3456 wrote:
20 Dec 2018, 10:19
THAT, was precisely what was hoping for, and had given up on.
Much, Much, Much Appreciated dude... :clap: :salute: :xmas:

Test: (wow)

Code: Select all

Loop{
	WinGet, hwndA, ID, A
	MouseGetPos, X, Y
	QPX(True)
	SplitRGBColor(DwmGetPixel(x, y, hwndA), Red, Green, Blue)
	ToolTip % QPX() "`n" Red "." Green "." Blue
}

;this function splits the color into its Red, Green, and Blue parts
SplitRGBColor(RGBColor, ByRef Red, ByRef Green, ByRef Blue){
    Red := RGBColor >> 16 & 0xFF,Green := RGBColor >> 8 & 0xFF,Blue := RGBColor & 0xFF
}


QPX( N=0 ) { ; Wrapper for QueryPerformanceCounter()by SKAN | CD: 06/Dec/2009
	Static F,A,Q,P,X ; www.autohotkey.com/forum/viewtopic.php?t=52083 | LM: 10/Dec/2009
	If	( N && !P )
		Return	DllCall("QueryPerformanceFrequency",Int64P,F) + (X:=A:=0) + DllCall("QueryPerformanceCounter",Int64P,P)
	DllCall("QueryPerformanceCounter",Int64P,Q), A:=A+Q-P, P:=Q, X:=X+1
	Return	( N && X=N ) ? (X:=X-1)<<64 : ( N=0 && (R:=A/X/F) ) ? ( R + (A:=P:=X:=0) ) : 1
}



DwmGetPixel(x, y, hwnd)
{
	;// input: x,y coordinates relative to the full window, including borders and caption bar
	;// returns: pixel color in BGR hex
	
	/*
		;// to test for Aero theme
		DllCall("dwmapi\DwmIsCompositionEnabled","int*",DwmIsEnabled)
		if (!DwmIsEnabled)
		{
			;msgbox, aero theme off
			;return 0
		}
	*/
	
	;// GetDC only grabs client area, GetDCEx with DCX_WINDOW flag grabs full window
	hDC := DllCall("user32.dll\GetDCEx", "UInt", hwnd, "UInt", 0, "UInt", 1|2)
	; DCX_CACHE = 0x2
	; DCX_WINDOW = 0x1
	
	pix := DllCall("gdi32.dll\GetPixel", "UInt", hDC, "Int", x, "Int", y, "UInt")
	
	DllCall("user32.dll\ReleaseDC", "UInt", hwnd, "UInt", hDC)
	DllCall("gdi32.dll\DeleteDC", "UInt", hDC)
	
	pix := DecToHex(pix)
	
	return pix
}


DecToHex(dec)
{
	oldfrmt := A_FormatInteger
	hex := dec
	SetFormat, IntegerFast, hex
	hex += 0
	hex .= ""
	SetFormat, IntegerFast, %oldfrmt%
	return hex
}
live ? long & prosper : regards

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Rohwedder and 269 guests