在 cocos2d-x 中獲取紋理的畫素值
本文基於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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ccColor4B getColor4B(floatx,floaty) { ccColor4B color={0,0,0,0}; intix=(int)x-1; intiy=(int)y-1; m_pData+=(iy*getWidth()+ix)*4; color.r=*(m_pData++); color.g=*(m_pData++); color.b=*(m_pData++); color.a=*(m_pData++); returncolor; }; ccColor4F getColor4F(floatx,floaty) { returnccc4FFromccc4B(getColor4B(x,y)); }; |
2014-10-24更新:上面的程式碼沒有考慮越界問題,在傳遞的座標不在影象中時,程式會崩潰。
最新的程式碼改正了問題,請參考 github 。
由於 CCImage 是跨平臺實現的,所以放在標頭檔案中比放在實現檔案中要方便許多。否則,就需要在 CCImage 的若干個平臺相關實現中分別執行實現了。
下面是 quick-cocos2d-x 中的實現程式碼,我將其放在了 CCSpriteExtned.lua 框架中,這樣能讓所有的 CCSprite 例項都支援這個方法。
具體的實現請看程式碼,不解釋了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | --NOTE!!!The method isvery slowly!Please useit incarefully. --@param __pointAcoordinate forcolor. --@param __convertToNodeSpace Optional,defaultistrue,convertacoordinate tonode space from world space. --@param __isFloat Optional,defaultisfalse,convertacoordinate tonode space from world space. functionCCSpriteExtend:getColor(__point,__convertToNodeSpace,__isFloat) if__convertToNodeSpace ==nil then __convertToNodeSpace=true end if__convertToNodeSpace then __point=self:convertToNodeSpace(__point) end --CreateanewTexture toget the pixel datas. local __size=self:getContentSize() local __rt=CCRenderTexture:create(__size.width,__size.height) --Hold the old anchor andposition torestore it late on. local __oldAnchor=self:getAnchorPoint() local __oldPos=self:getPositionInCCPoint() --Move the sprite toleft bottom. self:align(display.LEFT_BOTTOM,0,0) --print("getColor:",__point.x,__point.y,__size.width,__size.height) --Render the sprite togetanewtexture. __rt:begin(); self:visit() __rt:endToLua(); --Restore the original anchor andposition. self:setAnchorPoint(__oldAnchor) self:setPosition(__oldPos) local __img=__rt:newCCImage(false) local __color=nil if__isFloat then __color=__img:getColor4F(__point.x,__point.y) else __color=__img:getColor4B(__point.x,__point.y) end return__color,__rt end --Only getaalpha value. functionCCSpriteExtend:getColorAlpha(__point,__convertToNodeSpace,__isFloat) local color=self:getColor(__point,__convertToNodeSpace,__isFloat) returncolor.a en |
2014-10-24更新:由於 newCCImage 方法在 C++ 中是請求堆記憶體並返回一個指標。因此必須手動釋放。上面的程式碼沒有考慮釋放問題,將會導致記憶體洩露。
最新的程式碼改正了問題,請參考 github 。
源:http://zengrong.net/post/2104.htm