cocos2d-x 源码剖析(16)

上一节讲了CCImage,这节讲CCTexture。毕竟我们就是从CCTexture讲岔到CCImage的。CCTexture负责纹理的加载和维护。他的函数分为三部分:

  • 初始化函数
  • 加载函数
  • 管理函数

我们先来看加载函数,这是CCTexture的根本所在:

bool CCTexture2D::initWithData(const void *data, CCTexture2DPixelFormat pixelFormat,
 unsigned int pixelsWide, unsigned int pixelsHigh, const CCSize& contentSize)
{
  unsigned int bitsPerPixel;
    //Hack: bitsPerPixelForFormat returns wrong number for RGB_888 textures. See function.
  if(pixelFormat == kCCTexture2DPixelFormat_RGB888)
  {
    bitsPerPixel = 24;
  }
  else
  {
    bitsPerPixel = bitsPerPixelForFormat(pixelFormat);
  }
 
  unsigned int bytesPerRow = pixelsWide * bitsPerPixel / 8;
 
  if(bytesPerRow % 8 == 0)
  {
    glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
  }
  else if(bytesPerRow % 4 == 0)
  {
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
  }
  else if(bytesPerRow % 2 == 0)
  {
    glPixelStorei(GL_UNPACK_ALIGNMENT, 2);
  }
  else
  {
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  }
 
  glGenTextures(1, &m_uName);
  ccGLBindTexture2D(m_uName);
 
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
 
    // Specify OpenGL texture image
 
  switch(pixelFormat)
  {
    case kCCTexture2DPixelFormat_RGBA8888:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh,
     0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    break;
    case kCCTexture2DPixelFormat_RGB888:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)pixelsWide, (GLsizei)pixelsHigh,
     0, GL_RGB, GL_UNSIGNED_BYTE, data);
    break;
    case kCCTexture2DPixelFormat_RGBA4444:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh,
     0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
    break;
    case kCCTexture2DPixelFormat_RGB5A1:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh,
     0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data);
    break;
    case kCCTexture2DPixelFormat_RGB565:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)pixelsWide, (GLsizei)pixelsHigh,
     0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
    break;
    case kCCTexture2DPixelFormat_AI88:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh,
     0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
    break;
    case kCCTexture2DPixelFormat_A8:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh,
     0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
    break;
    case kCCTexture2DPixelFormat_I8:
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, (GLsizei)pixelsWide, (GLsizei)pixelsHigh,
     0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
    break;
    default:
    CCAssert(0, "NSInternalInconsistencyException");
 
  }
 
  m_tContentSize = contentSize;
  m_uPixelsWide = pixelsWide;
  m_uPixelsHigh = pixelsHigh;
  m_ePixelFormat = pixelFormat;
  m_fMaxS = contentSize.width / (float)(pixelsWide);
  m_fMaxT = contentSize.height / (float)(pixelsHigh);
 
  m_bHasPremultipliedAlpha = false;
  m_bHasMipmaps = false;
 
  setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTexture));
 
  return true;
}


对于那些OpenGL函数是做什么用的,我就不多做介绍了。不明真相的同学可以参考相关资料。这里要注意的是那个m_uName,他是由OpenGL分配的,也是Texture的索引。我们可以注意到,OpenGL是支持相当多的格式的,根据自身的情况选择合适的格式才是王道。如果你要开发一款像素风格的游戏,还要采用RGBA8888的话,我只能说你很有想法了。

下面来看初始化函数:

bool initWithImage(CCImage* uiImage);
 
/** Initializes a texture from a string with dimensions, alignment, font name and font size */
bool initWithString(const char*text,  const char*fontName,float fontSize,const CCSize &dimensions,CCTextAlignmenth Alignment,CCVerticalTextAlignmentv Alignment);
/** Initializes a texture from a string with font name and font size */
bool initWithString(const char*text,cons tchar*fontName,float fontSize);
/** Initializes a texture from a string using a text definition*/
bool initWithString(const char*text,ccFontDefinition* textDefinition);
 
/** Initializes a texture from a PVR file */
bool initWithPVRFile(const char*file);
 
/** Initializes a texture from a ETC file */
bool initWithETCFile(const char*file);


先来讲讲从CCImage来加载纹理。我们通过不同的图片来解压,得到的其实是一个RGBA8888的位图(Bitmap)格式。这个格式是很占用内存的,在initWithImage的内部调用了initPremultipliedATextureWithImage(uiImage, imageWidth, imageHeight);这个函数来将ARGB8888格式转化为我们预设的格式,当然默认情况下是RGBA8888的。这个函数是一个很好的位图格式转化示例,有需求的朋友可以参考下。格式可以由下面的函数来设置:

