Hi, I'm writing code that unfortunately has to use a GetPixel call to check the color of every pixel on a fairly large 600x600 bitmap. The inner loop is fairly optimized but the big time spender is the call to GetPixel. I've seen many people using it, but I was wondering if there was code available or if someone has written an 'optimized' alternative? I'm sure others will be interested in it, and I've searched the board with no luck!

Thanx in advance!
Posted on 2004-03-03 12:44:31 by SubEvil
GetPixel and SetPixel are both very slow when you need max speed. What I use in such cases is CreateDIBSection which will return the memory address for the bitmap data. I then have direct memory access to each pixel without having to use the slow APIs. (You will also need other API functions to load your bitmap into that DIB section.)

Just remember that, if the biHeight member of the BITMAPINFOHEADER structure is positive, the bitmap will be a bottom-up DIB and its origin will be the lower left corner when you check each pixel.

Posted on 2004-03-03 14:13:00 by Raymond
I totally agree with the above - my own GetPixel function (from my CTexture class) is merely a wrapper to allow direct access to the image color data in the context of textures loaded via DirectX.
I used IDirect3DTexture8_LockRect to obtain the same result as CreateDIBSection, ie, return a ptr to the image color data.
I wonder why the gdi functions are so slow :P
Posted on 2004-03-03 22:24:51 by Homer
I deffinately need help on this one! This Proc causes a GPF on procedure exit (ret instruction), all the functions execute with the proper return values except the one listed near the end. I've left my own (untidy) comments, hope it's not too difficult to understand. What the hell am I doing wrong? Is the stack reset properly (should be automatic)? Am I freeing all DC's and Bitmaps and other "objects" correctly? Am I accessing variables and pointers correctly? It works without a GPF, until the last line! But it crashes when code execution passes back to the calling procedure!

EnterGetPixel PROTO :DWORD

pDIB DD 0 ;; Address of Bitmap bits allocated by CreateDIBSection and later used by GetDIBits
hBM DD 0 ;; Handle to newly created Bitmap, returned by CreateDIBSection, needs to be freed later!

EnterGetPixel PROC hDC :DWORD ;; DC with a BMP used to make a replica that NewGetPixel will be called upon!
LOCAL Alloc :DWORD ;; Pointer to a BitmapInfoEx structure, allocated on Heap and becomes the return address.
LOCAL tmpDC :DWORD ;; A temporary DC compatible with hDC
LOCAL tmpBM :DWORD ;; Stock object initially assigned to tmpDC
LOCAL hBM :DWORD ;; Handle to the currently selected object in hDC

call GetProcessHeap
INVOKE HeapAlloc, eax, HEAP_GENERATE_EXCEPTIONS or HEAP_ZERO_MEMORY, SIZEOF(BITMAPINFOEX) ;; We need the BITMAPINFO structure in NewGetPixel to get the Height for subtraction
mov Alloc, eax ;; Alloc will store our BITMAPINFOEX structure

INVOKE CreateCompatibleDC, hDC
mov tmpDC, eax

INVOKE GetCurrentObject, hDC, OBJ_BITMAP
mov hBM, eax

; mov hBM, mCall(SelectObject, hDC, hbmBackground)

m2m (BITMAPINFOEX PTR [Alloc]).BMI.bmiHeader.biWidth, BM.bmWidth
m2m (BITMAPINFOEX PTR [Alloc]).BMI.bmiHeader.biHeight, BM.bmHeight
m2m (BITMAPINFOEX PTR [Alloc]).BMI.bmiHeader.biPlanes, BM.bmPlanes ;; Must be set to 1
m2m (BITMAPINFOEX PTR [Alloc]).BMI.bmiHeader.biBitCount, BM.bmBitsPixel ;; Should be 32!
mov (BITMAPINFOEX PTR [Alloc]).BMI.bmiHeader.biCompression, BI_RGB
;; The remaining structure members are cleared in the call to HeapAlloc
; mov (BITMAPINFO PTR [Alloc]).BMI.bmiHeader.biSizeImage, NULL ;; Must be set to 0 for BI_RGB
; mov (BITMAPINFO PTR [Alloc]).BMI.bmiHeader.biXPelsPerMeter, NULL
; mov (BITMAPINFO PTR [Alloc]).BMI.bmiHeader.biYPelsPerMeter, NULL
; mov (BITMAPINFO PTR [Alloc]).BMI.bmiHeader.biClrUsed, NULL
; mov (BITMAPINFO PTR [Alloc]).BMI.bmiHeader.biClrImportant, NULL

;;mov (BITMAPINFO PTR [Alloc]).BMI.bmiColors, 0 ;; No need to clear the RGB colors array, done in call to HeapAlloc!

mov (BITMAPINFOEX PTR [Alloc]).hBM, eax

