AutoHotkey Community

It is currently May 26th, 2012, 3:22 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: March 9th, 2009, 6:27 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
AutoHotkey.chm: PixelGetColor wrote:
The pixel must be visible; in other words, it is not possible to retrieve the pixel color of a window hidden behind another window.
...
A window that is partially transparent or that has one of its colors marked invisible (TransColor) typically yields colors for the window behind itself rather than its own.

To retrieve the pixel color from a window, which is out of screen (fully or partially), transparent/translucent, or behind (an)other window(s), use this function:

Code:
PixelColor(pc_x, pc_y, pc_wID)
{
   If pc_wID
   {
      pc_hDC := DllCall("GetDC", "UInt", pc_wID)
      WinGetPos, , , pc_w, pc_h, ahk_id %pc_wID%
      pc_hCDC := CreateCompatibleDC(pc_hDC)
      pc_hBmp := CreateCompatibleBitmap(pc_hDC, pc_w, pc_h)
      pc_hObj := SelectObject(pc_hCDC, pc_hBmp)
      
      pc_hmCDC := CreateCompatibleDC(pc_hDC)
      pc_hmBmp := CreateCompatibleBitmap(pc_hDC, 1, 1)
      pc_hmObj := SelectObject(pc_hmCDC, pc_hmBmp)
      
      DllCall("PrintWindow", "UInt", pc_wID, "UInt", pc_hCDC, "UInt", 0)
      DllCall("BitBlt" , "UInt", pc_hmCDC, "Int", 0, "Int", 0, "Int", 1, "Int", 1, "UInt", pc_hCDC, "Int", pc_x, "Int", pc_y, "UInt", 0xCC0020)
      pc_fmtI := A_FormatInteger
      SetFormat, Integer, Hex
      DllCall("GetBitmapBits", "UInt", pc_hmBmp, "UInt", VarSetCapacity(pc_bits, 4, 0), "UInt", &pc_bits)
      pc_c := NumGet(pc_bits, 0)
      SetFormat, Integer, %pc_fmtI%

      DeleteObject(pc_hBmp), DeleteObject(pc_hmBmp)
      DeleteDC(pc_hCDC), DeleteDC(pc_hmCDC)
      DllCall("ReleaseDC", "UInt", pc_wID, "UInt", pc_hDC)
      Return pc_c
   }
}


CreateCompatibleDC(hdc=0) {
   return DllCall("CreateCompatibleDC", "UInt", hdc)
}     

CreateCompatibleBitmap(hdc, w, h) {
   return DllCall("CreateCompatibleBitmap", UInt, hdc, Int, w, Int, w)
}

SelectObject(hdc, hgdiobj) {
   return DllCall("SelectObject", "UInt", hdc, "UInt", hgdiobj)
}

DeleteObject(hObject) {
   Return, DllCall("DeleteObject", "UInt", hObject)
}

DeleteDC(hdc) {
   Return, DllCall("DeleteDC", "UInt", hdc)
}


It's based on infogulch's RegionGetColor

As expected, won't work on minimized windows, or area "out of scroll".
Also you can't get the color of a video (overlayed or not), and using this function on a video application window brings the video control on top, and the video 'twitchs' (I want to say: stops for a moment) when this function is called (maybe not an issue if you've got a "supercomputer" :D).

Scrolling a window which is monitored by this function may result in artifacts (at least IE).

Video and scroll problems are same as with LiveWindows.

Memory leak -tested.

Variables used by the script (preceeded by pc_ to make it simple to insert it to scripts as non-function) are:
Code:
pc_bits
pc_c
pc_fmtI
pc_h
pc_hBmp
pc_hCDC
pc_hDC
pc_hmBmp
pc_hmCDC
pc_hmObj
pc_hObj
pc_w
pc_wID
pc_x
pc_y



I used the following script to verify that the function is working.
It shows how you can get pixel color using a loop (more efficient without creating + deleting/releasing DCs, bitmaps ans selecting/deleting objects each time). Also a timer could be employed to call those 3 dll functions.
The value shown by the tooltip should change almost every second: digits 5 and 6 vs. 0 and 9 have same pixel at that position with default(?) font (seems to be Tahoma), normal size (8 points).
Esc to exit.

Code:
Run timedate.cpl
WinWaitActive ahk_class #32770
Loop
{
   WinGet clock?, ControlList, A
   IfInString clock?, ClockWndMain1
      Break
   Sleep 100
}
SendMessage, 0x1330, 0, , SysTabControl321 ; Select first tab

