Jump to content
  • Advertisement
Sign in to follow this  
craziecoder

Reading pixel data from a texture

This topic is 3802 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi All, I am using Directx 9. I want to read RGBA value from a given texture. Am using D3DXCreateTextureFromFileEx to load a texture. The texture is stored in LPDIRECT3DTEXTURE9. What I want is, If I pass X & Y values, I should get RGBA value at that point. How do I go about it. Thanks In advance. Altaf

Share this post


Link to post
Share on other sites
Advertisement
Use IDirect3DTexture9::LockRect to lock the surface. If you're reading pixels very infrequently (Less often than once or twice per frame), you could just lock a 1x1 rect, otherwise lock the entire surface and use the pitch to work out the X,Y coordinate.

Assuming your texture is 32-bit (If it's not, you'll have to change the code to cope with that):

DWORD GetPixelColour(const D3DLOCKED_RECT& rect, DWORD x, DWORD y)
{
const DWORD* pBits = (const DWORD*)rect.pBits;
return pBits[y*rect.Pitch + x];
}

Share this post


Link to post
Share on other sites
Hi Steve,

This is how I implemented it...
DWORD GetPixelColour( DWORD x, DWORD y )
{
D3DLOCKED_RECT rect;
m_texture->LockRect(0, &rect, NULL, D3DLOCK_READONLY );
const DWORD* pBits = (const DWORD*)rect.pBits;
m_texture->UnlockRect(0);

return pBits[y*rect.Pitch + x];
}

returned value is stored in a DWORD.
To convert this value into ARGB format I use the following code,

int iRed = hex & 0xFF;
int iGreen = ( hex / 0x100 ) & 0xFF;
int iBlue = ( hex / 0x10000 ) & 0xFF;
int iAlpha = ( hex / 0x1000000 ) & 0xFF;

Am I doing any thing wrong here. because am not getting proper values.


But am not getting proper values.

Share this post


Link to post
Share on other sites
As soon as you unlock the texture, the locked data is no longer valid (It might "just work", but might not). So you should grab the pixel, unlock the surface, then return the pixel.

