Link to home
Start Free TrialLog in
Avatar of jamesxi
jamesxi

asked on

The GetPixel API function - It doesn't work!

'Delaration for the getPixel API function
Private Declare Function GetPixel Lib "gdi32" _
    (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long

'code
Dim pixVal As Long
pixVal = GetPixel(Form1.hdc, 100, 100)


The value pixVal gets set to is -1, when it should be some positive number. What are possible problems with this code? It seems like it should certainly work to me.
Avatar of Ryan Chong
Ryan Chong
Flag of Singapore image

Avatar of JonFish85
JonFish85

try setting the forms ScaleMode = vbPixels.

hope this helps!
Avatar of jamesxi

ASKER

Changing the mode to pixel didn't help. That MVPS.org example used almost the exact same code that I did. The only difference I can see is that they used a picture instead of a form. Shouldn't getPixel work on any type of object though?
try using your same code on a picturebox... That way we know that the GetPixel() is working...
Avatar of jamesxi

ASKER

It won't give me the hdc of the picture. I'll try using another program I have though that can get the hdc of any object.
Avatar of jamesxi

ASKER

No, it simply does not work at all. Not on a picurebox. Not on anything. Did you try to get the code to work? Its extremely quick to try. Just copy the delcare statement in a module, then the rest of the code in with the form code.
its worked for me before, as I've used the GetPixel stuff... Im burning a cd now, but Ill try it in a little bit.
Avatar of jamesxi

ASKER

OK, well see if you can stuff a working getPixel function in a module so all i have to do is call the thing using a call statement. It seems like it works perfectly for everyone but me!
Avatar of jamesxi

ASKER

Wait a second, can only one hDC be open at a time?
this worked for me:

Option Explicit
Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long

Private Sub Command1_Click()
  MsgBox GetPixel(Me.hdc, 100, 100)
End Sub

when Me.Picture is set to some whatever sort of picture...
Avatar of jamesxi

ASKER

OK, now i'm mad!!!!!!!!! I copied and pasted, then fixed your code since getPixel has to be in a module and it has to be public. The command click has to be on the form code. After that I ran the program and it gave me this value: -1! I click the button and it gives me a -1! Thats right. -1!
Avatar of jamesxi

ASKER

Finally! The author of GetPixel program (Feng Jian Yu) sent me this code which actually works! I am still trying to figure out why it works because my code and your code look just like it! I'll let you know when I figure it out.

'**************************modules*************************
Type POINTAPI
    X As Long
    Y As Long
End Type
Public Declare Function GetDC Lib "User32" (ByVal hWnd As Long) As Long
Public Declare Function GetCursorPos Lib "User32" (lpPoint As POINTAPI) As Long
Public Declare Function GetPixel Lib "gdi32" (ByVal hDC As Long, ByVal X As Long, ByVal Y As Long) As Long
Public Declare Function ReleaseDC Lib "User32" (ByVal hWnd As Long, ByVal hDC As Long) As Long
'*********************************************************

Private Sub Form_Load()
    Dim pt As POINTAPI
    Dim c As Long

    h = GetDC(0)
    GetCursorPos pt
    c = GetPixel(h, pt.X, pt.Y)
    ReleaseDC 0, h
End Sub
put all this in a form:

Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long

Private Sub Command1_Click()
    dim a as long
    a = GetPixel(Me.hdc, 100, 100)
    MsgBox a
End Sub

it will work
why it does not work for you is that the declaration is probably in a module
if in a module the declaration has to be public

what that other code does:
Private Sub Form_Load()
   'it dims a variable as pointapi (udt)
   Dim pt As POINTAPI
   ' it dims a variable as a long
   Dim c As Long

   'it gets the device context to the screen
   'not your form
    h = GetDC(0)
    'it puts the screen coordinates of the mouse in pt
    GetCursorPos pt
    'it gets the color of the pixel under the mouse
    c = GetPixel(h, pt.X, pt.Y)
    'it releases the device context
    ReleaseDC 0, h
End Sub
The main difference btw the ode that works on jamesxi and the others whih are simillar is that Feng Jian Yu uses h = GetDC(0) to get the handle to a display device context, in this case the form.
disregard the previous, my c key is a bit jammed.
The main difference btw the code that works on jamesxi machine and the others which are simillar is that Feng
Jian Yu uses h = GetDC(0) to get the handle to a display device context, in this case the form.
Avatar of jamesxi

ASKER

OK. I understand the problem better now, though I am not really any closer to the solution. My progrom is designed to grab the pixel color ANYWHERE on the screen, not just within my program. Therefore, it must get the DC handle on whatever object it is currently over.

The problem occurs when the object does not start at 0,0 on the screen. This means if the object starts at 100,100 on the screen then pixels at 10,10 do not even exist which would be why it returns -1.

To solve this issue I guess I have to figure out which pixel the object starts at on the screen. I hope this is possible!
you could try the ScreenToClient and ClientToScreen API's. Im not sure what you are really asking though...
Avatar of jamesxi

ASKER

This code works when the curser is over the desktop screen but its performance is spotty at best when over other objects:

'module code
Private Type POINTAPI
    x As Long
    y As Long
End Type
Private curPoint As POINTAPI

Public Sub getColorOverCurser(ByRef red As Integer, ByRef green As Integer, ByRef blue As Integer)
Dim objHandle As Long
Dim dcHandle As Long
Dim fxResponse As Long
Dim pixVal As Long
Dim xLoc As Long
Dim yLoc As Long

Call GetCursorPos(curPoint)
xLoc = curPoint.x
yLoc = curPoint.y

objHandle = WindowFromPoint(xLoc, yLoc) 'gets object handle from given coord
dcHandle = GetDC(objHandle) 'gets dc handle from given object
 
pixVal = GetPixel(dcHandle, xLoc, yLoc)

red = pixVal Mod &H100  'get red
pixVal = pixVal \ &H100  'process formula
green = pixVal Mod &H100 'get green
pixVal = pixVal \ &H100  'process formula
blue = pixVal Mod &H100 'get blue
 
fxResponse = ReleaseDC(objHandle, dcHandle) 'releases dc handle

End Sub

'form objects: a timer(interval set to 200) and a textbox

'form code:
Private Sub Timer1_Timer()
Dim red As Integer
Dim green As Integer
Dim blue As Integer
Call getColorOverCurser(red, green, blue)
Text1.Text = red & "," & green & "," & blue
End Sub
Avatar of jamesxi

ASKER

Oops, I forgot some code in my last comment. The API declares in the module should be:

'gets position of curser
Public Declare Function GetCursorPos Lib "user32" _
    (lpPoint As POINTAPI) As Long

'gets the window handle from a given point
Private Declare Function WindowFromPoint Lib "user32" _
    (ByVal xPoint As Long, ByVal yPoint As Long) As Long

'gets the color of a given pixel
Private Declare Function GetPixel Lib "gdi32" _
    (ByVal hDC As Long, ByVal x As Long, ByVal y As Long) As Long
'gets the device context(DC) of a given handle
Private Declare Function GetDC Lib "user32" _
(ByVal hWnd As Long) As Long
'releases the device context(DC) of a given handle
Private Declare Function ReleaseDC Lib "user32" _
(ByVal hWnd As Long, ByVal hDC As Long) As Long
Avatar of jamesxi

ASKER

The code I just gave works just fine when the curser is over the desktop screen. However, when I open up a computer game to test the function with, it will work incorrectly. When my curser is over position 20, it will actually get the value of a nearby pixel for example 28, 44(?). So my question is, why is it not getting the pixel I tell it to get, and how will it get the pixel I want it to get?
Avatar of jamesxi

ASKER

Well, since my question has changed and this message is so long I'll just delete this and start a new question.
jamesxi
the code given to you by Feng Jian Yu will work and it will work always because it does not get the device context of a particular window it gets the device context of the screen
but regarding your comment:
>When my curser is over position
20, it will actually get the value of a nearby pixel for example 28, 44(?). So my question is, why is
it not getting the pixel I tell it to get, and how will it get the pixel I want it to get?<

how do you know the position of mouse cursor ?
what i mean is the GetCursorPos api returns the position of the cursors hotspot and how do you know where the cursors hot spot is ?
especially is the cursor of that game the standard arrow ?
might it just be possible you think the cursors hotspot is at say 20,20 when it really is at say 23,21
did you ask the color of a specific pixel on the screen and did you count the pixels just to make sure ?
as far as my experience goes the method by Feng Jian Yu will give correct results no matter what window the cursor is over
but then again my experience does ofcource not include every possible hardware combination,
I've done a lot of work with GetPixel, and I know exactly what's going on.
Using GetPixel on a location OUTSIDE of the edge of an hDC returns -1. That means you're trying to sample from outside your hDC. To get an hDC from a picture, you have to create a new hDC of your own in memory, and copy the picture onto that hDC. Or you can just put the picture in a picturebox - that'll work too.

If you want to sample directly from the screen, you have to retrieve the hDC of the screen. But then, if someone moves the mouse over your form or picturebox, the mouse coordinates you get won't be correct. use GetCursorPos for that.

Are you trying to get pixels while a fullscreen game is open?
Wow, long topic,

Well I can say that there are many ways other then Get/Set Pixel, by directly accessing the array of the image by using GetDIBits, but that's a bit complicated.
I'm sure my friend Janus (Kevin) will drop by this one.

Later,

Linguar
Woah, speak of the devil, he got in before me even :)
This should do the trick:

