WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

cocos2d-xでピクセル単位の描画をしよう

今回のエンジニアブログを担当する大原です。
演出効果として、ピクセル単位の処理を行いたいことがよくあります。
cocos2d-xでピクセル単位の情報を保持の仕方の実現方法について、書きたいと思います。

今回は、ピクセル単位の情報をもとに、市松模様(チェック柄)で画像を描画します。
まずは、初期化処理です。

bool HelloWorld::init()  
{  
  
  if ( !CCLayer::init() )  
  {  
    return false;  
  }  
  
  //画像の読み込み  
  CCImage img;  
  img.initWithImageFile("HelloWorld.png");  
  
  //総ピクセル数(unsigned int)  
  m_pixcelSize = img.getDataLen();  
  
  //横ピクセル数(unsigned int)  
  m_pixcelW = img.getWidth();  
  
  //縦ピクセル数(unsigned int)  
  m_pixcelH = img.getHeight();  
  
  //ピクセルデータ(配列)  
  unsigned char * data = img.getData();  

CCImageからピクセルの各情報を取り出すことができます。
ピクセル単位の情報を取り出すにはCCImageのgetData()を使います。
返り値がunsigned char*型となり、画像のピクセル情報を配列として返してくれます。

通常のpngファイルが読み込まれる場合、1ピクセルは32ビット(4バイト)の情報で表現されます。
unsigned charは8ビット(1バイト)のため、1ピクセルの情報を取り出すには4個単位で取り出さなければいけません。
1バイト単位で色情報がきまっており、赤、緑、青、透明度となっています。

その情報を格納する型として、cocos2d-xには色情報を扱う構造体、ccColor4Bがあります。
1ピクセル単位の情報としては、調度良いため、画像サイズ分の配列として保持します。

以下にバイト単位の情報から配列に置き換える処理を記載します。

  //カラー情報の配列(ccColor4B **)に入れ替え  
  m_pTexturePixcels = new ccColor4B*[m_pixcelW];  
  for(unsigned int indexW = 0; m_pixcelW >indexW ; indexW++)  
  {  
    m_pTexturePixcels[indexW] = new ccColor4B[m_pixcelH];  
    for(unsigned int indexH = 0; m_pixcelH > indexH ; indexH++)  
    {  
      //4バイトデータをcocosのカラー構造体に変換  
      //1ピクセルの情報を取り出す  
      m_pTexturePixcels[indexW][indexH].r = data[(indexH * m_pixcelW * 4 + indexW * 4 ) + 0];//赤  
      m_pTexturePixcels[indexW][indexH].g = data[(indexH * m_pixcelW * 4 + indexW * 4 ) + 1];//緑  
      m_pTexturePixcels[indexW][indexH].b = data[(indexH * m_pixcelW * 4 + indexW * 4 ) + 2];//青  
      m_pTexturePixcels[indexW][indexH].a = data[(indexH * m_pixcelW * 4 + indexW * 4 ) + 3];//透明度  
  
    }  
  }  
  
  //RGBA値のログ出力  
  for(unsigned int dotH= 0;dotH < m_pixcelH; dotH++)  
  {  
    for(unsigned int dotW = 0; dotW < m_pixcelW ; dotW++)  
    {  
      CCLOG("COLOR %X",m_pTexturePixcels[dotW][dotH]);  
    }  
  }  

これで情報の格納ができました。

後はこの情報をもとに好きなように描画処理を書いてみるだけです。
以下に市松模様の描画処理を記載します。

  //画面サイズの取得  
  CCSize size = CCDirector::sharedDirector()->getWinSize();  
  CCLayerColor *pDot = CCLayerColor::create();  
  
  // CCRenderTexture生成  
  CCRenderTexture* pRenderTexture = CCRenderTexture::create(m_pixcelW * 2, m_pixcelH * 2);  
  pRenderTexture->setPosition(ccp(size.width/2, size.height/2));  
  this->addChild(pRenderTexture);  
  
  //=========================  
  //ドット単位の判定  
  //=========================  
  // オフスクリーンレンダリング  
  pRenderTexture->beginWithClear(0,0,0,0);  
  
  for(unsigned int dotH= 0;dotH < m_pixcelH; dotH++)  
  {  
    for(unsigned int dotW = 0; dotW < m_pixcelW ; dotW++)  
    {  
      unsigned int dot = 4; //何ドット単位で  
      unsigned int x = dotW / dot, y = dotH / dot;  
      if( x % 2 == y % 2)  
      {  
        //Xが偶数でYが偶数の場合  
        //Xが奇数でYが奇数の場合  
        continue;  
      }  
      //ドットをわかりやすく2*2ドット単位に  
      pDot->initWithColor(m_pTexturePixcels[dotW][dotH], 2, 2);  
  
      //反転するので、逆転配置  
      pDot->setPosition(ccp(dotW * 2,( m_pixcelH - dotH ) *2));  
      pDot->visit();  
    }  
  }  
  pRenderTexture->end();  
  
  return true;  
}  
  

これを実行すると以下の様な画像が描画できると思います。

001

この処理は重い処理になりますので、リアルタイムで描画させるには、パフォーマンスを改善する工夫が必要です。
静止画の加工には使えそうです。