INVOKE SelectObject, tmpDC, (BITMAPINFOEX PTR [Alloc]).hBM
mov tmpBM, eax
INVOKE SelectObject, hDC, eax ;; eax is the return value from the above SelectObject
;; Because SelectObject returns the previously held object in the DC, we use the hDC as a temporary storage for
;; the old object in tmpDC, we need to remove hBM from hDC because hBM must not be in a DC when we call GetDIBits
;; And since we already have the handle to the bitmap in hDC, which is hBM, we don't need to store the return value
;; of the second call to SelectObject

INVOKE GetDIBits, tmpDC, hBM, 0, (BITMAPINFOEX PTR [Alloc]).BMI.bmiHeader.biHeight, (BITMAPINFOEX PTR [Alloc]).pDIB, ADDR (BITMAPINFOEX PTR [Alloc]).BMI, NULL ;; The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function.

INVOKE SelectObject, hDC, hBM ;; Seems to be failing as it returns a value of 0
INVOKE SelectObject, tmpDC, tmpBM

INVOKE DeleteDC, tmpDC

mov eax, Alloc
EnterGetPixel ENDP
Posted on 2004-03-06 14:12:28 by SubEvil
Raymond, do you think you could send me a code snipet of yours demonstrating your use of CreateDIBSection (and especially the other API's you mentioned!) I'm pretty sure you're talking about GetDIBits here, but I'd really appreciate to see someone else's implementation!

Posted on 2004-03-06 14:50:18 by SubEvil
Any code would depend on numerous factors such as:
- Has the bitmap been modified by the user or is it simply a static bitmap on file (most unclear from your original post).
- If it is after some modification by the user, what would be the extent of it and how is it controlled.
- What is the intent of checking the color of every pixel.

Answers to the above would probably raise other questions. Every program usually has a specific purpose which often guides on how it should be developed.

Posted on 2004-03-06 23:39:22 by Raymond
Hi Raymond, thanks in advance for your interest! The code I'm developing, is VERY similar to MOB's form reshaping code except that I use MASM HL syntax, but the algorithm is functionally the same. Using a bitmap mask (usually a mono bitmap), it determines the regions of a form by checking every pixel of your Mask bitmap. No data in the Mask bitmap needs to be changed, so I only need a fast GetPixel, because it's damn slow and it needs to check every pixel of the Mask bitmap for the transparency color (white) to determine the new shape of the form/window. Other functions of this process of changing the region of the window are also slower, but GetPixel is called so many times that it contributes greatly to the overall speed of the process.

My idea was, in regards to the above code sample, to "initialize" this bitmap, by copying the necessary BITMAPINFO header, and RGB color bits into a buffer (Check my BITMAPINFOEX Structure), making each call to GetPixel much faster. So instead of providing GetPixel with a DC, do everything for GetPixel, upto the point where it needs to check the specific color of a specific pixel, leaving that to each individual call to GetPixel.

I'd like to ask if my above program flow is correct: to initialize a new bitmap (CreateDIBSection), and copy the color data (32 bit longs with GetDIBits) into a buffer provided by CreateDIBSection. Is there a faster way of doing this? Can a person access the per pixel color data of a Bitmap directly from the DC? Also note, that I'm going to change from a Mono Mask Bitmap, to the bitmap I use to draw on the form, because after reshaping the form, I'll Bitblt a 24-bit color bitmap onto it. I'm going to use this bitmap as the mask bitmap as well, so I can pull RGB values directly from it, speading the call to GetPixel up.

So, in conclusion, all I need is a fast way to determine the shape of a new form, by checking the pixels on a form, and thereby changing the region data of that form.

Hope you understood my poor explinations.

Thanx again!
Posted on 2004-03-07 00:28:45 by SubEvil

I wonder why the gdi functions are so slow :P

Color conversion etc (taking gamma and other shit into account). GDI batching, transitions to kernel, etc. Anything using Get/PutPixel is doomed to be slow anyway, graphics routines (GDI or not) should work on large objects...
Posted on 2004-03-07 15:19:16 by f0dder

The following is not tested but you may want to consider it if your bitmap is dynamic. It is assumed that the original bitmap would have 32-bit colors so that you don't have to load a color table.

After you load the bitmap, use GetDIBits with the lpvBits member set to 0. This would fill a BITMAPINFO struc based on that bitmap.

Then use CreateDIBSection using the above BITMAPINFO struc. This will return the address of the bit values into a memory variable.

Use GetDIBits again but with the lpvBits member set to that address.
This should transfer the bitmap data to the DIB section. You can then create a compatible DC and select that DIB section into it.

The DIB section is then available for direct modification and direct retrieval of data without using the slow SetPixel or GetPixel functions. You only have to use the BitBlt function to transfer new data to the screen.

If that works, remember that the bit data would be bottom-up if the bitmap height is positive.

Hope this helps.

Posted on 2004-03-07 15:32:23 by Raymond