在 cocos2d-x 中获取纹理的像素值
How to get a pixel in cocos2d-x?
2014-10-24更新: 解决内存泄露以及越界问题。由于 quick 目前的版本分支混乱,这些修改不会提交到 quick 了。请自行参照下面的方法修改。
本文基于cocos2d-x 2.2.3
项目需要一个功能,就是在点击某个不规则边缘图片的时候,不响应图片的透明部分。
以前在 AS3 中处理类似需求的时候,就是获取点击点的像素值,得到 Alpha 的值,然后根据 Alpha 的值来判断是否需要响应。
但在 cocos2d-x 中,有一些问题。
cocos2d-x 的渲染流程是这样的:
- 载入一张图片,将它解析成 CCImage 对象;
- 根据 CCImage 对象,建立 CCTexture2D 对象;
- 在 CCTexturte2D 对象中,将 CCImage 对象中的定点和像素信息上传到显卡缓存;
- 删除 CCImage 对象。
从上面的流程可以看出,其实我们在看到这张显示在 cocos2d-x 中的图片时,图像的像素信息已经不存在 CCTexture2D 中了。
那么,怎么得到这些像素值呢?
一、预先存储的方法
下面两篇文章采用了预先存储的方法:
这种方法的原理就是修改 CCTexture2D ,使其在处理 CCImage 的时候将需要的像素信息缓存下来。
这种方法的弊端如下:
- 会占用内存来保存像素信息,如果保存所有的像素信息,则占用的内存还相当大;
- 需要修改 CCTexture2D。
二、使用 CCTexture2DMutable
下面两篇文章中都提到了这个类:
这个类继承了 CCTextuer2D,并在内部实现了对像素信息的缓存。如果要实现绘图等功能,这个类倒是挺有用的。
另外,在 cocos2d-x 中的 extensions/CCArmature
扩展中也带有这个类。
这种方法的弊端如下:
- 与 CCTextureCache 和 CCSprite 等常用类不兼容;
- 占用内存保存像素信息。
三、重绘图片取得像素
我采用的是这种办法。流程如下:
- 在需要图片像素值的时候,将这张图片使用 FrameBuffer 重新绘制成像素;
- 获得相关像素的颜色值;
- 删除已经获得的像素。
这种方法的弊端如下:
- 如果要取得的像素图片巨大,可能对性能有影响;
- 每次的数据没有缓存,频繁执行的话性能消耗巨大。
当然,如果确实需要在同一张图片上多次操作,缓存可以程序员自己来做。
为了实现这个流程,我修改了 CCImage.h,增加了两个方法 getColor4B
和 getColor4F
:
1ccColor4B getColor4B(float x, float y)
2{
3 ccColor4B color = { 0, 0, 0, 0 };
4 int ix = (int)x - 1;
5 int iy = (int)y - 1;
6 m_pData += (iy*getWidth() + ix) * 4;
7 color.r = *(m_pData++);
8 color.g = *(m_pData++);
9 color.b = *(m_pData++);
10 color.a = *(m_pData++);
11 return color;
12};
13
14ccColor4F getColor4F(float x, float y)
15{
16 return ccc4FFromccc4B(getColor4B(x, y));
17};
2014-10-24更新:上面的代码没有考虑越界问题,在传递的坐标不在图像中时,程序会崩溃。
最新的代码改正了问题,请参考 github 。
由于 CCImage 是跨平台实现的,所以放在头文件中比放在实现文件中要方便许多。否则,就需要在 CCImage 的若干个平台相关实现中分别执行实现了。
下面是 quick-cocos2d-x 中的实现代码,我将其放在了 CCSpriteExtned.lua 框架中,这样能让所有的 CCSprite 实例都支持这个方法。
具体的实现请看代码,不解释了。
1-- NOTE!!! The method is very slowly! Please use it in carefully.
2-- @param __point A coordinate for color.
3-- @param __convertToNodeSpace Optional, default is true, convert a coordinate to node space from world space.
4-- @param __isFloat Optional, default is false, convert a coordinate to node space from world space.
5function CCSpriteExtend:getColor(__point, __convertToNodeSpace, __isFloat)
6 if __convertToNodeSpace == nil then
7 __convertToNodeSpace = true
8 end
9 if __convertToNodeSpace then
10 __point = self:convertToNodeSpace(__point)
11 end
12 -- Create a new Texture to get the pixel datas.
13 local __size = self:getContentSize()
14 local __rt = CCRenderTexture:create(__size.width, __size.height)
15 -- Hold the old anchor and position to restore it late on.
16 local __oldAnchor = self:getAnchorPoint()
17 local __oldPos = self:getPositionInCCPoint()
18 -- Move the sprite to left bottom.
19 self:align(display.LEFT_BOTTOM, 0,0)
20 --print("getColor:", __point.x, __point.y, __size.width, __size.height)
21 -- Render the sprite to get a new texture.
22 __rt:begin();
23 self:visit()
24 __rt:endToLua();
25 -- Restore the original anchor and position.
26 self:setAnchorPoint(__oldAnchor)
27 self:setPosition(__oldPos)
28 local __img = __rt:newCCImage(false)
29 local __color = nil
30 if __isFloat then
31 __color = __img:getColor4F(__point.x, __point.y)
32 else
33 __color = __img:getColor4B(__point.x, __point.y)
34 end
35 return __color, __rt
36end
37
38-- Only get a alpha value.
39function CCSpriteExtend:getColorAlpha(__point, __convertToNodeSpace, __isFloat)
40 local color = self:getColor(__point, __convertToNodeSpace, __isFloat)
41 return color.a
42en
这个方法已经合并进入 quick-cocos2d-x 的 develop 分支。
2014-10-24更新:由于 newCCImage 方法在 C++ 中是请求堆内存并返回一个指针。因此必须手动释放。上面的代码没有考虑释放问题,将会导致内存泄露。
最新的代码改正了问题,请参考 github 。
如有问题,请留言。
- 文章ID:2104
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/how_to_get_a_pixel_in_cocos2d-x/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。