Public Declare Function GetDesktopWindow Lib "user32" () As Long
Public Declare Function GetDC Lib "user32" (ByVal hWnd As Long) As Long
Public Declare Function ReleaseDC Lib "user32" (ByVal hWnd As Long, ByVal hDC As Long) As Long
Public Declare Function GetCursorPos Lib "user32" Alias "GetCursorPos" (lpPoint As POINTAPI) As Long
Public Type POINTAPI
   x As Long
   y As Long
End Type

Public Function getColorUnderCursor() as Long
Dim mousePoint as POINTAPI
Dim mouseX As Long
Dim mouseY As Long
Dim deskWnd As Long, deskDC As Long
deskWnd = GetDesktopWindow
deskDC = GetDC(deskWnd)
Call GetCursorPos(mousePoint)
mouseX = mousePoint.x
mouseY = mousePoint.y
getColorUnderCursor = GetPixel(deskDC, mouseX, mouseY)
Call ReleaseDC(deskWnd, deskDC)
End Sub
correct
and if i remember it correct jamesxi was the first to post that code
so jamesxi since you were the first to post the solution give the points to yourself :-)
Yes james, you were on the right track the whole time. :)
Avatar of jamesxi

ASKER

I restarted the question since I knew most people just wouldn't bother reading through the whole thing. Sure enough, someone was able to see the problem. The problem is that you have to specify the position in the window to grab the pixel at, NOT the screen. Luckily their is an API function that does this for me (the ScreenToClient function). The reason I was getting a negative 1 sometimes is that certain objects on a window simply do not work with the GetPixel function. To correct that you have to do some weird BitBlt trick that is illustrated at the BlackBeltVB site (http://blackbeltvb.com/free/GETPIXEL.ZIP)