ControlGetPos, pc_x, pc_y, , , Edit5 ; Edit5 = seconds
pc_x += 8, pc_y +=6
pc_wID := WinExist()

   If pc_wID
   {
      pc_hDC := DllCall("GetDC", "UInt", pc_wID)
      WinGetPos, , , pc_w, pc_h, ahk_id %pc_wID%
      pc_hCDC := CreateCompatibleDC(pc_hDC)
      pc_hBmp := CreateCompatibleBitmap(pc_hDC, pc_w, pc_h)
      pc_hObj := SelectObject(pc_hCDC, pc_hBmp)
      
      pc_hmCDC := CreateCompatibleDC(pc_hDC)
      pc_hmBmp := CreateCompatibleBitmap(pc_hDC, 1, 1)
      pc_hmObj := SelectObject(pc_hmCDC, pc_hmBmp)
      
      Loop
      {
         DllCall("PrintWindow", "UInt", pc_wID, "UInt", pc_hCDC, "UInt", 0)
         DllCall("BitBlt" , "UInt", pc_hmCDC, "Int", 0, "Int", 0, "Int", 1, "Int", 1, "UInt", pc_hCDC, "Int"
         , pc_x, "Int", pc_y, "UInt", 0xCC0020)
         pc_fmtI := A_FormatInteger
         SetFormat, Integer, Hex
         DllCall("GetBitmapBits", "UInt", pc_hmBmp, "UInt", VarSetCapacity(pc_bits, 4, 0), "UInt", &pc_bits)
         pc_c := NumGet(pc_bits, 0)
         ToolTip % pc_c
         SetFormat, Integer, %pc_fmtI%
         Sleep 500
      }
      Esc::
      DeleteObject(pc_hBmp), DeleteObject(pc_hmBmp)
      DeleteDC(pc_hCDC), DeleteDC(pc_hmCDC)
      DllCall("ReleaseDC", "UInt", pc_wID, "UInt", pc_hDC)
         ExitApp
   }


CreateCompatibleDC(hdc=0) {
   return DllCall("CreateCompatibleDC", "UInt", hdc)
}     

CreateCompatibleBitmap(hdc, w, h) {
   return DllCall("CreateCompatibleBitmap", UInt, hdc, Int, w, Int, w)
}

SelectObject(hdc, hgdiobj) {
   return DllCall("SelectObject", "UInt", hdc, "UInt", hgdiobj)
}

DeleteObject(hObject) {
   Return, DllCall("DeleteObject", "UInt", hObject)
}

DeleteDC(hdc) {
   Return, DllCall("DeleteDC", "UInt", hdc)
}

_________________
Pekka Vartto


Last edited by svi on March 18th, 2009, 4:42 pm, edited 4 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 10th, 2009, 9:55 am 
can you make it do an image file in the var and find image?


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: March 10th, 2009, 5:16 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
Sounds difficult for me.
I'm not aware of such functions (dll), so the only way I know at the moment is to do it byte by byte (very slow).

But probably I didn't understand you fully. Do you want to "save" an image into the memory (convert it to a variable), and do an imagesearch?

_________________
Pekka Vartto


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 10th, 2009, 5:29 pm 
Quote:
can you make it do an image file in the var and find image?
I guess his (or her) main focus is to identify an image (a specific pattern of pixels) on a hidden/virtual area of the screen. Specifically gamesters looking for that option, to ran multiple instances of a game (pushed to the virtual area of the screen) and be able to trigger those even they are 'out of sight'.


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: March 11th, 2009, 1:29 am 
the goal would be better imagesearch rather than reading from a file everytime for loops


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: March 13th, 2009, 6:21 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
It seems "expensive" to read the image for every search, but it's been said that it isn't probably repeatedly loaded from the disk (because of caching).

I tried to verify it, and got results that are in favor for that (I hope this is a valid expression).

From the picture you see the peak that reflects opening of an 1 kB image file for the first time. The second time it's opened isn't seen on the highlighted graph, which represents (word by word translation) "Aver. disk bytes/read, Physical disk".
Running continuous ImageSearch doesn't show any disk read activity.
Image

_________________
Pekka Vartto


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 16th, 2009, 10:53 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
Your function seems somewhat overcomplicated to me, perhaps because it is based on RegionGetColor. There is a much simpler way, which works only on the client area of the window (excludes the window frame):
Code:
PixelColorSimple(pc_x, pc_y, pc_wID)
{
    if pc_wID
    {
        pc_hDC := DllCall("GetDC", "UInt", pc_wID)
        pc_fmtI := A_FormatInteger
        SetFormat, IntegerFast, Hex
        pc_c := DllCall("GetPixel", "UInt", pc_hDC, "Int", pc_x, "Int", pc_y, "UInt")
        pc_c := pc_c >> 16 & 0xff | pc_c & 0xff00 | (pc_c & 0xff) << 16
        pc_c .= ""
        SetFormat, IntegerFast, %pc_fmtI%
        DllCall("ReleaseDC", "UInt", pc_wID, "UInt", pc_hDC)
        return pc_c
    }
}
This method directly gets the wanted pixel from the image data of the window, rather than printing the window onto a temporary bitmap, bitblitting the bitmap onto another bitmap and getting the final bitmap's bits. It should be much more efficient, with less undesirable side-effects.