void CCTexture2D::setDefaultAlphaPixelFormat(CCTexture2DPixelFormat format)
{
    g_defaultAlphaPixelFormat=format;
}
 
CCTexture2DPixelFormat CCTexture2D::defaultAlphaPixelFormat()
{
    return g_defaultAlphaPixelFormat;
}


这是一个静态变量,使用的时候需要注意:

// If the image has alpha, you can create RGBA8 (32-bit) or RGBA4 (16-bit) or RGB5A1 (16-bit)
// Default is: RGBA8888 (32-bit textures)
static CCTexture2DPixelFormat g_defaultAlphaPixelFormat = kCCTexture2DPixelFormat_Default;


我们还可以从一个String初始化一个纹理。还记得我们能从一个String初始化一个CCImage吗?没错,CCTexutre就是先将用String初始化一个CCImage,再调用上面的方法由CCImage初始化的。

最后我们来看看从纹理格式初始化,ETC和PVR个过程极其类似,我们利用PVR进行讲解看其函数:

bool CCTexture2D::initWithPVRFile(const char* file)
{
  bool bRet=false;
    // nothing to do with CCObject::init
 
  CCTexturePVR* pvr = new CCTexturePVR;
  bRet = pvr->initWithContentsOfFile(file);
 
  if(bRet)
  {
    pvr->setRetainName(true);// don't dealloc texture on release
 
    m_uName=pvr->getName();
    m_fMaxS=1.0f;
    m_fMaxT=1.0f;
    m_uPixelsWide=pvr->getWidth();
    m_uPixelsHigh=pvr->getHeight();
    m_tContentSize=CCSizeMake((float)m_uPixelsWide,(float)m_uPixelsHigh);
    m_bHasPremultipliedAlpha=PVRHaveAlphaPremultiplied_;
    m_ePixelFormat=pvr->getFormat();
    m_bHasMipmaps=pvr->getNumberOfMipmaps()>1;      
 
    pvr->release();
  }
  else
  {
    CCLOG("cocos2d: Couldn't load PVR image %s",file);
  }
 
  return bRet;
}


我们看到,它将一些工作交给CCTexturePVR来做,我们只要注意的是那个m_uName就可以了,有了这个索引,我们就能从OpenGL中绑定指定的纹理来进行绘制。它的载入工作也是类似的,不过有一段代码比较有意思:

if(!((unpackPVRv2Data(pvrdata,pvrlen)  ||unpackPVRv3Data(pvrdata,pvrlen))&&createGLTexture()))
{
  CC_SAFE_DELETE_ARRAY(pvrdata);
  this->release();
  return false;
}


注意那个if语句中的函数调用,最后那个函数便是将数据导入到OpenGL中去。具体代码我就不贴了。从CCTexture的导入中我们可以看到,如果使用PVR或者ECT格式的话,加载过程和速度都要比使用图片方便快捷。唯一的不便在于这两种格式的体积略大。

看到这里,我想大家对于cocos2d-x如何加载纹理有了清晰的了解。我最好还有强调一点,就是那个m_uName,它是OpenGL中的纹理索引,而且是一个GLunit类型的。之后我们将会看到是如何管理和使用这个索引的。

CC_PROPERTY_READONLY(GLuint,m_uName,Name)

最后我们来看看纹理的删除:

CCTexture2D::~CCTexture2D()
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
  VolatileTexture::removeTexture(this);
#endif
 
  CCLOGINFO("cocos2d: deallocing CCTexture2D %u.",m_uName);
  CC_SAFE_RELEASE(m_pShaderProgram);
 
  if(m_uName)
  {
      ccGLDeleteTexture(m_uName);
  }
}

void ccGLDeleteTexture(GLuint textureId)
{
  ccGLDeleteTextureN(0,textureId);
}
 
void ccGLDeleteTextureN(GLuint textureUnit,GLuint textureId)
{
#if CC_ENABLE_GL_STATE_CACHE
  if(s_uCurrentBoundTexture[textureUnit]==textureId)
  {
    s_uCurrentBoundTexture[textureUnit]=-1;
  }
#endif // CC_ENABLE_GL_STATE_CACHE
 
  glDeleteTextures(1,&textureId);
}

与加载过程相比,删除过程真是简单粗暴啊。CCTexture是OpenGL中纹理的呈现,里面还有一些管理函数,都比较简单。有兴趣的可以查看。还有一个有意思的东西就是Premultiplied alpha,如果你使用纹理格式,一定要注意这个属性。对其原理,请Google之。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值