-
Sep 15th, 2021, 06:53 PM
#1
Thread Starter
Junior Member
GetPixel() replacement - read from a BITMAP buffer instead
Currently I access the pixel color using the GetPixel() API, but it's awfully slow. To access 1000 pixels it takes ~31 secs!
Code:
Dim lngDC As Long, lngScreen As Long, lngColor As Long
lngDC = GetDesktopWindow() ' the entire screen
lngScreen = GetWindowDC(lngDC)
lngColor = GetPixel(lngScreen, posX, posY) ' return COLORREF containing the RGB values
ReleaseDC lngDC, lngScreen
I want a replacement for GetPixel(), let's call it GetPixelB(), with "B" standing for Byte. I know it's possible to use GetDesktopWindow() and GDI+ to capture the entire screen to a BITMAP buffer in the memory, and then we can read the pixel color from this buffer, which is extremely efficient compared to the standard API. A replacement looks like this (pseudo-code):
Code:
Dim lngDC As Long, lngScreen As Long, lngColor As Long
lngDC = GetDesktopWindow() ' the entire screen
lngScreen = GetWindowDC(lngDC)
buffer = ScreenToBuffer(lngScreen) ' this is the sauce, so to speak
ReleaseDC lngDC, lngScreen ' release it, reason: we already have the buffer to work with
lngColor = GetPixelB(buffer, posX, posY)
Two useful posts:
https://www.vbforums.com/showthread....ll=1#post21100
https://www.vbforums.com/showthread....=1#post5076067
Can someone help me with this? I've been searching but I just can't put it together.
PS: the lngColor of both GetPixel() and GetPixelB() must be equal when accessing the same pixel, after all, the new function is a replacement so compatibility in so far as result is concerned is a must.
-
Sep 15th, 2021, 08:38 PM
#2
Re: GetPixel() replacement - read from a BITMAP buffer instead
I know exactly how to do what you ask. Can't do it in a couple minutes though. I have to hunt down a bunch of constants, API declarations and structures to do it. The pains of VB6
I'll check back on this thread later and if no one has given you a satisfactory solution, I'll go through my old VB6 projects and put together something for you. It's very easy to do in principle. It just requires a tonne of boiler plate code to do it.
EDIT:
The first link seems to have all the necessary boiler plate to do it but I like to test things myself. Don't have the time at this very moment but like I said, I'll try to help you a little later. Hopefully one of the VB6 regulars here already has something like this put together and can just post it for you before I get back.
Last edited by Niya; Sep 15th, 2021 at 08:43 PM.
-
Sep 15th, 2021, 09:44 PM
#3
Thread Starter
Junior Member
Re: GetPixel() replacement - read from a BITMAP buffer instead
Thank you Niya. I reckon the links I provided are useful because I know more or less what is required, having done this with modern C++ in the past.
Two things that are worth mentionting:
.biCompression must be BI_RGB, which is uncompressed RGB.
.biBitCount must be 32. In practice it can be 24, but would require padding. I think it's faster to use 32 which is the default color depth of the OS since Windows 8, although in the end only 24 is truly used, 8 for each of R, G, B. The alpha is left unused.
Hope you or someone can help me out
My app's routine speed will improve by, I guess, at least 20%. Currently it uses the GetPixel() API to read pixels from the screen, which is often static, so working with buffer is a huge improvement in this scenario where you don't need to update the buffer on every GetPixelB() call. When needed, I can simply update the buffer with ScreenToBuffer(), which is fast - less than 50ms with my screen resolution.
I mention an improvement of at least 20% in my app's routine speed, but if we were to compare the two functions alone - the GetPixel() API vs this custom GetPixelB() function - then the latter is >1000x faster (!!!) but only if you are making hundreds or thousands of calls using the same buffer. I make hundreds before the buffer needs to be updated.
Last edited by veloz; Sep 16th, 2021 at 01:10 AM.
-
Sep 16th, 2021, 04:18 AM
#4
Re: GetPixel() replacement - read from a BITMAP buffer instead
Originally Posted by veloz
...done this with modern C++ in the past.
...I can simply update the buffer with ScreenToBuffer(), which is fast - less than 50ms with my screen resolution.
...GetPixel() API vs this custom GetPixelB() function - then the latter is >1000x faster (!!!)
Since you've done this in the past, and now report concrete performance-numbers,
everything seems to be working fine already.
So, if there's anything you need further help with, what is it exactly?
Olaf
-
Sep 16th, 2021, 04:36 AM
#5
Re: GetPixel() replacement - read from a BITMAP buffer instead
Everything about this seems so scatterbrained. "From the screen?" Is this a Goggle Translate issue?
Why wouldn't you just use GetDIBits() against the bitmap of the DC you want to pixel-peek?
You ask for 32 bits/pixel so why would you name this thing GetPixelB()? GetPixelL() for "Long" might make a lot more sense.
Or just return a 2D Long array from GetDIBits() and be all done.
-
Sep 16th, 2021, 05:03 AM
#6
Thread Starter
Junior Member
Re: GetPixel() replacement - read from a BITMAP buffer instead
@Schmidt
That was for a modern C++ project, months ago. I was able to put it together with examples from the internet. I have been so far unable to do the same for my old VB6 project which I'm upgrading. I'm not a programmer and it's a high bar to port one language to another.
@dilettante
Sorry, English is not my native language. What you describe is what I think is the way to go. Using GetDIBits() against the bitmap of the DC. But how can I do it, and read the pixels with the custom GetPixelB() function as I would with GetPixel() API? It seems like a relatively straightforward process for people with experience, but I'm breaking my head over it.
-
Sep 16th, 2021, 05:46 AM
#7
Re: GetPixel() replacement - read from a BITMAP buffer instead
If you are starting with just the hDC you will probably have to fetch its selected hBitmap.
So first create a junk hBitmap with CreateBitmap(), 1x1 pixel is probably good enough.
Then use SelectObject() to make this the selected bitmap, getting back the hBitmap that the DC had originally.
At that point you can call GetObject() on the de-selected hBitmap returned by SelectObject(), in order to get a BITMAP structure to get the dimensions from.
Then you could ReDim a Pixels() As Long array with the width and height.
Then you can call GetDIBits() to fill the Pixels() array.
Finally call SelectObject to put the hBitmap back, and destroy the returned temporary hBitmap by calling DeleteObject() on it.
That should be close, if not exactly the steps needed.
As long as you used ReDim Pixels(.bmWidth , .bmHeight) you can use Pixels(X, Y) to get a pixel's color value.
-
Aug 27th, 2023, 02:42 PM
#8
New Member
Re: GetPixel() replacement - read from a BITMAP buffer instead
On Win7, this takes 3 seconds in the VB5 IDE, or compiled exe, wth am I doing wrong? Did
MS fix something to make GetPixel very acceptably fast recently?
Code:
Dim i As Long, j As Long, hdc As Long, c As Long
hdc = Picture1.hdc ' 100 x 100
For i = 1 To 1000
For j = 1 To 1000
c = GetPixel(hdc, i Mod 100, j Mod 100) ' GDI.GetPixel defined in Bruce McKinney's Hardcore win.tlb
Next
Next
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|