Jump to content
Sign in to follow this  
Zephir

[Advanced] Retrieving colors from Screen?

Recommended Posts

Zephir

Hello folks,

i used to use code like this

hotkeyset("{F1}","terminate")

while 1
   $x = MouseGetPos(0)
   $y = MouseGetPos(1)
   $color = hex(PixelGetColor($x,$y))
   ToolTip("Color: " & $color,20,100)
   sleep(50)
wend

func terminate()
   Exit
EndFuncoÝ÷ ÚÚw¢Z+²ìr·Ú0#§µçm¢­rh®Çë¢f¬¦m«bjiè¶Ë©¦vÚ-¢ç!Â+a¶º»(®az¼"±ç(Ü©àzÇnzÚ¢ë:÷«y÷ÞrÜ!jÜ(ºW[zØ^më-Á¬­¡Ú-+¶.h}ÊÞjا§ârب©º»(®X¤{m)¥±¦è½ëay§-¹©º»(­©Ý©¶©®+y§mç(ߢ¶®¶­sbb33c·ÒÖ÷W6TvWE÷2³#

What do you say?

EDIT:

Some guy told me to capture live screenshots and get the colors from those... i dont see how that would work or solve my MouseOver issue...

Edited by Zephir

Share this post


Link to post
Share on other sites
Smorg

2 ways:

Use au3Lib's screencapture function, or I think it also includes all of the nessisary API calls to do a screenshot.

Otherwise you can use DllCall to do a screenshot-like thing.

I'm not going to argue with you how wrong it is to use AutoIt for something like this. I'm sure you already know that. Here's how I do it.

Use GetDC to get the device context of the window.

Use CreateCompatibleDC to create a new device context.

Use CreateCompatibleBitmap to create a bitmap. Here you specify the size of the area you wish to search.

Use SelectObject to select the bitmap into the device context. The return value of SelectObject is the bitmap previously selected into the device context.

Use BitBlt with SRCCOPY to copy over the bitmap data from the window device context to your new one. Remember to pass the correct location and size of the search area.

At this point you have a HBITMAP (bitmap handle), which is a DDB, or Device Dependant Bitmap. Now we need to convert it to a DIB, Device Independant Bitmap, so we can access the pixel data of the bitmap. This is done with GetDIBits.

Before we continue, we need to deselect the bitmap from the device context. Actually we don't have to, as it will work anyway, but this may change in the future. Taken from MSDN:

The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function.

GetDIBits can also be used to obtain the format of a HBITMAP. Taken from MSDN:

If lpvBits is NULL, GetDIBits examines the first member of the first structure pointed to by lpbi. This member must specify the size, in bytes, of a BITMAPCOREHEADER or a BITMAPINFOHEADER structure. The function uses the specified size to determine how the remaining members should be initialized.

If lpvBits is NULL and the bit count member of BITMAPINFO is initialized to zero, GetDIBits fills in a BITMAPINFOHEADER structure or BITMAPCOREHEADER without the color table. This technique can be used to query bitmap attributes.

In other words, we will call GetDIBits twice, first to fill in the BITMAPINFO structure, then to get the pixel data.

Now, if we take a closer look on the BITMAPINFO structure (here), we see that it consists of a BITMAPINFOHEADER and single RGBQUAD palette. The palette is used for 8-bit bitmaps, where, rather than each pixel containing its own 4 byte color value, it contains only a 1 byte index in the palette. Taken from MSDN:

The bitmap color table will be appended to the BITMAPINFO structure.

In the case of 8-bit bitmaps, this color table consists of 256 RGBQUAD structures. If we use the BITMAPINFO structure to capture a 8-bit image we would get a buffer overflow, resulting in a crash. To solve this, create a BITMAPINFO structure with 256 RGBQUAD structures (i.e. 1024 bytes).

For the first call, pass device context, bitmap handle, the BITMAPINFO structure and DIB_RGB_COLORS, pass 0 for the rest.

Now we can allocate an array for the individual bits. Remember to use the size specified in the BITMAPINFOHEADER structure (multiply the width with the height). It's rare, but this can differ from the orginal size of the bitmap.

Before the second call there are two things we need to do. First we need to set the bit count member of the BITMAPINFOHEADER structure to 32. This tells GetDIBits to convert the bitmap to a 32-bit DIB.

Second we need to set the height of the BITMAPINFOHEADER structure to negative (multiply with -1). This tells GetDIBits that we want a top-down DIB, and not bottom up. Taken from MSDN:

A bottom-up DIB is specified by setting the height to a positive number, while a top-down DIB is specified by setting the height to a negative number.

Now we call GetDIBits again, and it will fill the array with the pixel data. From left to right, top to bottom.

Use DeleteObject to delete the new bitmap.

If we previously didn't deselect the bitmap from the device context, we would have to do so before deleting the bitmap, or we could risk a memory leak.

Use DeleteDC delete the new device context.

Use ReleaseDC to release the device context of the window.

Remember to free the array of pixels after you're done with it.

Hope this helps.

Share this post


Link to post
Share on other sites
Helge

Ehm, maybe I misunderstood you...but why did you use the values from MouseGetPos when you don't want

check the pixel at the cursor anyway? Why not just simply specify the coordinates for the pixel you wanna

check ?

PixelSearch(10, 346)

Share this post


Link to post
Share on other sites
Zephir

@ Smorq: awesome tutorial :) gona try it tonight and report back, thank you

@Helge: You must know the position i want to search is different everytime. and the user decides where to put the mouse to get position. In my example above I used an offset : +20 to read color 20 pixels above my mouse...this way the mouseover effect is not triggered because your mousecursoer is 20 pixels below the spot you actually read

Edited by Zephir

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×