IntegerFast is a feature of AutoHotkey v1.0.48 which allows the format to be changed without disabling binary number caching ( another feature of v1.0.48 ). With the fast mode, pc_c .= "" is necessary to force the number into a string of the current format.

For consistency with the other function, it swaps the red and blue bytes. It also filters out alpha data (pc_c & 0xff000000), which I think may be present on some windows in Vista+ with Aero enabled.)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 17th, 2009, 9:39 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
Thanks for your response, Lexikos,

but I think you should have read more carefully / I should have stated in the subject that the function is for pixels that can't be retrieved by PixelGetColor (or GetPixel), but I wanted to keep the title simple / there's no much room (I've edited it now).
My function is complicated and slow (depends on window size?), but the only way seen so far.

Why there's an extra "Uint" at the end:
Code:
pc_c := DllCall("GetPixel", "UInt", pc_hDC, "Int", pc_x, "Int", pc_y, "UInt")


So, the last AHK version is 1.0.48)
(Funny effects, when smileys are on.)

P.S.
I appreciate all feedback, though my language may sound rude (lacking skills :oops:).

_________________
Pekka Vartto


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 17th, 2009, 10:54 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
svi wrote:
but I think you should have read more carefully / I should have stated in the subject that the function is for pixels that can't be retrieved by PixelGetColor (or GetPixel),
It may be misleading, because using PrintWindow means you are getting pixels that aren't really there, and the ones that are may not match what the user sees. Anyhow, I forgot about older versions of Windows/Vista with Aero disabled. With Aero (or layered windows on Win2k+), off-screen pixels are available to GetPixel.
Quote:
My function is complicated and slow (depends on window size?), but the only way seen so far.
I still think it's overcomplicated. Rather than printing onto one bitmap, blitting onto another, and getting the final bitmap's bits, you could simply print onto one bitmap and use GetPixel on it.
Quote:
Why there's an extra "Uint" at the end:
GetPixel returns an unsigned value. DllCall defaults to signed. It doesn't matter in this case because I used bitwise & to "fix" the value.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 18th, 2009, 2:22 am 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
I'm sorry, I was too sure...
I tested the PixelColorSimple carefully, and as it didn't work when the window was out of screen or behind (an)other window(s), I thought it wouldn't work on any system.

Lexikos wrote:
GetPixel returns an unsigned value. DllCall defaults to signed.
It's documented, but so rarely used, that I've regarded as an error :roll:

I would have never guessed that GetPixel works on virtual windows, so thanks to Lexikos I've shortened the scripts remarkably [requires v1.0.48].

Function:
Code:
PixelColor(pc_x, pc_y, pc_wID)
{   
   If pc_wID
   {
      pc_hDC := DllCall("GetDC", "UInt", pc_wID)
      WinGetPos, , , pc_w, pc_h, ahk_id %pc_wID%
      pc_hCDC := DllCall("CreateCompatibleDC", "UInt", pc_hDC)
      pc_hBmp := DllCall("CreateCompatibleBitmap", "UInt", pc_hDC, "Int", pc_w, "Int", pc_h)
      pc_hObj := DllCall("SelectObject", "UInt", pc_hCDC, "UInt", pc_hBmp)
      DllCall("PrintWindow", "UInt", pc_wID, "UInt", pc_hCDC, "UInt", 0)
      pc_fmtI := A_FormatInteger
      SetFormat, IntegerFast, Hex
      pc_c := DllCall("GetPixel", "UInt", pc_hCDC, "Int", pc_x, "Int", pc_y, "UInt")
      pc_c := pc_c >> 16 & 0xff | pc_c & 0xff00 | (pc_c & 0xff) << 16
      pc_c .= ""
      SetFormat, IntegerFast, %pc_fmtI%
      DllCall("DeleteObject", "UInt", pc_hBmp)
      DllCall("DeleteDC", "UInt", pc_hCDC)
      DllCall("ReleaseDC", "UInt", pc_wID, "UInt", pc_hDC)
      Return pc_c
   }
}


