cocos2d-x 源码剖析(15)

上节讲到了cocos2d-x在iOS上解压绝大数的图片格式为CCImage,然后再转化为纹理格式。其中有一个列外就是Webp格式。今天我们来看看Windows上面的情况,Android上的情况和Windows是类似的,都是使用第三方库来解码图片。按照道理来讲,Android上是可以采用类似iOS的技术,使用Android的系统库来解码图片,而且Android在4.0版本上已经开始原生支持Webp格式了。但是Android的系统库是Java的,如果要传到cocos2d-x中需要通过Jni调用,也就增加了不必要的麻烦。在早期cocos2d-x读取assets资源有严重的性能问题,本人采用了这个方法获得了巨大的性能提升。新的cocos2d-x读取assets的资源利用了缓存加速,性能和这种迂回的方式相差不多了。

好我们来看看windows平台下的CCImage文件,如果你是Mac系统,在xcode工程中是看不到这个文件的,但是你可以使用Sublime打开源码目录下面的文件自行查看。在Windows下面,CCImage的文件组织有点特别。除了platform下面的CCImage.h的头文件,和win32下面的CCImage.cpp文件以外,在platform下面还有两个额外的文件也就是CCImageCommon_cpp.h和CCImageCommonWebp.cpp文件。第一个文件也是源文件,命名为.h是可以通过条件编译导入到CCImage.cpp文件中,至于Webp文件要单独列出来,是因为iOS上也要用到这个文件。因为其他格式的图片解码和Webp的流程差不多,我们使用Webp作为讲解。还记得那个判断函数吗?在Windows下面他变成了这个样子:

bool CCImage::initWithImageData(void * pData, 
  int nDataLen, 
                                EImageFormat eFmt/* = eSrcFmtPng*/, 
                                int nWidth/* = 0*/,
                                int nHeight/* = 0*/,
                                int nBitsPerComponent/* = 8*/)
{
  bool bRet = false;
  do 
  {
    CC_BREAK_IF(! pData || nDataLen <= 0);
 
    if (kFmtPng == eFmt)
    {
      bRet = _initWithPngData(pData, nDataLen);
      break;
    }
    else if (kFmtJpg == eFmt)
    {
      bRet = _initWithJpgData(pData, nDataLen);
      break;
    }
    else if (kFmtTiff == eFmt)
    {
      bRet = _initWithTiffData(pData, nDataLen);
      break;
    }
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
    else if (kFmtWebp == eFmt)
    {
      bRet = _initWithWebpData(pData, nDataLen);
      break;
    }
#endif
    else if (kFmtRawData == eFmt)
    {
      bRet = _initWithRawData(pData, nDataLen, nWidth, nHeight, nBitsPerComponent, false);
      break;
    }
    else
    {
          // if it is a png file buffer.
      if (nDataLen > 8)
      {
        unsigned char* pHead = (unsigned char*)pData;
        if (   pHead[0] == 0x89
          && pHead[1] == 0x50
          && pHead[2] == 0x4E
          && pHead[3] == 0x47
          && pHead[4] == 0x0D
          && pHead[5] == 0x0A
          && pHead[6] == 0x1A
          && pHead[7] == 0x0A)
        {
          bRet = _initWithPngData(pData, nDataLen);
          break;
        }
      }
 
          // if it is a tiff file buffer.
      if (nDataLen > 2)
      {
        unsigned char* pHead = (unsigned char*)pData;
        if (  (pHead[0] == 0x49 && pHead[1] == 0x49)
          || (pHead[0] == 0x4d && pHead[1] == 0x4d)
          )
        {
          bRet = _initWithTiffData(pData, nDataLen);
          break;
        }
      }
 
          // if it is a jpeg file buffer.
      if (nDataLen > 2)
      {
        unsigned char* pHead = (unsigned char*)pData;
        if (   pHead[0] == 0xff
          && pHead[1] == 0xd8)
        {
          bRet = _initWithJpgData(pData, nDataLen);
          break;
        }
      }
    }
  } while (0);
  return bRet;
}


如果是Webp格式的文件就采用下面这个函数初始化了:

bool CCImage::_initWithWebpData(void*pData,intnDataLen)
{
  bool bRet = false;
  do
  {
    WebPDecoderConfig config;
    if(WebPInitDecoderConfig(&config)==0)break;
    if(WebPGetFeatures((uint8_t*)pData,nDataLen,&config.input)!=VP8_STATUS_OK)break;
    if(config.input.width==0||config.input.height==0)break;
 
    config.output.colorspace=MODE_RGBA;
    m_nBitsPerComponent=8;
    m_nWidth    = config.input.width;
    m_nHeight  = config.input.height;
    m_bHasAlpha = true;
 
    int bufferSize = m_nWidth * m_nHeight * 4;
    m_pData = new unsigned char[bufferSize];
 
    config.output.u.RGBA.rgba=(uint8_t*)m_pData;
    config.output.u.RGBA.stride=m_nWidth*4;
    config.output.u.RGBA.size=bufferSize;
    config.output.is_external_memory=1;
 
    if(WebPDecode((uint8_t*)pData,nDataLen,&config)!=VP8_STATUS_OK)
    {
      delete[]m_pData;
      m_pData=NULL;
      break;
    }
 
    bRet=true;
  }while(0);
  return bRet;
}


这个方式很简单,采用Google提供的Webp库解码图片。当然也要包含其头文件和lib。

#if defined(__native_client__) || defined(EMSCRIPTEN)
// TODO(sbc): I'm pretty sure all platforms should be including
// webph headers in this way.
#include "webp/decode.h"
#else
#include "decode.h"
#endif


其他格式由其第三方库的使用方法不同而不同,就不再多说了。相对而已Webp是很简洁的。这便是cocos2d-x中解码图片所采用的方法。还有一点值得注意的是,CCImage将其拷贝构造函数声明为了私有方法,也就是说你不能利用CCImage来赋值,毕竟里面是一大块内存,使用赋值或者添加到容器中都是很愚蠢的行为。

CCImage还有几个比较有趣的功能,他提供了一个saveToFile的函数。我们知道CCImage可以从相当多的格式初始化,那个这个saveToFile就可以作为一个转化函数来使用了。它能将图片保存为png或者jpg的格式,注意这个函数默认是不保存透明的,如果需要可以将其第二个参数设置为true。你可以采用压缩的图片格式打包,然后在游戏第一次运行的时候将其保存为易于解码的格式,来达到游戏发布体积和加载速度上的平衡。CCImage就能简单的完成这个操作。

CCImage还有一个功能就是,它能从一个string初始化一个图片。另外IOS和Android还提供了一个initWithStringShadowStroke的功能。如果你要显示很有艺术感的字体,不妨用用这个函数。字体的渲染向来都是很恶心的内容,在Windows和Android上cocos2d-x采用了一个叫做BitmapDC的概念,再调用各自的平台特性来完成这个工作。在Android上是采用jni的方式调用系统的函数实现的。在iOS上直接采用了系统函数,没有借用BitmapDC这个概念。有一个问题就是,如果你在Android上开启了Debug模式,那么会输出相当多的JVM释放资源的信息,这是因为每一次信息改动都生成了一张显示图片,造成JVM频繁回收。这极大的影响了游戏性能。所以TTF文件使用在Android上是极其低效的,如果你要做聊天,这方面有很大的优化余地。

你可能感兴趣的:(cocos2d-x,学习笔记)