1. 程式人生 > >在 cocos2d-x 中獲取紋理的畫素值

在 cocos2d-x 中獲取紋理的畫素值

本文基於cocos2d-x 2.2.3

專案需要一個功能,就是在點選某個不規則邊緣圖片的時候,不響應圖片的透明部分。

以前在 AS3 中處理類似需求的時候,就是獲取點選點的畫素值,得到 Alpha 的值,然後根據 Alpha 的值來判斷是否需要響應。

但在 cocos2d-x 中,有一些問題。

cocos2d-x 的渲染流程是這樣的:

  1. 載入一張圖片,將它解析成 CCImage 物件;
  2. 根據 CCImage 物件,建立 CCTexture2D 物件;
  3. 在 CCTexturte2D 物件中,將 CCImage 物件中的定點和畫素資訊上傳到顯示卡快取;
  4. 刪除 CCImage 物件。

從上面的流程可以看出,其實我們在看到這張顯示在 cocos2d-x 中的圖片時,影象的畫素資訊已經不存在 CCTexture2D 中了。

那麼,怎麼得到這些畫素值呢?

一、預先儲存的方法

下面兩篇文章採用了預先儲存的方法:

這種方法的原理就是修改 CCTexture2D ,使其在處理 CCImage 的時候將需要的畫素資訊快取下來。

這種方法的弊端如下:

  1. 會佔用記憶體來儲存畫素資訊,如果儲存所有的畫素資訊,則佔用的記憶體還相當大;
  2. 需要修改 CCTexture2D。

二、使用 CCTexture2DMutable

下面兩篇文章中都提到了這個類:

這個類繼承了 CCTextuer2D,並在內部實現了對畫素資訊的快取。如果要實現繪圖等功能,這個類倒是挺有用的。

另外,在 cocos2d-x 中的 extensions/CCArmature

擴充套件中也帶有這個類。

這種方法的弊端如下:

  1. 與 CCTextureCache 和 CCSprite 等常用類不相容;
  2. 佔用記憶體儲存畫素資訊。

三、重繪圖片取得畫素

我採用的是這種辦法。流程如下:

  1. 在需要圖片畫素值的時候,將這張圖片使用 FrameBuffer 重新繪製成畫素;
  2. 獲得相關畫素的顏色值;
  3. 刪除已經獲得的畫素。

這種方法的弊端如下:

  1. 如果要取得的畫素圖片巨大,可能對效能有影響;
  2. 每次的資料沒有快取,頻繁執行的話效能消耗巨大。

當然,如果確實需要在同一張圖片上多次操作,快取可以程式設計師自己來做。

為了實現這個流程,我修改了 CCImage.h,增加了兩個方法 getColor4BgetColor4F

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