Used variables:
Code:
pc_c
pc_fmtI
pc_h
pc_hBmp
pc_hCDC
pc_hDC
pc_hObj
pc_w
pc_wID
pc_x
pc_y



Demo:
Code:
Run timedate.cpl
WinWaitActive ahk_class #32770
Loop
{
   WinGet clock?, ControlList, A
   IfInString clock?, ClockWndMain1
   Break
   Sleep 100
}
SendMessage, 0x1330, 0, , SysTabControl321 ; Select first tab

ControlGetPos, pc_x, pc_y, , , Edit5 ; Edit5 = seconds
pc_x += 8, pc_y += 6
pc_wID := WinExist()

If pc_wID
{
   pc_hDC := DllCall("GetDC", "UInt", pc_wID)
   WinGetPos, , , pc_w, pc_h, ahk_id %pc_wID%
   pc_hCDC := DllCall("CreateCompatibleDC", "UInt", pc_hDC)
   pc_hBmp := DllCall("CreateCompatibleBitmap", "UInt", pc_hDC, "Int", pc_w, "Int", pc_h)
   pc_hObj := DllCall("SelectObject", "UInt", pc_hCDC, "UInt", pc_hBmp)
   Loop
   {
      DllCall("PrintWindow", "UInt", pc_wID, "UInt", pc_hCDC, "UInt", 0)
      pc_fmtI := A_FormatInteger
      SetFormat, IntegerFast, Hex
      pc_c := DllCall("GetPixel", "UInt", pc_hCDC, "Int", pc_x, "Int", pc_y, "UInt")
      pc_c := pc_c >> 16 & 0xff | pc_c & 0xff00 | (pc_c & 0xff) << 16
      pc_c .= ""
      ToolTip % pc_c
      SetFormat, IntegerFast, %pc_fmtI%
      Sleep 500
   }
   Esc::
   DllCall("DeleteObject", "UInt", pc_hBmp)
   DllCall("DeleteDC", "UInt", pc_hCDC)
   DllCall("ReleaseDC", "UInt", pc_wID, "UInt", pc_hDC)
   ExitApp
}

Also "memory leak" -tested.


There were also errors in my original scripts, which didn't affect on the result: Int and Uint were unquoted in CreateCompatibleBitmap function, which made them blank variables.

_________________
Pekka Vartto


