How could i get the alpha value of a pixel in a texture?

I want to handle the touch event on a sprite and ignore it when i touch the alpha area of the sprite’s texture.
Could somebody tell me how can i get the alpha value of a pixel in a CCTexture2D? or another way to achieve which i mentioned before?

i have the same question, waiting

me too, any idea?

try it
bool CGameObject::hitTest(cocos2d::CCPoint& inTouchPoint) {

if (this~~>mBoolMap NULL)
{
return true;
}

CCPoint touchPointOnObject = CCNode::convertToNodeSpace(inTouchPoint);

int row = touchPointOnObject.y;
int col = touchPointOnObject.x;

if (row \< 0 || row \>= this-\>getContentSize().height
    ||
    col \< 0 || col \>= this-\>getContentSize().width)

return false;

return mBoolMap[row][col];

}

void CGameObject::makeAlphaBoolMap() {

CCAssert( mBoolMap  NULL, “”);

int renderTextureHeight, renderTextureWidth;
renderTextureHeight = this~~>getContentSizeInPixels().height;
renderTextureWidth = this~~>getContentSizeInPixels.width;
if || )
{
return;
}
CCPoint oldAnchor = this~~>getAnchorPoint();
CCPoint oldPosition = this~~>getPosition;
bool oldVisibility = this~~>m_bIsVisible;

this~~>setAnchorPoint);
this~~>setPosition(CCPoint(0.f, 0.f));
this~~>setIsVisible;
GLubyte* buffer = new GLubyte[renderTextureWidth * renderTextureHeight * 4];
CCRenderTexture *texture = new CCRenderTexture;
texture~~>initWithWidthAndHeight(renderTextureWidth, renderTextureHeight, kCCTexture2DPixelFormat_RGBA8888);
texture~~>beginWithClear;
this~~>visit();
glReadPixels(0, 0, renderTextureWidth, renderTextureHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
texture~~>end;
texture~~>release();

int charNumber = 0;
int bitNumber = 0;

bool *bigMatrix = new bool [renderTextureHeight];
for (int r = 0; r < renderTextureHeight; r**) {
bigMatrix[r] = new bool[renderTextureWidth]; // <
for {
bigMatrix[r] = 0;
}
}
for {
for
{
if
{
bitNumber = 0;
**charNumber;
}

if (buffer[i \* renderTextureWidth \* 4 + j \* 4 + 3] > 64)
{
bigMatrix[i][j] = 1;
}

++bitNumber;
}
}

int rows = this~~>getContentSize.height;
int cols = this~~>getContentSize().width;

mBoolMap = bigMatrix;

for (int r = 0; r < renderTextureHeight; r++) {
delete[] bigMatrix[r];
}
delete[] bigMatrix;
}

delete []buffer;

}

Thank you. it helped me a lot.

Dmytro Taradayka wrote:

try it
bool CGameObject::hitTest(cocos2d::CCPoint& inTouchPoint) {
>
if (this~~>mBoolMap NULL)
{
return true;
}

CCPoint touchPointOnObject = CCNode::convertToNodeSpace(inTouchPoint);
    
int row = touchPointOnObject.y;
int col = touchPointOnObject.x;

if (row \< 0 || row \>= this-\>getContentSize().height
    ||
    col \< 0 || col \>= this-\>getContentSize().width)
    
return false;

return mBoolMap[row][col];

}

void CGameObject::makeAlphaBoolMap() {

CCAssert( mBoolMap  NULL, “”);

>

int renderTextureHeight, renderTextureWidth;
>
renderTextureHeight = this~~>getContentSizeInPixels().height;
renderTextureWidth = this~~>getContentSizeInPixels.width;
>
if || )
{
return;
}
>
CCPoint oldAnchor = this~~>getAnchorPoint();
CCPoint oldPosition = this~~>getPosition;
bool oldVisibility = this~~>m_bIsVisible;
>
this~~>setAnchorPoint);
this~~>setPosition(CCPoint(0.f, 0.f));
this~~>setIsVisible;
>
GLubyte* buffer = new GLubyte[renderTextureWidth * renderTextureHeight * 4];
>
CCRenderTexture *texture = new CCRenderTexture;
>
texture~~>initWithWidthAndHeight(renderTextureWidth, renderTextureHeight, kCCTexture2DPixelFormat_RGBA8888);
texture~~>beginWithClear;
this~~>visit();
glReadPixels(0, 0, renderTextureWidth, renderTextureHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
texture~~>end;
texture~~>release();
>
int charNumber = 0;
int bitNumber = 0;
>
bool *bigMatrix = new bool [renderTextureHeight];
for (int r = 0; r < renderTextureHeight; r**) {
bigMatrix[r] = new bool[renderTextureWidth]; // <
for {
bigMatrix[r] = 0;
}
}
>
for {
for
{
if
{
bitNumber = 0;
>**charNumber;
}
>
if (buffer[i \* renderTextureWidth \* 4 + j \* 4 + 3] > 64)
{
bigMatrix[i][j] = 1;
}
>
++bitNumber;
}
}
>
int rows = this~~>getContentSize.height;
int cols = this~~>getContentSize().width;
>
>
>
mBoolMap = bigMatrix;
>
>
for (int r = 0; r < renderTextureHeight; r++) {
delete[] bigMatrix[r];
}
delete[] bigMatrix;
}
>
>
delete []buffer;
>
>
}

A word of caution here.