The pixel format depends on the D3DFMT_ type you passed to D3DXCreateTextureFromFileEx)(. It needs to be D3DFMT_A8B8G8R8 to work with your code. I believe D3DFMT_A8R8G8B8 is more common, in which case your code would be:

DWORD GetPixelColour( DWORD x, DWORD y )
{
D3DLOCKED_RECT rect;
HRESULT hResult = m_texture->LockRect(0, &rect, NULL, D3DLOCK_READONLY );
if(FAILED(hResult))
return 0; // Failed to lock the texture for some reason, return error

const DWORD* pBits = (const DWORD*)rect.pBits;
DWORD dwPixel = pBits[y*rect.Pitch + x];
m_texture->UnlockRect(0);

return dwPixel;
}

// And then:
int iAlpha = (hex >> 24) & 0xFF;
int iRed = (hex >> 16) & 0xFF;
int iGreen = (hex >> 8) & 0xFF;
int iBlue = (hex >> 0) & 0xFF;



A few points:
  • I changed divisions to bit shifts to make things a little easier to read (IMO).
  • I added error checking (Which is extremely important here, if LockRect() fails and you don't notice, you'll probably crash your app).
  • Locking a texture can be potentially expensive, so it'd be preferable to lock the texture, read several pixels, then unlock it if you're going to be processing more than one or two pixels per frame.
  • If you're just loading the texture to read pixels, and not actually rendering it, loading it into D3DPOOL_SCRATCH or D3DPOOL_SYSTEMMEM would probably be faster.
  • Unrelated - you should be using the Debug Runtimes if you're not already, which will give you useful error messages if anything goes wrong (Like Lockrect() failing).

    Share this post


    Link to post
    Share on other sites
    I have been having a similar problem and I do not see what is wrong with what I am doing. Here is how I load the texture and how I try and grab the data. The probably is that I do not recieve the correct alpha/red/green/blue values that I should.


    D3DXCreateTextureFromFileEx(lpD3Device, "Test.bmp", 0, 0, D3DX_DEFAULT, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, 0, 0, &m_tTestToRead);

    for(unsigned int x = 0; x < width; ++x)
    {
    for(unsigned int y = 0; y < height; ++y)
    {
    m_tTestToRead->LockRect(0, &tempLocked, NULL, D3DLOCK_READONLY);

    int bytesPerPixel = tempLocked.Pitch / width;
    int index = (x*tempLocked.Pitch) + y;

    m_tTestToRead->UnlockRect(0);

    BYTE* pColor = (BYTE*)tempLocked.pBits;

    float alpha = pColor[index];
    float red = pColor[index + 1];
    float green = pColor[index + 2];
    float blue = pColor[index + 3];
    }
    }

    Share this post


    Link to post
    Share on other sites
    Quote:
    Original post by LeonPost
    I have been having a similar problem and I do not see what is wrong with what I am doing. Here is how I load the texture and how I try and grab the data. The probably is that I do not recieve the correct alpha/red/green/blue values that I should.


    D3DXCreateTextureFromFileEx(lpD3Device, "Test.bmp", 0, 0, D3DX_DEFAULT, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0, 0, 0, &m_tTestToRead);

    for(unsigned int x = 0; x < width; ++x)
    {
    for(unsigned int y = 0; y < height; ++y)
    {
    m_tTestToRead->LockRect(0, &tempLocked, NULL, D3DLOCK_READONLY);

    int bytesPerPixel = tempLocked.Pitch / width;
    int index = (x*tempLocked.Pitch) + y;

    m_tTestToRead->UnlockRect(0);

    BYTE* pColor = (BYTE*)tempLocked.pBits;

    float alpha = pColor[index];
    float red = pColor[index + 1];
    float green = pColor[index + 2];
    float blue = pColor[index + 3];
    }
    }


    It's the exact same problem as others have already discussed above. You're trying to access the data (tempLocked.pBits) AFTER you unlock the surface. All data access should occur inside a lock/unlock pair.

    Share this post


    Link to post
    Share on other sites
    And X and Y are the wrong way around here:
    int index = (x*tempLocked.Pitch) + y;
    It should be:
    int index = (y*tempLocked.Pitch) + x*4;
    (*4 because you have 4 bytes per pixel)

    EDIT: And you realise that'll give you values of 0..255 for alpha, red, green and blue? You'll want to do something like:
    float alpha = (float)pColor[index] / 255.0f;
    to give you values of 0..1.

    EDIT#2: And you'll definitely want to do the Lock/Unlock call outside the loop, putting it inside the loop like that could have severe performance problems.

    Share this post


    Link to post
    Share on other sites
    I am still having the same exact problem, even with those fixes.


    for(unsigned int x = 0; x < width; ++x)
    {
    for(unsigned int y = 0; y < height; ++y)
    {
    m_tTestToRead->LockRect(0, &tempLocked, NULL, D3DLOCK_READONLY);

    int bytesPerPixel = tempLocked.Pitch / width;
    int index = (y*tempLocked.Pitch) + x*4;
    BYTE* pColor = (BYTE*)tempLocked.pBits;


    m_tTestToRead->UnlockRect(0);


    float alpha = (float)pColor[index] / 255.0f;
    float red = (float)pColor[index + 1] / 255.0f;
    float green = (float)pColor[index + 2] / 255.0f;
    float blue = (float)pColor[index + 3] / 255.0f;
    }
    }


    Is what I am currently using. Was there anything wrong with the way I was loading my texture?

    Share this post


    Link to post
    Share on other sites
    You're still using the locked data after you've unlocked the texture. You need to copy the data out and then unlock the texture:

    m_tTestToRead->LockRect(0, &tempLocked, NULL, D3DLOCK_READONLY);

    for(unsigned int x = 0; x < width; ++x)
    {
    for(unsigned int y = 0; y < height; ++y)
    {
    int index = (y*tempLocked.Pitch) + x*4;
    BYTE* pColor = (BYTE*)tempLocked.pBits;

    float alpha = (float)pColor[index] / 255.0f;
    float red = (float)pColor[index + 1] / 255.0f;
    float green = (float)pColor[index + 2] / 255.0f;
    float blue = (float)pColor[index + 3] / 255.0f;
    }
    }

    m_tTestToRead->UnlockRect(0);

    I've also moved the LockRect() out of the loop. Also, this line doesn't do what you think it does:
    int bytesPerPixel = tempLocked.Pitch / width;
    A 32-bit texture that's 129 pixels wide could well have a pitch of 1024 bytes, which would give you a bytesperpixel value of 7. The pitch is the distance in bytes between scanlines. Some drivers keep information at the end of a scanline of pixels, and trampling over it will in the best case not work, and in the worst case cause graphical corruption or crash the application.

    Share this post


    Link to post
    Share on other sites
    Sign in to follow this  

    • Advertisement
    ×

    Important Information

    By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

    We are the game development community.

    Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

    Sign me up!