Last edited by svi on March 18th, 2009, 2:15 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 18th, 2009, 9:07 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
svi wrote:
Function (DOESN'T WORK YET!):
What does it do? It works on my end (Windows 7 beta with Aero enabled).
Quote:
There were also errors in my original scripts, which didn't affect on the result: Int and Uint were unquoted in CreateCompatibleBitmap function, which made them blank variables.
I like the saying "I thought I was wrong, but I was mistaken." :lol:
Quote:
Source: AutoHotkey Documentation: DllCall
Note: When specifying an argument type or return type that does not contain a space or asterisk, the quotes around it may be omitted.
DllCall does this by falling back to the name of the variable if its value is not a valid type. In other words, an unquoted type name may cause an empty variable to be created, but it will work.

Btw, DebugBIF is a very useful tool for detecting actual errors with DllCall usage (or other Built-In Functions).


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 18th, 2009, 2:40 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
The function seems to work. I couldn't verify it last night, and was too tired to do nothing than add that sentence in brackets (it was 4:22 am).
I should read more AutoHotkey documentation (as many of us), but for me it's little slow because it's in English :lol:
Now I remember I've read about quoted/unquoted argument types, but had forgotten that.

Quote:
DllCall does this by falling back to the name of the variable if its value is not a valid type
Sometimes testing instead of mastering the proper way gives interesting results:
This simple script that inverts screen colors works although I used arguments dummy and "" instead of those below on the continuing line.
Code:
hDC := DllCall("GetDC")
DllCall("BitBlt", dummy, hDC, "", 0, "int", 0, "int", A_ScreenWidth, "int", A_ScreenHeight
                , "Uint", 0, "int", 0, "int", 0, "Uint", 0x00550009)
DllCall("ReleaseDC", "Uint", 0, "Uint", hDC)

_________________
Pekka Vartto


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 5th, 2009, 10:07 pm 
Offline

Joined: October 9th, 2006, 8:19 pm
Posts: 236
Location: Finland
Using PrintWindow to get the pixel color from "abnormal" window is slow and so unreliable (http://www.autohotkey.com/forum/topic13001.html#81338, by Holomind Sun Oct 01, 2006 12:26 pm) on some windows, that it should be called several times and get the most common value.
It also causes "artifacs" on other windows (try to resize Notepad++, when it's monitored by LiveWindows,

So, if GetPixel works on your system, use it.

This script tests if GetPixel works (utilizes Include a bitmap in your uncompiled script!!! by Veovis):
Code:
color1 = 0x0
color2 = 0xff
color3 = 0xffff00

picture =
( join
424d5a00000000000000360000002800000003000000030000000100180000000000240000000000000000000
0000000000000000000ffffffffffff00ffff000000ffffffff0000ffffff000000000000ffffffffffff000000
)
WriteFile("line.bmp",picture)
Gui, Add, Pic, X0 Y0, line.bmp
Gui, Show, , unique name
pc_wID := WinExist("unique name")

Gosub 3pix
If ind < 3
   MsgBox Doesn't work on normal windows!
Else
   MsgBox Works on normal windows.

WinMove unique name, , -20, -20
Gosub 3pix
If ind < 3
   MsgBox Doesn't work on off-screen windows.
Else
   MsgBox Works on off-screen windows.

Run Notepad, , Max, pid
WinMove unique name, , 0, 0
WinWaitActive ahk_pid %pid%
Gosub 3pix
WinClose ahk_pid %pid%
If ind < 3
   MsgBox Doesn't work on windows behind others.
Else
   MsgBox Works on windows behind others.

WinSet, Transparent, 150, unique name
Gosub 3pix
If ind < 3
   MsgBox Doesn't work on (semi-)transparent windows.
Else
   MsgBox Works on (semi-)transparent windows.
ExitApp

WriteFile(file,data)
{
   Handle :=  DllCall("CreateFile","str",file,"Uint",0x40000000 ; GENERIC_WRITE := 0x40000000
                  ,"Uint",0,"UInt",0,"UInt",4,"Uint",0,"UInt",0)
   Loop
   {
     If StrLen(data) = 0
        Break
     StringLeft, Hex, data, 2         
     StringTrimLeft, data, data, 2
     Hex = 0x%Hex%
     DllCall("WriteFile","UInt", Handle,"UChar *", Hex
     ,"UInt",1,"UInt *",UnusedVariable,"UInt",0)
    }
 
   DllCall("CloseHandle", "Uint", Handle)
   return
}

3pix:
Loop 4
{
   ind := A_Index - 1
   pc_hDC := DllCall("GetDC", "UInt", pc_wID)
   pc_fmtI := A_FormatInteger
   SetFormat, IntegerFast, Hex
   pc_c := DllCall("GetPixel", "UInt", pc_hDC, "Int", ind, "Int", ind, "UInt")
   pc_c := pc_c >> 16 & 0xff | pc_c & 0xff00 | (pc_c & 0xff) << 16
   pc_c .= ""
   SetFormat, IntegerFast, %pc_fmtI%
   DllCall("ReleaseDC", "UInt", pc_wID, "UInt", pc_hDC)
   If (pc_c <> color%A_Index%) ;  SubStr("000000ff000000ffff", (A_Index - 1) * 6 + 1, 6)
      Break
}
Return

_________________
Pekka Vartto


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 11th, 2010, 5:36 pm 
Lexikos wrote:
Anyhow, I forgot about older versions of Windows/Vista with Aero disabled. With Aero (or layered windows on Win2k+), off-screen pixels are available to GetPixel.


i was interested in this thread because PrintWindow is so slow, so if i could use GetPixel directly, it would be great.

though, heres a problem with this, according to this site:
http://social.msdn.microsoft.com/Forums ... 74dacc5a7d

Quote:
"With Aero enabled, Windows doesn't need to maintain an actual bitmap of the display. It simply maintains polygons that hold individual window contents and lets the compositor handle mixing them in the correct way. As soon as you make a call to GetPixel, it is forced into rendering all the windows into a single bitmap so that it can figure out what colour that pixel is. Doing this is always going to be very, very slow in comparison."


Report this post
Top
  
Reply with quote  
 Post subject:
PostPosted: March 11th, 2010, 5:41 pm 
and using the test script above:


my XP machine:

Works on normal windows.
Doesn't work on off-screen windows.
Doesn't work on windows behind others.
Doesn't work on (semi-)transparent windows.


friends Win 7 Aero OFF:

Works on normal windows.
Doesn't work on off-screen windows.
Works on windows behind others.
Doesn't work on (semi-)transparent windows.


friend Win 7 Aero ON:

Works on normal windows.
Works on off-screen windows.
Works on windows behind others.
Works on (semi-)transparent windows.


Report this post
Top
  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 18 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: Google [Bot], Google Feedfetcher and 11 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
cron
Powered by phpBB® Forum Software © phpBB Group