>> glReadPixels(0, 0, renderTextureWidth, renderTextureHeight, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

This call is unpredictable across GPUs. While it will give you your texture data, it can take a very long time to get the data, a very short amount of time, sometimes longer than shorter, and vice versa, on the same GPU hardware.

You should preprocess your sprite texture with a bitmask and use THAT to determine touch hit/miss.

This is class that can check it without glReadPixels, it uses CCPointer class: https://github.com/ivzave/cocos2dx-ext/blob/master/CCPointer.h

class NonTransparentTapChecker : public CCObject
{
public:
    CREATE_FUNC(NonTransparentTapChecker);

    bool tapsOnNonTransparent(const CCPoint &tap)
    {
        if (!m_imageRect.containsPoint(tap))
            return false;
        unsigned x = unsigned(tap.x) % m_width;
        /// Don't forget to invert y coordinate.
        unsigned y = unsigned(m_height - tap.y) % m_height;
        unsigned index = x + y * m_width;
        unsigned dataLen = m_image->getDataLen();
        CCAssert(index < dataLen, "index is bigger than image size.");
        unsigned char *pixel = m_image->getData() + (4 * index);
        return !isZeroPixel(pixel);
    }

private:
    bool isZeroPixel(unsigned char *pixel)
    {
        return 0 == pixel[0] && 0 == pixel[1] && 0 == pixel[2] && 0 == pixel[3];
    }

    bool init()
    {
        m_image = new CCImage();
        m_image->initWithImageFile("semitransparent_image.png");
        m_image->release();

        m_width = m_image->getWidth();
        m_height = m_image->getHeight();
        m_imageRect.size.width = m_width;
        m_imageRect.size.height = m_height;
        return true;
    }

    CCPointer m_image;
    CCRect m_imageRect;
    unsigned m_width;
    unsigned m_height;
};

Usage:

// Sprite uses the same image with NonTransparentTapChecker
// We should translate touch coordinates to sprite node space first.
CCPoint mapTapPoint = sprite->convertTouchToNodeSpace(pTouch);
// Check that user hadn't tapped on transparent area.
bool goal = tapChecker->tapsOnNonTransparent(mapTapPoint);

@Sergey Shambir
I am using the above class that you mentioned. But there is an error when I use CCPointer. It says there is no such class. Could you please tell me where to get the class file and where should that class file be pasted?
I am a beginner therefore detailed steps would be really appreciated.

thank you.

Amyn Virani wrote:

@Sergey Shambir
I am using the above class that you mentioned. But there is an error when I use CCPointer. It says there is no such class. Could you please tell me where to get the class file and where should that class file be pasted?
I am a beginner therefore detailed steps would be really appreciated.
>
thank you.

It located here: https://github.com/ivzave/cocos2dx-ext/blob/master/CCPointer.h

Just create yet one header, copy CCPointer.h content into it and include it in your file.

Sergey Shambir wrote:

Amyn Virani wrote:
> @Sergey Shambir
> I am using the above class that you mentioned. But there is an error when I use CCPointer. It says there is no such class. Could you please tell me where to get the class file and where should that class file be pasted?
> I am a beginner therefore detailed steps would be really appreciated.
>
> thank you.
>
It located here: https://github.com/ivzave/cocos2dx-ext/blob/master/CCPointer.h
>
Just create yet one header, copy CCPointer.h content into it and include it in your file.

Ok so the error is gone. Just one more thing. You wrote this in your usage part of the code:

// Check that user hadn’t tapped on transparent area.
bool goal = tapChecker->tapsOnNonTransparent(mapTapPoint);

Can you tell what is the type of the variable tapChecker? and where to initialize it and how?

@Sergey Shambir

I followed the above instructions and touch it working perfectly in case of static images.
I have mixture of static images and animations created by Cocosbuilder.
Is there a way to use above class for cocos-builder animations ?

Thanks

@quiet_readonly Thanks for the code! There are some issues though, so I took the liberty of improving on it:

const bool isZeroPixel( const unsigned char* pixel )
{
	return 0 == pixel[0] && 0 == pixel[1] && 0 == pixel[2] && 0 == pixel[3];
}

const bool tapsOnNonTransparent( const cocos2d::Point& tap )
{
	Image* imgPtr = new cocos2d::Image();
	imgPtr->initWithImageFile( "yourSpriteFile.png" );
        //imgPtr->Release(); this will crash!

	const int width = imgPtr ->getWidth();
	const int height = imgPtr ->getHeight();

	unsigned x = unsigned( tap.x ) % width;
	/// Don't forget to invert y coordinate.
	unsigned y = unsigned( height - tap.y ) % height;
	unsigned index = x + y * width;
	unsigned dataLen = imgPtr ->getDataLen();
	CCAssert( index < dataLen, "index is bigger than image size." );
	unsigned char* pixel = imgPtr ->getData() + (4 * index);
	return !isZeroPixel( pixel );
}

Usage:

Point p spriteYouWantToCheck->convertTouchToNodeSpace( touch );
tapsOnNonTransparent( p );

Remarks:

  • There is no need of the CCPointer class anymore since cocos2D-X has the Ref type and Image (CCImage in @quiet_readonly code) inherits from it.

  • imgPtr->Release() right after creating imgPtr will cause a crash since the code aliases access to imgPtr when creating the pixel buffer to pass to isZeroPixel().

  • To improve performance it’s better to create a bool** at start up and check against that instead of this (if your sprite changes texture at runtime though you got a problem)

1 Like

Not working. always returns false