In addition to the code I already have this declare is needed:
Private Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long

Modified getcolor routine:

Public Sub getColorOverCurser(ByRef red As Integer, ByRef green As Integer, ByRef blue As Integer)
   'The function that works!:
   Dim objHandle As Long 'wnd handle for object under curser
   Dim dcHandle As Long 'dc handle for object under curser
   Dim pixVal As Long 'the getpixel function should set
   Dim xLoc As Long
   Dim yLoc As Long
   
   Call GetCursorPos(curPoint)
   xLoc = curPoint.X
   yLoc = curPoint.Y
   
   objHandle = WindowFromPoint(xLoc, yLoc) 'gets object handle from given coord
   dcHandle = GetDC(objHandle) 'gets dc handle from given object
   
   
   'Converts screen coordinates to window coordinates
   ScreenToClient objHandle, curPoint
   xLoc = curPoint.X
   yLoc = curPoint.Y
   
   pixVal = GetPixel(dcHandle, xLoc, yLoc)
   
   red = pixVal Mod &H100  'get red
   pixVal = pixVal \ &H100  'process formula
   green = pixVal Mod &H100 'get green
   pixVal = pixVal \ &H100  'process formula
   blue = pixVal Mod &H100 'get blue
   
   Call ReleaseDC(objHandle, dcHandle) 'releases dc handle

End Sub

Sorry that I stopped responding but I thought I actually deleted the question, but here it is a few days later still alive. Thanks to all for helping me out and especially to PaulHews who figured out the solution.
Hi jamesxi,
It appears that you have forgotten this question. I will ask Community Support to close it unless you finalize it within 7 days. I will ask a Community Support Moderator to:

    Refund points and save as a 0-pt PAQ.

jamesxi, Please DO NOT accept this comment as an answer.
EXPERTS: Post a comment if you are certain that an expert deserves credit.  Explain why.
==========
DanRollins -- EE database cleanup volunteer
ASKER CERTIFIED SOLUTION
Avatar of Netminder
Netminder

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial