Reading single pixel color - Direct3D8 in Visual Basic 6

Started by
8 comments, last by The Melody Maker 11 years, 2 months ago

Hello. smile.png I did a search for a similar topic and couldn't find one, so I apologize in advance if one does indeed exist elsewhere.

I've been programming a game in Visual Basic 6 using the DirectX 8 Graphics interface, but I've run into a bit of trouble in my attempts to correctly read the color value from a single pixel from the front buffer that I've already locked with LockRect. Just so I'm not misunderstood, though, I'm not having trouble with the functionality. I've already successfully locked the front buffer, read from it, and unlocked it. My problem is figuring out the correct math for reading the appropriate color data from the pixels. (That being said, I hope I've started this thread in the right place and I apologize if I haven't.)

This is the helper function that I have going to make the job easier...


Function GrabPixel(XPos As Long, YPos As Long) As Long

Dim TempColor As Long

' LockedRect is the front buffer surface (D3DLOCKED_RECT) I've locked with Direct3DSurface.LockRect, and according to the docs it ought to be 32-bit automatically.
' X_RES& is the width of the game's current screen mode.
' Y_RES& is the height of the game's current screen mode.

With LockedRect
    TempColor = (.pBits + ((Y_RES& - 1 - YPos) * .Pitch) + (XPos * 4))
End With

' Anything else I need to do here to manipulate the color value I get before it's usable?

GrabPixel = TempColor

End Function

If anyone can figure out where I'm going wrong with my math, I'd greatly appreciate it. Thank you in advance to anyone who posts with help and advice. smile.png

Advertisement

This is difficult in VB6 (and VB in general) because the language doesn't have pointer dereferencing.

As a workaround, you can import the CopyMemory function from kernel32, and copy the data from the locked_rect to your own array of pixels.

The pBits field of the locked_rect structure represents the pointer (or local memory address) of the first pixel of the first scanline of the area you locked. The scanlines of the locked area are guaranteed to be adjacent in memory, but they may have padding in addition to the actual pixel data; this is why you have to consider the "pitch" to navigate between scanlines, not just width * numbytesperpixel.

The address of a given pixel on the locked area is given by the following formula:

address(x, y) = pBits + y * pitch + x * bytesPerPixel

The bytesPerPixel varies with the texture format, of course.

If you know that your pixels are 32-bit (byte per channel rgba), you can use CopyMemory once you have the address on the locked rectangle:

type rgbapixel

r as byte

g as byte

b as byte

a as byte

end type

dim destination as rgbapixel

CopyMemory(byval varptr(destination), byval address, 4) ' where 4 is the number of bytes to copy, and "address" is the address of the source pixel as described above

Niko Suni

Also, if you need many pixels, it is very much faster to copy them by scanline instead of one by one. In case you do scanline copies, your destination would be an array of the pixel structures and the correct first parameter (destination pointer) of the CopyMemory would be the first element of said array.

Note that CopyMemory has a nasty habit of crashing your VB6 app if the last parameter (number of bytes) is zero; be sure to safeguard your logic against that :)

Niko Suni

Thanks for replying, Nik02. :) I see now what I was doing wrong -- I was both reading the bitmap in memory upside-down and the resultant color backwards (assuming ABGR). XD

I tried the method you gave me (including using the CopyMemory sub from the Windows API) and now it's working perfectly; thank you so much for your help! ^__^

I would like to know why are you using directx 8 and VS 6

For win98 compatibility? :D

Niko Suni

why windows 98?

I don't know, just guessing smile.png

Sometimes it is fun toughening yourself by doing something the hard way instead of using these new fancy IDEs, OS and DirectX versions smile.png

Niko Suni

A fun, (distantly related) excercise is writing a Gouraud + multiple texture rasterizer in QBasic, and actually render some non-trivial mesh on that :)

Niko Suni

Backwards compatibility with earlier versions of Windows is only one my reasons I still use those legacy interfaces instead of upgrading to the newer ones. The Visual Basic 6 runtime, as well as the DirectX 7 and 8 interfaces which I've been using, do work with all the mainstream versions of Windows from 95 onward up to 8*. But I do have other reasons. I also use them because I still have them at my disposal, I've become comfortable with them after using them for 10+ years, and it's sufficient for what I need. That's enough reason for me to keep using them. At least at this time, I have no interest or need to upgrade. smile.png

* The two 2D-only games I've been making, both many years in the making and still under development, have been using DirectDraw 7 for graphical output but since finding out that Windows 8 handles DirectDraw operations very slowly, I've been making alternate Direct3D 8 versions of both which I'm happy to say so far actually do run at the right speed in Windows 8 upon testing. happy.png In the one game I'm currently working on upgrading, I had a routine set up to read the colors from the screen's middle column of pixels in DirectDraw, but Direct3D handles that so differently from DirectDraw that I had to come on here to ask for help. XD

This topic is closed to new replies.

Advertisement