本章所用Cocos2d-x版本为:cocos2d-2.0-x-2.0.2 @ Aug 30 2012,http://cn.cocos2d-x.org/download
大家好,最近工作实在是太忙了,公司项目这个月要进行对外测试,大伙都是忙的昏天黑地的,每天很少有时间写新博文,但每天看到博文的回复和排名,心中很是感谢,正是因为各位的支持才能让我保持旺盛的战斗力。当然,在此还要感谢一下亲爱的老婆大 人对我的支持,小乖每天闹困时主动的哄小乖安睡,否则咱咋能有时间写这些文字,小乖一岁半了,长得非常可爱,虽然在京仍然漂着,仍然买不起房,仍然只是一个程序员,但是我知道我已经得到了人生中最幸福的东西。
好了,感叹之后,今天我们来学习Cocos2d-x中的“纹理”。之前有几篇文章都是谈及图片方面的,也是我刻意的安排:
(1)如何利用Cocos2d-x开发一款游戏(2)游戏开发之图片元素(3)Cocos2d-x中图字原理之深入分析(4)红孩儿纹理打包器(5)CCImage深入分析(6)。词典类CCDictionary深入分析
在第一篇里我首先提到了“图元素的管理工具”。这是为什么呢?因为2D游戏画面是由图片构成的。了解好图片从美术绘制到加载到游戏以及使用和释放的整个过程是非常重要的,他关系着游戏的运行效率,内存占用等重要关键问题。有一个好的方案指导工作流程对于项目的成功是一项重要的保证。所以,请各位好好管理你们的图片资源。在第2篇里对于图片拼合的用途和意义做了启蒙。第3篇里我们深入了解了写汉字时Cocos2d-x是怎么使用图片来进行绘制的,它里面CCTextureAtlas和CCSpriteBatchNode 两个类告诉我们图片拼合原理与实现。而第4篇介绍了我开发的一款图片拼合工具。随后我会更新为最新版提供给大家使用。新增了PLIST方式导出和自动进行边缘空白裁剪的功能,免费而更强大!欢迎大家到下载。而后面两篇,其实是为了引出今天的文章而做的铺垫。本博为什么要花这么多时间在纹理上呢?因为本博一次再一次的看到很多的Cocos2d开发者在说:“为什么我的游戏占内存这么大?”。“为什么我的游戏跑起来效率这么低?”。其实这里面最根本的问题是你思想上没有对资源进行优化的一种流程在里面。在进行游戏开发的过程中,你被美术牵着鼻子跑,你没有自然而然的想到该怎么进行图片的格式,尺寸,拼合,复用方式,以及绘制时的方式。这些因素对游戏产生的具体影响在你的脑子里还没有留下完整的概念。所以,我希望经过对于这些源码的分析,让大家去了解这些因素,从而学会思考如何优化自已的游戏。好了,大家先将今天关于纹理代码的分析博文学习完,下一篇我讲对于这些优化因素做一个具体的讲解。
当一张图片被加载到内存后,它是以纹理的形式存在的。纹理是什么东西呢?纹理就是一块内存,这块内存中存放的是按照指定的像素格式填充的图片像素信息。它被最终作为三角面着色所依据的数据源。
我们来看一下cocos2d-x中的libcocos2d库,其下有许多目录,找到textures展开,可以看到有CCTexture2D,CCTextureAtlas,CCTextureCache,CCTexturePVR四个类。
这四个类的功能分别是:
CCTexture2D: 纹理,即图片加载入内存后供cpu和GPU操作的贴图对象。
CCTexturePVR:处理PVR文件生成纹理的类,提示:大家可以用它解析愤怒的小鸟中的图片。
CCTextureCache:纹理管理器,负责加载图片并对生成的纹理进行管理。通过“字典”来进行快速的查询。
CCTextureAtlas:纹理块管理器,如果图片是由多个小图块组成的,则纹理块管理器用来存储这些小图块的相关信息,以方便绘制相应图块。
为了让大家更好的学习纹理,在讲解纹理的代码之前我已经先给大家分析了本章用到的两个功能类:
CCImage和CCDictionary。这两个类分别在纹理模块中担任加载图片和管理纹理指针的作用。希望大家先顶一下这两篇贴子之后再开始下面的代码学习,你一定会感到非常容易。
一.CCTexture2D:
好,咱们现在开始看CCTexture2D:
#ifndef __CCTEXTURE2D_H__ #define __CCTEXTURE2D_H__ #include <string> #include "cocoa/CCObject.h" #include "cocoa/CCGeometry.h" #include "ccTypes.h" //Cocos2d命名空间 NS_CC_BEGIN //需要用到CCImage,这里声明一下。 class CCImage; //纹理格式:即每个纹理中的像素单位分别是怎么为颜色值进行实际内存分配的。这个非常重要,我们在进行游戏开发的过程中,会常常与各种图片类型打交通。每种图片往往也有各自的像素格式。但当它们一旦加载到游戏中后,就会根据我们的要求变成以下某种类型的纹理。不同的纹理格式所占据的内存大小可能不同,我们要根据实际情况和需求来选择相应的纹理格式。比如我们用RGBA8888纹理格式来创建纹理,它占据的内存容量很大,如果我们要显示的纹理中没有ALPHA值,那就不应该使用带ALPHA通道的纹理格式。我们就可以改成RGB565像素格式。 typedef enum { //32位真彩色,最真但最耗内存 kCCTexture2DPixelFormat_RGBA8888,//24位真彩色,去掉了ALPHA通道 kCCTexture2DPixelFormat_RGB888,//16位色,将RGB压缩在一个字中。绿色多了1位,因为人眼对绿色更敏感。 kCCTexture2DPixelFormat_RGB565,//8位色,只存ALPHA值,做遮罩图用 kCCTexture2DPixelFormat_A8,//8位色,只存灰度或者强度值,做灰度图用 kCCTexture2DPixelFormat_I8,//16位色,只存ALPHA值与强度值,双功能 kCCTexture2DPixelFormat_AI88,//16位色,RGBA四通道各占4位。 kCCTexture2DPixelFormat_RGBA4444,//16位色,RGB三通道各占5位,多1位留做ALPHA镂空使用 kCCTexture2DPixelFormat_RGB5A1,// PVR的PVRTC4压缩格式 kCCTexture2DPixelFormat_PVRTC4,// PVRTC的PVRTC2压缩格式 kCCTexture2DPixelFormat_PVRTC2,//默认格式RGBA8888 kCCTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_RGBA8888,// 为了兼容性而保留的枚举值 kTexture2DPixelFormat_RGBA8888 = kCCTexture2DPixelFormat_RGBA8888,kTexture2DPixelFormat_RGB888 = kCCTexture2DPixelFormat_RGB888,kTexture2DPixelFormat_RGB565 = kCCTexture2DPixelFormat_RGB565,kTexture2DPixelFormat_A8 = kCCTexture2DPixelFormat_A8,kTexture2DPixelFormat_RGBA4444 = kCCTexture2DPixelFormat_RGBA4444,kTexture2DPixelFormat_RGB5A1 = kCCTexture2DPixelFormat_RGB5A1,kTexture2DPixelFormat_Default = kCCTexture2DPixelFormat_Default } CCTexture2DPixelFormat; //需要使用Shader代码片段,这里声明一下 class CCGLProgram; //定义了纹理的一些参数 typedef struct _ccTexParams { GLuint minFilter;//纹理过滤器:缩小过滤器 GLuint magFilter;//纹理过滤器:放大过滤器 GLuint wrapS;//横向纹理寻址模式 GLuint wrapT;//纵向纹理寻址模式 } ccTexParams; // CCTexture2D类可以方便的从图片,文本或raw数据文件中创建OpenGL所用贴图,创建的贴图会自动转为2的幂次方大小,所以要注意对于贴图坐标的影响。 class CC_DLL CCTexture2D : public CCObject { public: //构造 CCTexture2D(); //析构 virtual ~CCTexture2D(); //取得纹理的描述 const char* description(void); //释放数据 void releaseData(void *data); //保存数据 void* keepData(void *data,unsigned int length); //由数据指针和指定的像素格式,图片宽高,来生成OpenGL贴图。 bool initWithData(const void* data,CCTexture2DPixelFormat pixelFormat,unsigned int pixelsWide,unsigned int pixelsHigh,const CCSize& contentSize); //在指定的位置绘制贴图 void drawAtPoint(const CCPoint& point); //纹制贴图上的一个图像块 void drawInRect(const CCRect& rect); //由CCImage指针生成OpenGL贴图 bool initWithImage(CCImage * uiImage); //由一个字符串生成OpenGL贴图。 bool initWithString(const char *text,const CCSize& dimensions,CCTextAlignment hAlignment,CCVerticalTextAlignment vAlignment,const char *fontName,float fontSize); //由一个字符串和指定的字体与大小生成OpenGL贴图 bool initWithString(const char *text,float fontSize); //如果支持PVR的压缩格式 #ifdef CC_SUPPORT_PVRTC //由一个PVR压缩格式的数据生成OpenGL贴图 bool initWithPVRTCData(const void *data,int level,int bpp,bool hasAlpha,int length,CCTexture2DPixelFormat pixelFormat); #endif // CC_SUPPORT_PVRTC //从普通PVR文件生成OpenGL贴图 bool initWithPVRFile(const char* file); //设置贴图参数 void setTexParameters(ccTexParams* texParams); //设置为抗锯齿的贴图过滤方式(线性过滤) void setAntiAliasTexParameters(); //设置为非抗锯齿的贴图过滤方式(最近点采样) void setAliasTexParameters(); //生成多级贴图: 由图片数据生成一系列尺寸为2的幂次方直至当前贴图大小的贴图。系统会根据距离自动选择纹理图片。可以解决大图片显示在小空间时的闪烁问题。 void generateMipmap(); //取得像素格式名称 const char* stringForFormat(); //返回当前贴图色深,即每个像素占多少位 unsigned int bitsPerPixelForFormat(); //通过参数贴图格式返回纹理色深 unsigned int bitsPerPixelForFormat(CCTexture2DPixelFormat format); //静态函数,用于设置默认带ALPHA通道的贴图像素格式。则图片创建为贴图时,如果有ALPHA通道,则生成此默认贴图像素格式。 static void setDefaultAlphaPixelFormat(CCTexture2DPixelFormat format); //静态函数,取得默认带ALPHA通道的贴图像素格式。 static CCTexture2DPixelFormat defaultAlphaPixelFormat(); //静态函数,设置载入PVR时是否开启ALPHA渐变,默认不开启,则ALPHA值只有是与否,无渐变。 static void PVRImagesHavePremultipliedAlpha(bool haveAlphaPremultiplied); //取得图片大小(以像素为单位) const CCSize& getContentSizeInPixels(); //是否有ALPHA渐变值 bool hasPremultipliedAlpha(); //是否有多级贴图 bool hasMipmaps(); private: //加载一个带ALPHA渐变的图片生成OpenGL贴图 bool initPremultipliedATextureWithImage(CCImage * image,unsigned int pixelsHigh); //ALPHA渐变开关 bool m_bPVRHaveAlphaPremultiplied; //贴图格式变量及get接口 CC_PROPERTY_READONLY(CCTexture2DPixelFormat,m_ePixelFormat,PixelFormat) //贴图宽度及get接口 CC_PROPERTY_READONLY(unsigned int,m_uPixelsWide,PixelsWide) //贴图高度及get接口 CC_PROPERTY_READONLY(unsigned int,m_uPixelsHigh,PixelsHigh) //OpenGL贴图索引及get接口 CC_PROPERTY_READONLY(GLuint,m_uName,Name) //横向贴图坐标终点。因为图片如果不是2的幂次方,图片大小会小于贴图的大小,贴图一定是2的幂次方嘛,这时候横向的贴图坐标终点不是1.0。 CC_PROPERTY(GLfloat,m_fMaxS,MaxS) //纵向贴图坐标终点。 CC_PROPERTY(GLfloat,m_fMaxT,MaxT) //图片大小及get接口 CC_PROPERTY_READONLY(CCSize,m_tContentSize,ContentSize) // ALPHA渐变开关 bool m_bHasPremultipliedAlpha; // 多级纹理开关 bool m_bHasMipmaps; //Shader代码片段指针 CC_PROPERTY(CCGLProgram*,m_pShaderProgram,ShaderProgram); }; NS_CC_END #endif //__CCTEXTURE2D_H__
再来看CCTexture2D.cpp:
#include "CCTexture2D.h" #include "ccConfig.h" #include "ccMacros.h" #include "CCConfiguration.h" #include "platform/platform.h" #include "platform/CCImage.h" #include "CCGL.h" #include "support/ccUtils.h" #include "platform/CCPlatformMacros.h" #include "textures/CCTexturePVR.h" #include "CCDirector.h" #include "shaders/CCGLProgram.h" #include "shaders/ccGLStateCache.h" #include "shaders/CCShaderCache.h" //这里定义是否使用可变纹理 #if CC_ENABLE_CACHE_TEXTURE_DATA #include "CCTextureCache.h" #endif //Cocos2d-x命名空间 NS_CC_BEGIN //静态全局的默认贴图像素格式。缺省为kCCTexture2DPixelFormat_Default,即RGBA8888。 static CCTexture2DPixelFormat g_defaultAlphaPixelFormat = kCCTexture2DPixelFormat_Default; //静态全局的PVR是否有ALPHA渐变的开关变量,默认为否。 static bool PVRHaveAlphaPremultiplied_ = false; //构造函数。 CCTexture2D::CCTexture2D() : m_uPixelsWide(0),m_uPixelsHigh(0),m_uName(0),m_fMaxS(0.0),m_fMaxT(0.0),m_bHasPremultipliedAlpha(false),m_bHasMipmaps(false),m_bPVRHaveAlphaPremultiplied(true),m_pShaderProgram(NULL) { } //析构 CCTexture2D::~CCTexture2D() { //如果使用可变纹理,删除此可变纹理中的数据。 #if CC_ENABLE_CACHE_TEXTURE_DATA VolatileTexture::removeTexture(this); #endif //打印日志。 CCLOGINFO("cocos2d: deallocing CCTexture2D %u.",m_uName); //释放所用到的Shader代码片段 CC_SAFE_RELEASE(m_pShaderProgram); //释放OpenGL所用到的贴图。 if(m_uName) { ccGLDeleteTexture(m_uName); } } //取得当前纹理的贴图像素格式。 CCTexture2DPixelFormat CCTexture2D::getPixelFormat() { return m_ePixelFormat; } //取得贴图宽度。 unsigned int CCTexture2D::getPixelsWide() { return m_uPixelsWide; } //取得贴图高度。 unsigned int CCTexture2D::getPixelsHigh() { return m_uPixelsHigh; } //取得贴图索引。 GLuint CCTexture2D::getName() { return m_uName; } //取得图片大小(以点为单位) CCSize CCTexture2D::getContentSize() { // CC_CONTENT_SCALE_FACTOR宏返回的是在不同屏幕下的点与像素的比率。Mac电脑上返回1.而使用Retina显示屏的iphone上返回2。 CCSize ret; ret.width = m_tContentSize.width / CC_CONTENT_SCALE_FACTOR(); ret.height = m_tContentSize.height / CC_CONTENT_SCALE_FACTOR(); return ret; } //取得图片大小(以像素为单位) const CCSize& CCTexture2D::getContentSizeInPixels() { return m_tContentSize; } //取得横向的贴图坐标终点 GLfloat CCTexture2D::getMaxS() { return m_fMaxS; } //设置横向的贴图坐标终点 void CCTexture2D::setMaxS(GLfloat maxS) { m_fMaxS = maxS; } //取得纵向的贴图坐标终点 GLfloat CCTexture2D::getMaxT() { return m_fMaxT; } //设置纵向的贴图坐标终点 void CCTexture2D::setMaxT(GLfloat maxT) { m_fMaxT = maxT; } //所用到的Shader代码片段。 CCGLProgram* CCTexture2D::getShaderProgram(void) { return m_pShaderProgram; } //设置用到的Shader代码片段。 void CCTexture2D::setShaderProgram(CCGLProgram* pShaderProgram) { CC_SAFE_RETAIN(pShaderProgram); CC_SAFE_RELEASE(m_pShaderProgram); m_pShaderProgram = pShaderProgram; } //释放数据 void CCTexture2D::releaseData(void *data) { free(data); } //保存数据 void* CCTexture2D::keepData(void *data,unsigned int length) { //这里只是使用CC_UNUSED_PARAM宏用一下length,没什么实际功能。作者给出这个函数是预备未来供子类重载。 CC_UNUSED_PARAM(length); return data; } //是否有ALPHA渐变的通道数据。 bool CCTexture2D::hasPremultipliedAlpha() { return m_bHasPremultipliedAlpha; } //由数据指针创建指定大小和格式的贴图。取得创建成功后图片在贴图中的实际区域 。 bool CCTexture2D::initWithData(const void *data,const CCSize& contentSize) { //如果是RGBA8888格式或者大小正好就是2的幂次方。像素数据按四字节(DWORD)对齐。否则按1字节(BYTE)进行对齐。 if( pixelFormat == kCCTexture2DPixelFormat_RGBA8888 || ( ccNextPOT(pixelsWide)==pixelsWide && ccNextPOT(pixelsHigh)==pixelsHigh) ) { glPixelStorei(GL_UNPACK_ALIGNMENT,4); } else { glPixelStorei(GL_UNPACK_ALIGNMENT,1); } //产生一个OpenGL的贴图索引。 glGenTextures(1,&m_uName); //将此贴图绑定为GL_TEXTURE_2D纹理。 ccGLBindTexture2D(m_uName); //设置OpenGL中的贴图的过滤参数。 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 ); //这里根据不同的像素格式来生成不同的OpenGL所用的贴图。注意:传入的宽和高在成功生成贴图后会返回实际贴图的宽和高。如果图片不是2的幂次方,这个数值会改成2的幂次方。比如你传入的图片宽高是148x245,则调用完成后宽高会转成256x256。 switch(pixelFormat) { case kCCTexture2DPixelFormat_RGBA8888: glTexImage2D(GL_TEXTURE_2D,GL_RGBA,(GLsizei)pixelsWide,(GLsizei)pixelsHigh,GL_UNSIGNED_BYTE,data); break; case kCCTexture2DPixelFormat_RGB888: glTexImage2D(GL_TEXTURE_2D,GL_RGB,data); break; case kCCTexture2DPixelFormat_RGBA4444: glTexImage2D(GL_TEXTURE_2D,GL_UNSIGNED_SHORT_4_4_4_4,data); break; case kCCTexture2DPixelFormat_RGB5A1: glTexImage2D(GL_TEXTURE_2D,GL_UNSIGNED_SHORT_5_5_5_1,data); break; case kCCTexture2DPixelFormat_RGB565: glTexImage2D(GL_TEXTURE_2D,GL_UNSIGNED_SHORT_5_6_5,data); break; case kCCTexture2DPixelFormat_AI88: glTexImage2D(GL_TEXTURE_2D,GL_LUMINANCE_ALPHA,data); break; case kCCTexture2DPixelFormat_A8: glTexImage2D(GL_TEXTURE_2D,GL_ALPHA,data); break; case kCCTexture2DPixelFormat_I8: glTexImage2D(GL_TEXTURE_2D,GL_LUMINANCE,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); //默认不使用ALPHA渐变通道。 m_bHasPremultipliedAlpha = false; //默认不使用多级纹理。 m_bHasMipmaps = false; //设置使用kCCShader_PositionTexture对应类型的Shader。此Shader的顶点格式由位置和纹理坐标组成。 setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTexture)); return true; } //取得纹理的描述 const char* CCTexture2D::description(void) { return CCString::createWithFormat("<CCTexture2D | Name = %u | Dimensions = %u x %u | Coordinates = (%.2f,%.2f)>",m_fMaxT)->getCString(); } // 由CCImage对象来初始化纹理生成OpenGL贴图。 bool CCTexture2D::initWithImage(CCImage *uiImage) { //参数有效性判断。 if (uiImage == NULL) { CCLOG("cocos2d: CCTexture2D. Can't create Texture. UIImage is nil"); this->release(); return false; } //取得图片的宽高 unsigned int imageWidth = uiImage->getWidth(); unsigned int imageHeight = uiImage->getHeight(); //取得引擎的配置信息 CCConfiguration *conf = CCConfiguration::sharedConfiguration(); //取得配置信息中指定的最大纹理大小 unsigned maxTextureSize = conf->getMaxTextureSize(); //如果当前图片大于指定的最大纹理大小,提示错误警告交释放当前纹理返回NULL。 if (imageWidth > maxTextureSize || imageHeight > maxTextureSize) { CCLOG("cocos2d: WARNING: Image (%u x %u) is bigger than the supported %u x %u",imageWidth,imageHeight,maxTextureSize,maxTextureSize); this->release(); return NULL; } //总是按加载ALPHA渐变的图片方式来生成OpenGL贴图 return initPremultipliedATextureWithImage(uiImage,imageHeight); } //加载一个带ALPHA渐变的图片生成OpenGL贴图 bool CCTexture2D::initPremultipliedATextureWithImage(CCImage *image,unsigned int width,unsigned int height) { //取得图片的相关信息 //定义指针变量指向图片像素数据。 unsigned char* tempData = image->getData(); //定义无符号int指针变量,也是为了指向32位色深的图片像素数据,以便使指针直接对应指定的一个像素数据位置。 unsigned int* inPixel32 = NULL; //定义无符号char指针变量,也是为了指向8位色深的图片像素数据,以便使指针直接对应指定的一个像素数据位置。 unsigned char* inPixel8 = NULL; //定义无符号short指针变量,指向16位色深的贴图像素数据,以便使指针直接对应指定的一个像素数据位置。 unsigned short* outPixel16 = NULL; //定义bool变量hasAlpha取得图片是否有Alpha通道。 bool hasAlpha = image->hasAlpha(); //定义变量imageSize保存图片大小。 CCSize imageSize = CCSizeMake((float)(image->getWidth()),(float)(image->getHeight())); //定义变量pixelFormat用来保存贴图的像素格式。 CCTexture2DPixelFormat pixelFormat; //定义变量bpp保存图片的色深。 size_t bpp = image->getBitsPerComponent(); // 如果有ALPHA通道,使用默认的RGBA8888格式。 if(hasAlpha) { pixelFormat = g_defaultAlphaPixelFormat; } else { //如果没有ALPHA通道 //如果色深大于等于8,则转为RGB888格式,否则转为RGB565格式。这里有点问题,感觉应该按色深大于16来进行判断。即24和32位都转为RGB888,而16位及以下转为RGB565。 if (bpp >= 8) { pixelFormat = kCCTexture2DPixelFormat_RGB888; } else { pixelFormat = kCCTexture2DPixelFormat_RGB565; } } // 取得数据的长度 unsigned int length = width * height; //根据图片的不同格式和要创建的纹理格式,将数据填充到纹理中。 if (pixelFormat == kCCTexture2DPixelFormat_RGB565) { //根据是否有ALPHA通道来分别进行填充处理 if (hasAlpha) { // 转换RGBA8888到RGB565 // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB" //一个像素占2个字节,所以图像中所有像素占用的字节数为width*height*2。由此大小申请内存作为贴图的像素数据。 tempData = new unsigned char[width * height * 2]; //将贴图像素数据的地址返回给unsigned short指针。这样outPixel16就指向了贴图中的第一个像素的数据位置。 outPixel16 = (unsigned short*)tempData; //因为有alpha,则图片是32位RGBA8888格式。取得图像的像素数据地址返回给unsigned int指针,则inPixel32指向了贴图中第一个像素的数据位置。 inPixel32 = (unsigned int*)image->getData(); //遍历图片中所有的像素,逐像素处理。 for(unsigned int i = 0; i < length; ++i,++inPixel32) { //将inPixel32指向的unsigned int数据通过取出R,G,B各8位数据值,然后组成RGB565值。放入outPixel16指向的unsigned short数据中。 *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R ((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | // G ((((*inPixel32 >> 16) & 0xFF) >> 3) << 0); // B } } else { // 转换RGB888到RGB565 // Convert "RRRRRRRRRGGGGGGGGBBBBBBBB" to "RRRRRGGGGGGBBBBB" //一个像素占2个字节,所以图像中所有像素占用的字节数为width*height*2。由此大小申请内存作为贴图的像素数据。 tempData = new unsigned char[width * height * 2]; //将贴图像素数据的地址返回给unsigned short指针。这样outPixel16就指向了贴图中的第一个像素的数据位置。 outPixel16 = (unsigned short*)tempData; //如果图像的格式为RGB888。取得图像的像素数据地址返回给unsigned char指针,则inPixel8指向了贴图中第一个像素的R值位置。 inPixel8 = (unsigned char*)image->getData(); //遍历图片中所有的像素,逐像素处理。 for(unsigned int i = 0; i < length; ++i) { //inPixel8指向的是unsigned char值,通过++操作来取出R,B数据值,然后组成RGB565值。放入outPixel16指向的unsigned short数据中。 *outPixel16++ = (((*inPixel8++ & 0xFF) >> 3) << 11) | // R (((*inPixel8++ & 0xFF) >> 2) << 5) | // G (((*inPixel8++ & 0xFF) >> 3) << 0); // B } } } else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444) { // 转换RGBA8888到RGBA4444 // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA" //取得图像的像素数据地址返回给unsigned int指针,则inPixel32指向了贴图中第一个像素的数据位置。 inPixel32 = (unsigned int*)image->getData(); //一个像素占2个字节,所以图像中所有像素占用的字节数为width*height*2。由此大小申请内存作为贴图的像素数据。 tempData = new unsigned char[width * height * 2]; //将贴图像素数据的地址返回给unsigned short指针。这样outPixel16就指向了贴图中的第一个像素的数据位置。 outPixel16 = (unsigned short*)tempData; //遍历图片中所有的像素,逐像素处理。 for(unsigned int i = 0; i < length; ++i,++inPixel32) { //将inPixel32指向的unsigned int数据通过取出R,B,A各8位数据值,然后组成RGBA4444值。放入outPixel16指向的unsigned short数据中。 *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R ((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G ((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B ((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A } } else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1) { // 转换RGBA8888到RGBA5551 // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA" inPixel32 = (unsigned int*)image->getData(); //一个像素占2个字节,所以图像中所有像素占用的字节数为width*height*2。由此大小申请内存作为贴图的像素数据。 tempData = new unsigned char[width * height * 2]; //将贴图像素数据的地址返回给unsigned short指针。这样outPixel16就指向了贴图中的第一个像素的数据位置。 outPixel16 = (unsigned short*)tempData; //遍历图片中所有的像素,逐像素处理。 for(unsigned int i = 0; i < length; ++i,A各8位数据值,然后组成RGB5A1值。放入outPixel16指向的unsigned short数据中。 *outPixel16++ = ((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R ((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G ((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B ((((*inPixel32 >> 24) & 0xFF) >> 7) << 0); // A } } else if (pixelFormat == kCCTexture2DPixelFormat_A8) { // 转换RGBA8888到A8,同理,不再赘述 // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "AAAAAAAA" inPixel32 = (unsigned int*)image->getData(); tempData = new unsigned char[width * height]; unsigned char *outPixel8 = tempData; for(unsigned int i = 0; i < length; ++i,++inPixel32) { *outPixel8++ = (*inPixel32 >> 24) & 0xFF; // A } } if (hasAlpha && pixelFormat == kCCTexture2DPixelFormat_RGB888) { // 转换RGBA8888到RGB888,同理,不再赘述 // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRRRRGGGGGGGGBBBBBBBB" inPixel32 = (unsigned int*)image->getData(); tempData = new unsigned char[width * height * 3]; unsigned char *outPixel8 = tempData; for(unsigned int i = 0; i < length; ++i,++inPixel32) { *outPixel8++ = (*inPixel32 >> 0) & 0xFF; // R *outPixel8++ = (*inPixel32 >> 8) & 0xFF; // G *outPixel8++ = (*inPixel32 >> 16) & 0xFF; // B } } //因为最终相应像素格式的数据都存放在tempData中,所以这里通过像素数据来生成OpenGL贴图。 initWithData(tempData,pixelFormat,width,height,imageSize); //如果是以上相应格式,则tempData都是新申请的内存块,则在这里释放申请的内存。 if (tempData != image->getData()) { delete [] tempData; } //取得是否有ALPHA渐变通道数据 m_bHasPremultipliedAlpha = image->isPremultipliedAlpha(); return true; } // 从字符串中创建OpenGL 贴图。 //参1:字符串 //参2:字体名称 //参3:字体大小 bool CCTexture2D::initWithString(const char *text,float fontSize) { return initWithString(text,CCSizeMake(0,0),kCCTextAlignmentCenter,kCCVerticalTextAlignmentTop,fontName,fontSize); } // 从字符串中创建OpenGL 贴图,可指定更多参数。 //参1:字符串 //参2:返回参数,代表在屏幕上占用的区域大小 //参3:文字的横向对齐方式 //参4:文字的纵向对齐方式 //参5:字体名称 //参6:字体大小 bool CCTexture2D::initWithString(const char *text,float fontSize) { //如果定义使用可变纹理 #if CC_ENABLE_CACHE_TEXTURE_DATA // cache the texture data VolatileTexture::addStringTexture(this,text,dimensions,hAlignment,vAlignment,fontSize); #endif //定义一个CCImage实例对象 CCImage image; //定义一个CCImage进行由字符串创建图片时指定的文字对齐方式的变量eAlign CCImage::ETextAlign eAlign; //如果文字纵向对齐方式为顶部对齐。 if (kCCVerticalTextAlignmentTop == vAlignment) { //根据文字横向对齐方式的不同分别对eAlign进行设置。 eAlign = (kCCTextAlignmentCenter == hAlignment) ? CCImage::kAlignTop : (kCCTextAlignmentLeft == hAlignment) ? CCImage::kAlignTopLeft : CCImage::kAlignTopRight; } //如果文字纵向对齐方式为居中对齐。 else if (kCCVerticalTextAlignmentCenter == vAlignment) { //根据文字横向对齐方式的不同分别对eAlign进行设置。 eAlign = (kCCTextAlignmentCenter == hAlignment) ? CCImage::kAlignCenter : (kCCTextAlignmentLeft == hAlignment) ? CCImage::kAlignLeft : CCImage::kAlignRight; } //如果文字纵向对齐方式为底部对齐。 else if (kCCVerticalTextAlignmentBottom == vAlignment) { //根据文字横向对齐方式的不同分别对eAlign进行设置。 eAlign = (kCCTextAlignmentCenter == hAlignment) ? CCImage::kAlignBottom : (kCCTextAlignmentLeft == hAlignment) ? CCImage::kAlignBottomLeft : CCImage::kAlignBottomRight; } else { //其它对齐方式不应存在,故打印错误。 CCAssert(false,"Not supported alignment format!"); } //调用CCImage的成员函数由字符串创建出图片数据。 if (!image.initWithString(text,(int)dimensions.width,(int)dimensions.height,eAlign,(int)fontSize)) { return false; } //再由CCImage实例对象来创建出OpenGL贴图,初始化纹理。 return initWithImage(&image); } // 在指定的位置绘制贴图。 void CCTexture2D::drawAtPoint(const CCPoint& point) { //定义贴图中图像区域的UV坐标。从左上至右下。 GLfloat coordinates[] = { 0.0f,0.0f,0.0f }; //取得贴图中图像区域的宽高 GLfloat width = (GLfloat)m_uPixelsWide * m_fMaxS,height = (GLfloat)m_uPixelsHigh * m_fMaxT; //定义对应的顶点坐标 GLfloat vertices[] = { point.x,point.y,width + point.x,point.x,height + point.y,height + point.y }; //Shader中使用位置和纹理坐标通道。 ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); //后面的代码便用Shader进行渲染 m_pShaderProgram->use(); //设置Shader使用的最终结果矩阵 m_pShaderProgram->setUniformForModelViewProjectionMatrix(); //将贴图绑定 ccGLBindTexture2D( m_uName ); //将vertices设置为顶点位置参数 glVertexAttribPointer(kCCVertexAttrib_Position,2,GL_FLOAT,GL_FALSE,vertices); //将coordinates设置为顶点的纹理坐标参数 glVertexAttribPointer(kCCVertexAttrib_TexCoords,coordinates); //绘制三角形,参1为绘图方式,参2为顶点起始索引,参3为三角形面数。 glDrawArrays(GL_TRIANGLE_STRIP,4); } //绘制纹理上的一个区域。 void CCTexture2D::drawInRect(const CCRect& rect) { //定义贴图中图像区域的UV坐标。从左上至右下。 GLfloat coordinates[] = { 0.0f,0.0f }; //绘制到的区域 GLfloat vertices[] = { rect.origin.x,rect.origin.y,/*0.0f,*/ rect.origin.x + rect.size.width,*/ rect.origin.x,rect.origin.y + rect.size.height,/*0.0f*/ }; //Shader中使用位置和纹理坐标通道。 ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); //后面的代码便用Shader进行渲染 m_pShaderProgram->use(); //设置Shader使用的最终结果矩阵 m_pShaderProgram->setUniformForModelViewProjectionMatrix(); //将贴图绑定 ccGLBindTexture2D( m_uName ); //将vertices设置为顶点位置参数 glVertexAttribPointer(kCCVertexAttrib_Position,4); } #ifdef CC_SUPPORT_PVRTC // 如果支持PVR文件的压缩格式。提供的读取PVR压缩文件的函数。 bool CCTexture2D::initWithPVRTCData(const void *data,CCTexture2DPixelFormat pixelFormat) { if( !(CCConfiguration::sharedConfiguration()->supportsPVRTC()) ) { CCLOG("cocos2d: WARNING: PVRTC images is not supported."); this->release(); return false; } //产生一个OpenGL的贴图索引。 glGenTextures(1,&m_uName); //绑定纹理 glBindTexture(GL_TEXTURE_2D,m_uName); //设置纹理抗锯齿 this->setAntiAliasTexParameters(); //贴图格式 GLenum format; //数据大小 GLsizei size = length * length * bpp / 8; //根据是否有Alpha来取得贴图格式 if(hasAlpha) { format = (bpp == 4) ? GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } else { format = (bpp == 4) ? GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG : GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; } if(size < 32) { size = 32; } //加载压缩纹理。 glCompressedTexImage2D(GL_TEXTURE_2D,level,format,length,size,data); //设置其它属性。 m_tContentSize = CCSizeMake((float)(length),(float)(length)); m_uPixelsWide = length; m_uPixelsHigh = length; m_fMaxS = 1.0f; m_fMaxT = 1.0f; m_bHasPremultipliedAlpha = PVRHaveAlphaPremultiplied_; m_ePixelFormat = pixelFormat; return true; } #endif // CC_SUPPORT_PVRTC //加载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; } //设置PVR文件加载时是否使用ALPHA渐变。 void CCTexture2D::PVRImagesHavePremultipliedAlpha(bool haveAlphaPremultiplied) { PVRHaveAlphaPremultiplied_ = haveAlphaPremultiplied; } //生成多级纹理。 void CCTexture2D::generateMipmap() { CCAssert( m_uPixelsWide == ccNextPOT(m_uPixelsWide) && m_uPixelsHigh == ccNextPOT(m_uPixelsHigh),"Mimpap texture only works in POT textures"); ccGLBindTexture2D( m_uName ); glGenerateMipmap(GL_TEXTURE_2D); m_bHasMipmaps = true; } //是否有多级纹理 bool CCTexture2D::hasMipmaps() { return m_bHasMipmaps; } //设置纹理参数。 void CCTexture2D::setTexParameters(ccTexParams *texParams) { CCAssert( (m_uPixelsWide == ccNextPOT(m_uPixelsWide) || texParams->wrapS == GL_CLAMP_TO_EDGE) && (m_uPixelsHigh == ccNextPOT(m_uPixelsHigh) || texParams->wrapT == GL_CLAMP_TO_EDGE),"GL_CLAMP_TO_EDGE should be used in NPOT dimensions"); ccGLBindTexture2D( m_uName ); glTexParameteri(GL_TEXTURE_2D,texParams->minFilter ); glTexParameteri(GL_TEXTURE_2D,texParams->magFilter ); glTexParameteri(GL_TEXTURE_2D,texParams->wrapS ); glTexParameteri(GL_TEXTURE_2D,texParams->wrapT ); } //设置为非抗锯齿纹理模式 void CCTexture2D::setAliasTexParameters() { //绑定纹理 ccGLBindTexture2D( m_uName ); //设置多级纹理 if( ! m_bHasMipmaps ) { //设置最小滤波方式为最近点采样,这种方式最快,但有锯齿。 glTexParameteri( GL_TEXTURE_2D,GL_NEAREST ); } else { //设置最小滤波方式为多级纹理方式。 glTexParameteri( GL_TEXTURE_2D,GL_NEAREST_MIPMAP_NEAREST ); } //设置放大滤波方式为最近点采样 glTexParameteri( GL_TEXTURE_2D,GL_NEAREST ); } //设置为抗锯齿纹理模式 void CCTexture2D::setAntiAliasTexParameters() { //绑定纹理 ccGLBindTexture2D( m_uName ); //设置多级纹理 if( ! m_bHasMipmaps ) { //设置最小滤波方式为线性过滤,这种方式纹理会有一定程度模糊。 glTexParameteri( GL_TEXTURE_2D,GL_LINEAR ); } else { //设置最小滤波方式为多级纹理方式。 glTexParameteri( GL_TEXTURE_2D,GL_LINEAR_MIPMAP_NEAREST ); } //设置放大滤波方式为最近点采样 glTexParameteri( GL_TEXTURE_2D,GL_LINEAR ); } //取得纹理格式的名字字符串 const char* CCTexture2D::stringForFormat() { switch (m_ePixelFormat) { case kCCTexture2DPixelFormat_RGBA8888: return "RGBA8888"; case kCCTexture2DPixelFormat_RGB888: return "RGB888"; case kCCTexture2DPixelFormat_RGB565: return "RGB565"; case kCCTexture2DPixelFormat_RGBA4444: return "RGBA4444"; case kCCTexture2DPixelFormat_RGB5A1: return "RGB5A1"; case kCCTexture2DPixelFormat_AI88: return "AI88"; case kCCTexture2DPixelFormat_A8: return "A8"; case kCCTexture2DPixelFormat_I8: return "I8"; case kCCTexture2DPixelFormat_PVRTC4: return "PVRTC4"; case kCCTexture2DPixelFormat_PVRTC2: return "PVRTC2"; default: CCAssert(false,"unrecognised pixel format"); CCLOG("stringForFormat: %ld,cannot give useful result",(long)m_ePixelFormat); break; } return NULL; } //设置默认带ALPHA通道的纹理格式 void CCTexture2D::setDefaultAlphaPixelFormat(CCTexture2DPixelFormat format) { g_defaultAlphaPixelFormat = format; } //取得默认带ALPHA通道的纹理格式 CCTexture2DPixelFormat CCTexture2D::defaultAlphaPixelFormat() { return g_defaultAlphaPixelFormat; } //取得相应纹理格式的色深。 unsigned int CCTexture2D::bitsPerPixelForFormat(CCTexture2DPixelFormat format) { unsigned int ret=0; switch (format) { case kCCTexture2DPixelFormat_RGBA8888: ret = 32;//32位真彩色 break; case kCCTexture2DPixelFormat_RGB888: // 看起来是用24位,但内部实际是用DWORD来存数据的,所以还是32位。只不过Alpha通道没用。 ret = 32; break; case kCCTexture2DPixelFormat_RGB565: ret = 16; break; case kCCTexture2DPixelFormat_RGBA4444: ret = 16; break; case kCCTexture2DPixelFormat_RGB5A1: ret = 16; break; case kCCTexture2DPixelFormat_AI88: ret = 16; break; case kCCTexture2DPixelFormat_A8: ret = 8; break; case kCCTexture2DPixelFormat_I8: ret = 8; break; case kCCTexture2DPixelFormat_PVRTC4: ret = 4; break; case kCCTexture2DPixelFormat_PVRTC2: ret = 2; break; default: ret = -1; CCAssert(false,"unrecognised pixel format"); CCLOG("bitsPerPixelForFormat: %ld,(long)format); break; } return ret; } //取得当前纹理格式的色深。 unsigned int CCTexture2D::bitsPerPixelForFormat() { return this->bitsPerPixelForFormat(m_ePixelFormat); } NS_CC_END
二.CCTexturePVR:
CCTexturePVR.h:#ifndef __CCPVRTEXTURE_H__ #define __CCPVRTEXTURE_H__ #include "CCStdC.h" #include "CCGL.h" #include "cocoa/CCObject.h" #include "cocoa/CCArray.h" //Cocos2d命名空间 NS_CC_BEGIN //用于多级纹理的结构 struct CCPVRMipmap { unsigned char *address; unsigned int len; }; //最大的多级纹理级数 enum { CC_PVRMIPMAP_MAX = 16,}; //所支持的 PVR 图片格式: - RGBA8888 - BGRA8888 - RGBA4444 - RGBA5551 - RGB565 - A8 - I8 - AI88 - PVRTC 4BPP - PVRTC 2BPP class CCTexturePVR : public CCObject { public: //构造 CCTexturePVR(); //析构 virtual ~CCTexturePVR(); //成员函数:载入一个PVR图片文件 bool initWithContentsOfFile(const char* path); //导出的静态函数:载入一个PVR图片文件。此函数为可供js调用。 CC_DEPRECATED_ATTRIB一个UTE static CCTexturePVR* pvrTextureWithContentsOfFile(const char* path); //静态函数:载入一个PVR图片文件 static CCTexturePVR* create(const char* path); //相关属性的获取 //取得贴图索引 inline unsigned int getName() { return m_uName; } //取得 inline unsigned int getWidth() { return m_uWidth; } inline unsigned int getHeight() { return m_uHeight; } //取得是否有ALPHA通道 inline bool hasAlpha() { return m_bHasAlpha; } //取得多级纹理的级数 inline unsigned int getNumberOfMipmaps() { return m_uNumberOfMipmaps; } //取得图片格式 inline CCTexture2DPixelFormat getFormat() { return m_eFormat; } //取得是否是供Retain显示屏使用的高清图片 。 inline bool isRetainName() { return m_bRetainName; } //设置为供Retain显示屏使用的高清图片。 inline void setRetainName(bool retainName) { m_bRetainName = retainName; } private: //解压PVR图片数据 bool unpackPVRData(unsigned char* data,unsigned int len); //创建OpenGL纹理 bool createGLTexture(); protected: //多级纹理的各级信息结构。 struct CCPVRMipmap m_asMipmaps[CC_PVRMIPMAP_MAX]; // pointer to mipmap images //多级纹理的最大级别数 unsigned int m_uNumberOfMipmaps; // number of mipmap used // unsigned int m_uTableFormatIndex; unsigned int m_uWidth,m_uHeight; //贴图索引 GLuint m_uName; //是否有Alpha通道 bool m_bHasAlpha; //是否是供Retain显示屏使用的高清图片 。 bool m_bRetainName; //图片格式 CCTexture2DPixelFormat m_eFormat; }; NS_CC_END #endif //__CCPVRTEXTURE_H__
CCTexturePVR.cpp:
#include "CCTexture2D.h" #include "CCTexturePVR.h" #include "ccMacros.h" #include "CCConfiguration.h" #include "support/ccUtils.h" #include "CCStdC.h" #include "platform/CCFileUtils.h" #include "support/zip_support/ZipUtils.h" #include "shaders/ccGLStateCache.h" #include <ctype.h> #include <cctype> //Cocos2d命名空间 NS_CC_BEGIN #define PVR_TEXTURE_FLAG_TYPE_MASK 0xff //PVR文件的信息标志位的各bit位意义 enum { kPVRTextureFlagMipmap = (1<<8),// 有多级纹理 kPVRTextureFlagTwiddle = (1<<9),// is twiddled kPVRTextureFlagBumpmap = (1<<10),// 是法线贴图(用于产生凹凸感) kPVRTextureFlagTiling = (1<<11),// is bordered for tiled pvr kPVRTextureFlagCubemap = (1<<12),// 是立方体环境映射贴图(一般用于做天空盒) kPVRTextureFlagFalseMipCol = (1<<13),// are there false coloured MIP levels kPVRTextureFlagVolume = (1<<14),// 立体纹理,相当于有多层的纹理。 kPVRTextureFlagAlpha = (1<<15),// v2.1 is there transparency info in the texture kPVRTextureFlagVerticalFlip = (1<<16),// v2.1 is the texture vertically flipped }; //PVR文件头标识 static char gPVRTexIdentifier[5] = "PVR!"; //所有PVR文件的格式 enum { kPVRTexturePixelTypeRGBA_4444= 0x10,kPVRTexturePixelTypeRGBA_5551,kPVRTexturePixelTypeRGBA_8888,kPVRTexturePixelTypeRGB_565,kPVRTexturePixelTypeRGB_555,// 这个Cocos2d-x暂不支持 kPVRTexturePixelTypeRGB_888,kPVRTexturePixelTypeI_8,kPVRTexturePixelTypeAI_88,kPVRTexturePixelTypePVRTC_2,kPVRTexturePixelTypePVRTC_4,kPVRTexturePixelTypeBGRA_8888,kPVRTexturePixelTypeA_8,}; //信息数组 static const unsigned int tableFormats[][7] = { //数组元素的结构为: // 1- PVR 文件格式 // 2- OpenGL 内部格式 // 3- OpenGL 格式 // 4- OpenGL 数据类型 // 5- 色深 // 6- 是否压缩 // 7- Cocos2d 像素格式 { kPVRTexturePixelTypeRGBA_4444,16,false,kCCTexture2DPixelFormat_RGBA4444 },{ kPVRTexturePixelTypeRGBA_5551,kCCTexture2DPixelFormat_RGB5A1 },{ kPVRTexturePixelTypeRGBA_8888,32,kCCTexture2DPixelFormat_RGBA8888 },{ kPVRTexturePixelTypeRGB_565,kCCTexture2DPixelFormat_RGB565 },{ kPVRTexturePixelTypeRGB_888,24,kCCTexture2DPixelFormat_RGB888 },{ kPVRTexturePixelTypeA_8,8,kCCTexture2DPixelFormat_A8 },{ kPVRTexturePixelTypeI_8,kCCTexture2DPixelFormat_I8 },{ kPVRTexturePixelTypeAI_88,kCCTexture2DPixelFormat_AI88 },#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) //如果程序运行在IOS上,还可支持以下信息 { kPVRTexturePixelTypePVRTC_2,GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG,(unsigned int)-1,true,kCCTexture2DPixelFormat_PVRTC2 },{ kPVRTexturePixelTypePVRTC_4,GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG,4,kCCTexture2DPixelFormat_PVRTC4 },{ kPVRTexturePixelTypeBGRA_8888,GL_BGRA,#endif }; //信息表的元素数量 #define MAX_TABLE_ELEMENTS (sizeof(tableFormats) / sizeof(tableFormats[0])) // enum { kCCInternalPVRTextureFormat,kCCInternalOpenGLInternalFormat,kCCInternalOpenGLFormat,kCCInternalOpenGLType,kCCInternalBPP,kCCInternalCompressedImage,kCCInternalCCTexture2DPixelFormat,}; //PVR文件头信息结构 typedef struct _PVRTexHeader { unsigned int headerLength; //头信息长度 unsigned int height; //高 unsigned int width; //宽 unsigned int numMipmaps; //是否有多级纹理 unsigned int flags; //标记位 unsigned int dataLength; //后面的像素数据长度 unsigned int bpp; //色深 unsigned int bitmaskRed; //对应红色的像素位 unsigned int bitmaskGreen; //对应绿色的像素位 unsigned int bitmaskBlue; //对应蓝色的像素位 unsigned int bitmaskAlpha; //对应ALPHA色的像素位 unsigned int pvrTag; // unsigned int numSurfs; //是否有多层 } PVRTexHeader; //构造函数 CCTexturePVR::CCTexturePVR() : m_uTableFormatIndex(0),m_uNumberOfMipmaps(0),m_uWidth(0),m_uHeight(0),m_bRetainName(false),m_bHasAlpha(false),m_eFormat(kCCTexture2DPixelFormat_Default) { } //析构函数 CCTexturePVR::~CCTexturePVR() { CCLOGINFO( "cocos2d: deallocing CCTexturePVR" ); //释放OpenGL贴图 if (m_uName != 0 && ! m_bRetainName) { ccGLDeleteTexture(m_uName); } } //解压PVR像素数据 bool CCTexturePVR::unpackPVRData(unsigned char* data,unsigned int len) { bool success = false; PVRTexHeader *header = NULL; unsigned int flags,pvrTag; unsigned int dataLength = 0,dataOffset = 0,dataSize = 0; unsigned int blockSize = 0,widthBlocks = 0,heightBlocks = 0; unsigned int width = 0,height = 0,bpp = 4; unsigned char *bytes = NULL; unsigned int formatFlags; //将数据地址转为头信息指针,这个就可以直接通过结构指针进行存取。 header = (PVRTexHeader *)data; //格式有效性检查 pvrTag = CC_SWAP_INT32_LITTLE_TO_HOST(header->pvrTag); /* Check that given data really represents pvrtexture [0] = 'P' [1] = 'V' [2] = 'R' [3] = '!' */ if (gPVRTexIdentifier[0] != ((pvrTag >> 0) & 0xff) || gPVRTexIdentifier[1] != ((pvrTag >> 8) & 0xff) || gPVRTexIdentifier[2] != ((pvrTag >> 16) & 0xff) || gPVRTexIdentifier[3] != ((pvrTag >> 24) & 0xff)) { CCLOG("Unsupported PVR format. Use the Legacy format until the new format is supported"); return false; } //取得配置信息 CCConfiguration *configuration = CCConfiguration::sharedConfiguration(); //取得标记 flags = CC_SWAP_INT32_LITTLE_TO_HOST(header->flags); formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK; bool flipped = (flags & kPVRTextureFlagVerticalFlip) ? true : false; if (flipped) { CCLOG("cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool"); } //查看是否支持非2的幂次方大小的纹理 if (! configuration->supportsNPOT() && (header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height))) { CCLOG("cocos2d: ERROR: Loding an NPOT texture (%dx%d) but is not supported on this device",header->width,header->height); return false; } //遍历所有的格式信息数据,找到对应的格式信息。 for (m_uTableFormatIndex = 0; m_uTableFormatIndex < (unsigned int)MAX_TABLE_ELEMENTS; m_uTableFormatIndex++) { if (tableFormats[m_uTableFormatIndex][kCCInternalPVRTextureFormat] == formatFlags) { //Reset num of mipmaps m_uNumberOfMipmaps = 0; //取得图片大小 m_uWidth = width = CC_SWAP_INT32_LITTLE_TO_HOST(header->width); m_uHeight = height = CC_SWAP_INT32_LITTLE_TO_HOST(header->height); //检查是否有ALPHA通道 if (CC_SWAP_INT32_LITTLE_TO_HOST(header->bitmaskAlpha)) { m_bHasAlpha = true; } else { m_bHasAlpha = false; } //取得数据长度 dataLength = CC_SWAP_INT32_LITTLE_TO_HOST(header->dataLength); //将数据指针偏移到头信息之后,即像素数据所在位置 bytes = ((unsigned char *)data) + sizeof(PVRTexHeader); m_eFormat = (CCTexture2DPixelFormat)(tableFormats[m_uTableFormatIndex][kCCInternalCCTexture2DPixelFormat]); bpp = tableFormats[m_uTableFormatIndex][kCCInternalBPP]; // 遍历每个多级纹理图像块 while (dataOffset < dataLength) { switch (formatFlags) { case kPVRTexturePixelTypePVRTC_2: blockSize = 8 * 4; // Pixel by pixel block size for 2bpp widthBlocks = width / 8; heightBlocks = height / 4; break; case kPVRTexturePixelTypePVRTC_4: blockSize = 4 * 4; // Pixel by pixel block size for 4bpp widthBlocks = width / 4; heightBlocks = height / 4; break; case kPVRTexturePixelTypeBGRA_8888: if (CCConfiguration::sharedConfiguration()->supportsBGRA8888() == false) { CCLOG("cocos2d: TexturePVR. BGRA8888 not supported on this device"); return false; } default: blockSize = 1; widthBlocks = width; heightBlocks = height; break; } // Clamp to minimum number of blocks if (widthBlocks < 2) { widthBlocks = 2; } if (heightBlocks < 2) { heightBlocks = 2; } dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8); unsigned int packetLength = (dataLength - dataOffset); packetLength = packetLength > dataSize ? dataSize : packetLength; //记录每个多级纹理图像块的像素数据地址和长度。 m_asMipmaps[m_uNumberOfMipmaps].address = bytes + dataOffset; m_asMipmaps[m_uNumberOfMipmaps].len = packetLength; m_uNumberOfMipmaps++; //检查是否超出最大级数 CCAssert(m_uNumberOfMipmaps < CC_PVRMIPMAP_MAX,"TexturePVR: Maximum number of mimpaps reached. Increate the CC_PVRMIPMAP_MAX value"); //偏移到下一个多级纹理图像块。 dataOffset += packetLength; //大小减为原来1/2。 width = MAX(width >> 1,1); height = MAX(height >> 1,1); } //读取完成 success = true; break; } } //成功判断 if (! success) { CCLOG("cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%2x. Re-encode it with a OpenGL pixel format variant",formatFlags); } return success; } //创建OpenGL贴图 bool CCTexturePVR::createGLTexture() { //保存宽高 unsigned int width = m_uWidth; unsigned int height = m_uHeight; GLenum err; //如果文件中有多级纹理 if (m_uNumberOfMipmaps > 0) { //先释放原来的纹理 if (m_uName != 0) { ccGLDeleteTexture(m_uName); } //像素数据每字节对齐 glPixelStorei(GL_UNPACK_ALIGNMENT,1); //产生OpenGL贴图 glGenTextures(1,&m_uName); glBindTexture(GL_TEXTURE_2D,m_uName); // 如果无多级纹理,设置最小滤波方式为线性过滤,产生抗锯齿效果。 if (m_uNumberOfMipmaps == 1) { glTexParameteri(GL_TEXTURE_2D,GL_LINEAR); } else { //设置最小滤波方式为最近点采样,这种方式有锯齿。 glTexParameteri(GL_TEXTURE_2D,GL_LINEAR_MIPMAP_NEAREST); } //设置最大滤波方式为线性过滤 glTexParameteri(GL_TEXTURE_2D,GL_LINEAR); //设置贴图的横向纹理寻址模式为边缘截取模式。总是忽略边界。 glTexParameteri(GL_TEXTURE_2D,GL_CLAMP_TO_EDGE); //设置贴图的纵向纹理寻址模式为边缘截取模式。总是忽略边界。 glTexParameteri(GL_TEXTURE_2D,GL_CLAMP_TO_EDGE); } //检错 CHECK_GL_ERROR_DEBUG(); // clean possible GL error //取得对应格式的相关信息。 GLenum internalFormat = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLInternalFormat]; GLenum format = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLFormat]; GLenum type = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLType]; bool compressed = (0 == tableFormats[m_uTableFormatIndex][kCCInternalCompressedImage]) ? false : true; // 循环产生多级纹理贴图。 for (unsigned int i = 0; i < m_uNumberOfMipmaps; ++i) { //检查配置信息是否支持压缩格式。 if (compressed && ! CCConfiguration::sharedConfiguration()->supportsPVRTC()) { CCLOG("cocos2d: WARNING: PVRTC images are not supported"); return false; } //取得当前级别的图像块的像素数据地址和长度。 unsigned char *data = m_asMipmaps[i].address; unsigned int datalen = m_asMipmaps[i].len; //如果是压缩格式,则产生压缩格式的贴图。 if (compressed) { glCompressedTexImage2D(GL_TEXTURE_2D,i,internalFormat,datalen,data); } else {//产生一般格式的贴图。 glTexImage2D(GL_TEXTURE_2D,type,data); } //如果是非2的幂次方大小,则提示。 if (i > 0 && (width != height || ccNextPOT(width) != width )) { CCLOG("cocos2d: TexturePVR. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%u != height=%u",height); } //检错 err = glGetError(); if (err != GL_NO_ERROR) { CCLOG("cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X",err); return false; } //变为原来大小的1/2。 width = MAX(width >> 1,1); height = MAX(height >> 1,1); } return true; } //成员函数:加载PVR图片 bool CCTexturePVR::initWithContentsOfFile(const char* path) { unsigned char* pvrdata = NULL; int pvrlen = 0; //定义字符串变量,存储转换为小写的路径字符串。 std::string lowerCase(path); for (unsigned int i = 0; i < lowerCase.length(); ++i) { lowerCase[i] = tolower(lowerCase[i]); } //检查是否是ccz格式。 if (lowerCase.find(".ccz") != std::string::npos) { //调用压缩库函数将文件数据读取到为pvrdata申请的内存中。取得其大小存入pvrlen变量中。 pvrlen = ZipUtils::ccInflateCCZFile(path,&pvrdata); } //检查是否是gz格式。 else if (lowerCase.find(".gz") != std::string::npos) { //调用压缩库函数将文件数据读取到为pvrdata申请的内存中。取得其大小存入pvrlen变量中。 pvrlen = ZipUtils::ccInflateGZipFile(path,&pvrdata); } else { //普通PVR文件,将文件数据读取到一块内存中返回给pvrdata。取得其大小存入pvrlen变量中。 pvrdata = CCFileUtils::sharedFileUtils()->getFileData(path,"rb",(unsigned long *)(&pvrlen)); } //如果读取失败则返回 if (pvrlen < 0) { this->release(); return false; } m_uNumberOfMipmaps = 0; m_uName = 0; m_uWidth = m_uHeight = 0; m_bHasAlpha = false; m_bRetainName = false; //如果解压数据失败则返回。 //解压后,如果创建贴图失败则返回。 if (!unpackPVRData(pvrdata,pvrlen) || !createGLTexture()) { CC_SAFE_DELETE_ARRAY(pvrdata); this->release(); return false; } //释放数据占用的内存。 CC_SAFE_DELETE_ARRAY(pvrdata); return true; } //导出的静态函数:加载PVR图片 CCTexturePVR * CCTexturePVR::pvrTextureWithContentsOfFile(const char* path) { return CCTexturePVR::create(path); } //静态函数:加载PVR图片 CCTexturePVR * CCTexturePVR::create(const char* path) { //new 一个新的CCTexturePVR CCTexturePVR * pTexture = new CCTexturePVR(); if (pTexture) { //调用成员函数加载PVR图片 if (pTexture->initWithContentsOfFile(path)) { //如果成功,设置交由内存管理器使用引用计数器进行内存管理。 pTexture->autorelease(); } else { //否则释放纹理 delete pTexture; pTexture = NULL; } } //返回这个CCTexturePVR return pTexture; } NS_CC_END
三.CCTextureCache:
打开CCTextureCache.h:
#ifndef __CCTEXTURE_CACHE_H__ #define __CCTEXTURE_CACHE_H__ //由CCObject派生 #include "cocoa/CCObject.h" //需要用到字典 #include "cocoa/CCDictionary.h" #include "textures/CCTexture2D.h" #include <string> //这里用到CCImage类和STL容器之一list #if CC_ENABLE_CACHE_TEXTURE_DATA #include "platform/CCImage.h" #include <list> #endif //Cocos2d命名空间 NS_CC_BEGIN //用到线程锁 class CCLock; //用到CCImage处理图片 class CCImage; //纹理管理器 class CC_DLL CCTextureCache : public CCObject { protected: //字典对象指针。 CCDictionary* m_pTextures; //线程临界区。用于锁定字典访问,貌似用不到。这里屏蔽了~ //pthread_mutex_t *m_pDictLock; private: // 设置多线程加载图片时的回调函数。 void addImageAsyncCallBack(float dt); public: //构造函数 CCTextureCache(); //析构函数 virtual ~CCTextureCache(); //取得当前类描述 const char* description(void); //取得当前字典的快照(拷贝) CCDictionary* snapshotTextures(); //返回唯一纹理管理器的实例指针 static CCTextureCache * sharedTextureCache(); //销毁唯一纹理管理器的实例指针 static void purgeSharedTextureCache(); //加载一个图片生成纹理,文件名做为字典的查询对应关键字。返回生成的纹理指针,支持png,bmp,tiff,jpeg,pvr,gif等格式。 CCTexture2D* addImage(const char* fileimage); //此函数可以支持多线程载入图片,调用时会创建一个线程进行异步加载,加载成功后由主线程调用设置的回调函数,当然创建的纹理会做为参数传递。支持png和jpg void addImageAsync(const char *path,CCObject *target,SEL_CallFuncO selector); //加载一个图片生成纹理,指定参数key(其实要求是图片的相对路径字符串)做为字典的查询对应关键字。 CCTexture2D* addUIImage(CCImage *image,const char *key); //通过查询关键字(其实要求是图片的相对路径字符串)从字典里找到对应的纹理。 CCTexture2D* textureForKey(const char* key); //清空字典,释放所有纹理。 void removeAllTextures(); //清除未被外部使用的纹理。怎么知道未使用呢?因为在Cocos2d-x中使用“引用计数器”来管理各种资源的使用情况,纹理也不例外。在资源类构造时,纹理的计数器值为0,但由CCTextureCache来创建完成后,会对纹理的资源计数器做加1操作以通知纹理说“你现在被我占用呢”。如果纹理被外部使用,应该再次调用其资源计数器做加1操作,退出使用时做减1操作通知其“我现在不用你了”。所以这里只需要遍历下计数器值为1的纹理即未被外部使用的纹理进行释放即可。 void removeUnusedTextures(); //移除一个纹理 void removeTexture(CCTexture2D* texture); //由字典查询关键字找到相应纹理并移除。 void removeTextureForKey(const char *textureKeyName); //打印出当前管理的纹理信息,包括现在纹理占用的内存和总的纹理内存。 void dumpCachedTextureInfo(); #ifdef CC_SUPPORT_PVRTC //如果开启支持PVR的压缩格式,这里提供加载PVR压缩文件生成纹理的函数。 //参1:PVR压缩文件名 //参2:压缩质量参数,只能设为2或4,4比2质量高,但压缩比低。2则相反。 //参3:是否有Alpha通道。这里会根据是否有ALPHA通道以生成相应的默认纹理格式。 //参4:图片必须是2的幂次方大小的正方形,所以这里只需要填写一下宽度,也就是图片大小。 CCTexture2D* addPVRTCImage(const char* fileimage,int width); #endif // CC_SUPPORT_PVRTC //加载普通的PVR图片文件生成纹理。 CCTexture2D* addPVRImage(const char* filename); //如果CC_ENABLE_CACHE_TEXTURE_DATA宏定义为可用(即值为1),则调用此函数会将所有的纹理都预加载进内存生成相应纹理。 static void reloadAllTextures(); }; //如果定义了CC_ENABLE_CACHE_TEXTURE_DATA,这里定义一个新的类 #if CC_ENABLE_CACHE_TEXTURE_DATA //新定义的类名称为VolatileTexture,意思是多变纹理。这里代表了多种数据源生成的纹理的管理器。 class VolatileTexture { //这里声明了一个枚举,代表了多变纹理对应的几种数据源类型 typedef enum { kInvalid = 0,//无效未加载任何数据的状态 kImageFile,//图片文件 kImageData,//内存中的图片数据 kString,//字符串 kImage,//图片对象(CCImage) }ccCachedImageType; public: //构造 VolatileTexture(CCTexture2D *t); //析构 ~VolatileTexture(); //静态函数:通过图片文件生成的纹理及相关信息生成一个多变纹理并将其指针放入容器。 static void addImageTexture(CCTexture2D *tt,const char* imageFileName,CCImage::EImageFormat format); //静态函数:通过字符串生成的纹理及相关信息生成一个多变纹理并将其指针放入容器。 static void addStringTexture(CCTexture2D *tt,const char* text,CCTextAlignment alignment,float fontSize); //通过图片数据生成的纹理及相关信息生成一个多变纹理并将其指针放入容器。 static void addDataTexture(CCTexture2D *tt,void* data,const CCSize& contentSize); //通过图片对象生成的纹理及相关信息生成一个多变纹理并将其指针放入容器。 static void addCCImage(CCTexture2D *tt,CCImage *image); //通过纹理指针参数从容器中删除对应的多变纹理 static void removeTexture(CCTexture2D *t); //重新载入所有的纹理 static void reloadAllTextures(); public: //静态多变纹理指针容器,用来存储所有的多变纹理对象指针。 static std::list<VolatileTexture*> textures; //是否正在进行全部重新载入 static bool isReloading; private: //通过纹理指针参数从容器找到对应的多变纹理对象指针 static VolatileTexture* findVolotileTexture(CCTexture2D *tt); protected: //与当前多变纹理对应的纹理指针 CCTexture2D *texture; //对应的图片对象 CCImage *uiImage; //数据源类型 ccCachedImageType m_eCashedImageType; //纹理数据指针 void *m_pTextureData; //纹理的实际大小 CCSize m_TextureSize; //纹理的像素格式 CCTexture2DPixelFormat m_PixelFormat; //对应的图片名称 std::string m_strFileName; //图片的像素格式 CCImage::EImageFormat m_FmtImage; //图片的大小 CCSize m_size; //横向文字的对齐方式 CCTextAlignment m_alignment; //纵向文字的对齐方式 CCVerticalTextAlignment m_vAlignment; //字体名称 std::string m_strFontName; //文字 std::string m_strText; //字体大小 float m_fFontSize; }; #endif NS_CC_END #endif //__CCTEXTURE_CACHE_H__
然后是CPP文件:
#include "CCTextureCache.h"
#include "CCTexture2D.h"
#include "ccMacros.h"
#include "CCDirector.h"
#include "platform/platform.h"
#include "platform/CCFileUtils.h"
#include "platform/CCThread.h"
#include "platform/CCImage.h"
#include "support/ccUtils.h"
#include "CCScheduler.h"
#include "cocoa/CCString.h"
#include <errno.h>
#include <stack>
#include <string>
#include <cctype>
#include <queue>
#include <list>
#include <pthread.h>
#include <semaphore.h>
//使用标准库命名空间
using namespace std;
//使用Cocos2d命名空间
NS_CC_BEGIN
//异步加载所用的消息结构
typedef struct _AsyncStruct
{
std::string filename;//文件名
CCObject *target; //调用者
SEL_CallFuncO selector; //载回完的回调函数
} AsyncStruct;
//图片信息
typedef struct _ImageInfo
{
AsyncStruct *asyncStruct; //异步加载消息结构
CCImage *image; //图片指针
CCImage::EImageFormat imageType//图片类型
} ImageInfo;
//加载图片的线程
static pthread_t s_loadingThread;
//用于读取异步消息队列的线程临界区
static pthread_mutex_t s_asyncStructQueueMutex;
//用于存储图片信息结构处理的临界区
static pthread_mutex_t s_ImageInfoMutex;
//信号量指针。信号量是当前进程中的多个线程通信的一种方式。
static sem_t* s_pSem = NULL;
//多线程加载图片的数量。
static unsigned long s_nAsyncRefCount = 0;
//如果是IOS平台,则定义是否使用信号量命名。
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#define CC_ASYNC_TEXTURE_CACHE_USE_NAMED_SEMAPHORE 1
#else
#define CC_ASYNC_TEXTURE_CACHE_USE_NAMED_SEMAPHORE 0
#endif
//如果使用信号量命名,则定义命名的字符串宏,否则定义静态全局的信号量结构。
#if CC_ASYNC_TEXTURE_CACHE_USE_NAMED_SEMAPHORE
#define CC_ASYNC_TEXTURE_CACHE_SEMAPHORE "ccAsync"
#else
static sem_t s_sem;
#endif
//是否在当前加载线程处理完手上的活儿就退出。
static bool need_quit = false;
//异步加载图片的消息结构指针容器,即消息队列。
static std::queue<AsyncStruct*>* s_pAsyncStructQueue = NULL;
//异步存储图片信息结构指针的容器。
static std::queue<ImageInfo*>* s_pImageQueue = NULL;
//通过文件扩展名取得图片格式
static CCImage::EImageFormat computeImageFormatType(string& filename)
{
CCImage::EImageFormat ret = CCImage::kFmtUnKnown;
//JPG
if ((std::string::npos != filename.find(".jpg")) || (std::string::npos != filename.find(".jpeg")))
{
ret = CCImage::kFmtJpg;
}//PNG
else if ((std::string::npos != filename.find(".png")) || (std::string::npos != filename.find(".PNG")))
{
ret = CCImage::kFmtPng;
}//TIFF
else if ((std::string::npos != filename.find(".tiff")) || (std::string::npos != filename.find(".TIFF")))
{
ret = CCImage::kFmtTiff;
}
return ret;
}
//加载图片的线程函数
static void* loadImage(void* data)
{
// 创建一个线程信息对象
CCThread thread;
thread.createAutoreleasePool();
//多线程加载消息结构
AsyncStruct *pAsyncStruct = NULL;
//线程将处理所有随时要进行多线程加载的图片,所以会有一个While循环。
while (true)
{
//当前线程等待信号量变为非零值,并减1。
int semWaitRet = sem_wait(s_pSem);
//如果信号量为负值,打印出错信息并中断。
if( semWaitRet < 0 )
{
CCLOG( "CCTextureCache async thread semaphore error: %s\n",strerror( errno ) );
break;
}
//取得全局的异步加载信息结构指针容器
std::queue<AsyncStruct*> *pQueue = s_pAsyncStructQueue;
//下面代码作为临界区上锁
pthread_mutex_lock(&s_asyncStructQueueMutex);
//如果没有需要异步加载的图片
if (pQueue->empty())
{
//解锁临界区
pthread_mutex_unlock(&s_asyncStructQueueMutex);
//如果退出线程标记为true则中断退出,否则继续。
if (need_quit)
break;
else
continue;
}
else
{
//如果有需要异步加载的图片,则从队列中取第一个消息指针保存在变量pAsyncStruct中随后将其从队列中移除。
pAsyncStruct = pQueue->front();
pQueue->pop();
//解锁临界区
pthread_mutex_unlock(&s_asyncStructQueueMutex);
}
//取得要进行异步加载的图片名称
const char *filename = pAsyncStruct->filename.c_str();
//取得图片类型
CCImage::EImageFormat imageType = computeImageFormatType(pAsyncStruct->filename);
//如果不是PNG,JPG或TIFF就不支持了。打印错误后进行相应处理。
if (imageType == CCImage::kFmtUnKnown)
{
CCLOG("unsupportted format %s",filename);
delete pAsyncStruct;
continue;
}
// 如果是有效格式,生成一个新的CCImage对象
CCImage *pImage = new CCImage();
// 由文件名和图片格式将图片加载到CCImage中。
if (! pImage->initWithImageFileThreadSafe(filename,imageType))
{ //如果失败,释放CCImage对象并打印错误。
delete pImage;
CCLOG("can not load %s",filename);
continue;
}
// 动态创建一个新的图片信息结构并填充相应信息
ImageInfo *pImageInfo = new ImageInfo();
pImageInfo->asyncStruct = pAsyncStruct;
pImageInfo->image = pImage;
pImageInfo->imageType = imageType;
//下面代码作为临界区上锁
pthread_mutex_lock(&s_ImageInfoMutex);
//将新的图片信息放入图片信息结构容器。
s_pImageQueue->push(pImageInfo);
//解锁临界区
pthread_mutex_unlock(&s_ImageInfoMutex);
}
//如果退出循环,释放信号量
if( s_pSem != NULL )
{
#if CC_ASYNC_TEXTURE_CACHE_USE_NAMED_SEMAPHORE
sem_unlink(CC_ASYNC_TEXTURE_CACHE_SEMAPHORE);
sem_close(s_pSem);
#else
sem_destroy(s_pSem);
#endif
s_pSem = NULL;
//释放多线程加载所用的消息队列和与之对应的图片信息队列。
delete s_pAsyncStructQueue;
delete s_pImageQueue;
}
return 0;
}
// 唯一的全局纹理数据缓冲区对象指针
static CCTextureCache *g_sharedTextureCache = NULL;
//取得唯一的全局纹理数据缓冲区对象指针
CCTextureCache * CCTextureCache::sharedTextureCache()
{
if (!g_sharedTextureCache)
{
g_sharedTextureCache = new CCTextureCache();
}
return g_sharedTextureCache;
}
//构造函数
CCTextureCache::CCTextureCache()
{
CCAssert(g_sharedTextureCache == NULL,"Attempted to allocate a second instance of a singleton.");
//生成一个字典
m_pTextures = new CCDictionary();
}
//析构函数
CCTextureCache::~CCTextureCache()
{
CCLOGINFO("cocos2d: deallocing CCTextureCache.");
need_quit = true;
if (s_pSem != NULL)
{
sem_post(s_pSem);
}
//释放字典
CC_SAFE_RELEASE(m_pTextures);
}
//释放唯一的全局纹理数据缓冲区对象
void CCTextureCache::purgeSharedTextureCache()
{
CC_SAFE_RELEASE_NULL(g_sharedTextureCache);
}
//取得当前类描述
const char* CCTextureCache::description()
{
return CCString::createWithFormat("<CCTextureCache | Number of textures = %u>",m_pTextures->count())->getCString();
}
//取得当前字典的快照
CCDictionary* CCTextureCache::snapshotTextures()
{
//动态创建一个新的字典
CCDictionary* pRet = new CCDictionary();
CCDictElement* pElement = NULL;
//遍历原来字典将数据填充到新字典中
CCDICT_FOREACH(m_pTextures,pElement)
{
pRet->setObject(pElement->getObject(),pElement->getStrKey());
}
return pRet;
}
//使用多线程载入图片。
//参1:图片相对路径名。
//参2:载入完成后要通知的对象。
//参3:载入完成后要通知对象调用的函数。
void CCTextureCache::addImageAsync(const char *path,SEL_CallFuncO selector)
{
//文件名不能为空
CCAssert(path != NULL,"TextureCache: fileimage MUST not be NULL");
//定义一个纹理指针并置空
CCTexture2D *texture = NULL;
//创建字符串pathKey做为字典查询关键字。
std::string pathKey = path;
//取得图片所在位置的全路径名
pathKey = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(pathKey.c_str());
//先查询一下是否字典里已经有了此纹理。
texture = (CCTexture2D*)m_pTextures->objectForKey(pathKey.c_str());
std::string fullpath = pathKey;
//如果已经有了,则直接把纹理做为参数调用要通知的对象的函数。
if (texture != NULL)
{
if (target && selector)
{
(target->*selector)(texture);
}
return;
}
//如果是第一次调用多线程载入,创建信号量并进行相应初始化。
if (s_pSem == NULL)
{
//判断是否使用信号量命名,如果是,创建一个信号量返回其地址给指针s_pSem。
#if CC_ASYNC_TEXTURE_CACHE_USE_NAMED_SEMAPHORE
s_pSem = sem_open(CC_ASYNC_TEXTURE_CACHE_SEMAPHORE,O_CREAT,0644,0);
if( s_pSem == SEM_Failed )
{
CCLOG( "CCTextureCache async thread semaphore init error: %s\n",strerror( errno ) );
s_pSem = NULL;
return;
}
#else
//如果不使用信号量命名,直接用sem_init来初始化信号量对象s_sem。
int semInitRet = sem_init(&s_sem,0);
if( semInitRet < 0 )
{
//如果失败,打印出错并退出。
CCLOG( "CCTextureCache async thread semaphore init error: %s\n",strerror( errno ) );
return;
}
//如果成功,将信号量对象地址给指针s_pSem。
s_pSem = &s_sem;
#endif
//建立加载消息队列
s_pAsyncStructQueue = new queue<AsyncStruct*>();
//建立加载的图片信息结构队列
s_pImageQueue = new queue<ImageInfo*>();
//线程锁初始化
pthread_mutex_init(&s_asyncStructQueueMutex,NULL);
pthread_mutex_init(&s_ImageInfoMutex,NULL);
//创建加载线程。
pthread_create(&s_loadingThread,NULL,loadImage,NULL);
//将退出指令设为false。
need_quit = false;
}
//多线程加载图片的引用计数器如果为0,
if (0 == s_nAsyncRefCount)
{
//将addImageAsyncCallBack函数加入显示设备上的回调函数处理器中。
CCDirector::sharedDirector()->getScheduler()->scheduleSelector(schedule_selector(CCTextureCache::addImageAsyncCallBack),this,false);
}
//计数器加1
++s_nAsyncRefCount;
//
if (target)
{
target->retain();
}
// 产生一个新的加载消息,放入加载消息队列中。
AsyncStruct *data = new AsyncStruct();
data->filename = fullpath.c_str();
data->target = target;
data->selector = selector;
// 当然,放入时得锁一下,放入后再解锁。
pthread_mutex_lock(&s_asyncStructQueueMutex);
s_pAsyncStructQueue->push(data);
pthread_mutex_unlock(&s_asyncStructQueueMutex);
//给信号量加1,sem_post是原子操作,即多个线程同时调用并不会产生冲突。
sem_post(s_pSem);
}
//多线程加载图片时的回调函数。
void CCTextureCache::addImageAsyncCallBack(float dt)
{
// 取得多线程加载的图片信息队列
std::queue<ImageInfo*> *imagesQueue = s_pImageQueue;
//下面代码作为临界区上锁
pthread_mutex_lock(&s_ImageInfoMutex);
//如果图片信息队列为空直接解锁,否则进行处理
if (imagesQueue->empty())
{
pthread_mutex_unlock(&s_ImageInfoMutex);
}
else
{
//取出图片信息队列的头一个信息从队列中弹出。
ImageInfo *pImageInfo = imagesQueue->front();
imagesQueue->pop();
//解锁临界区
pthread_mutex_unlock(&s_ImageInfoMutex);
//取得信息中的加载消息。
AsyncStruct *pAsyncStruct = pImageInfo->asyncStruct;
//取得图片信息中的CCImage指针。
CCImage *pImage = pImageInfo->image;
//取得加载完成后要通知的对象以及要调用的函数。
CCObject *target = pAsyncStruct->target;
SEL_CallFuncO selector = pAsyncStruct->selector;
//取得图片文件名
const char* filename = pAsyncStruct->filename.c_str();
// 新建一个纹理。
CCTexture2D *texture = new CCTexture2D();
//使用CCImage指针pImage来初始化纹理生成OpenGL贴图。
#if 0 //TODO: (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
texture->initWithImage(pImage,kCCResolutioniPhone);
#else
texture->initWithImage(pImage);
#endif
#if CC_ENABLE_CACHE_TEXTURE_DATA
//使用纹理和图片信息生成相应的可变纹理
VolatileTexture::addImageTexture(texture,filename,pImageInfo->imageType);
#endif
//使用文件名做为查询关键字将纹理存入字典
m_pTextures->setObject(texture,filename);
texture->autorelease();
//调用通知目标的相应函数。
if (target && selector)
{
(target->*selector)(texture);
target->release();
}
//释放CCImage对象。
pImage->release();
//释放new出来的消息结构和图片信息结构。
delete pAsyncStruct;
delete pImageInfo;
//多线程加载引用计数器减1,
--s_nAsyncRefCount;
if (0 == s_nAsyncRefCount)
{
//从显示设备上的回调函数处理器中移除加载回调函数。
CCDirector::sharedDirector()->getScheduler()->unscheduleSelector(schedule_selector(CCTextureCache::addImageAsyncCallBack),this);
}
}
}
//加载一个图片生成纹理。
CCTexture2D * CCTextureCache::addImage(const char * path)
{
//参数有效性判断
CCAssert(path != NULL,"TextureCache: fileimage MUST not be NULL");
//定义纹理指针变量并置空
CCTexture2D * texture = NULL;
//非多线程,故屏蔽,如果addImageAsync在其它线程调用此函数,则打开这段代码。
//pthread_mutex_lock(m_pDictLock);
//创建字符串pathKey做为字典查询关键字。
std::string pathKey = path;
//取得图片所在位置的全路径名
pathKey = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(pathKey.c_str());
//用pathKey查询字典中是否有此纹理。
texture = (CCTexture2D*)m_pTextures->objectForKey(pathKey.c_str());
//新建字符串fullpath存储全路径。
std::string fullpath = pathKey;
//如果没有找到,
if( ! texture )
{
//将文件路径放入新字符串lowerCase。并将字符串中的所有字母转为小写。
std::string lowerCase(path);
for (unsigned int i = 0; i < lowerCase.length(); ++i)
{
lowerCase[i] = tolower(lowerCase[i]);
}
//
do
{
//如果字符串能够找到".pvr",则代表是pvr文件。调用相应函数将其载入。
if (std::string::npos != lowerCase.find(".pvr"))
{
texture = this->addPVRImage(fullpath.c_str());
}
else
{
//否则分别取得文件的格式。
CCImage::EImageFormat eImageFormat = CCImage::kFmtUnKnown;
if (std::string::npos != lowerCase.find(".png"))
{
eImageFormat = CCImage::kFmtPng;
}
else if (std::string::npos != lowerCase.find(".jpg") || std::string::npos != lowerCase.find(".jpeg"))
{
eImageFormat = CCImage::kFmtJpg;
}
else if (std::string::npos != lowerCase.find(".tif") || std::string::npos != lowerCase.find(".tiff"))
{
eImageFormat = CCImage::kFmtTiff;
}
//创建CCImage对象,从文件中读取文件的数据并初始化CCImage对象。
CCImage image;
//取得文件大小
unsigned long nSize = 0;
//读入数据返回给BYTE类型指针。
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullpath.c_str(),&nSize);
//使用读入的数据初始化相应的图片对象。
CC_BREAK_IF(! image.initWithImageData((void*)pBuffer,nSize,eImageFormat));
//读完后释放数据占用的内存。
CC_SAFE_DELETE_ARRAY(pBuffer);
//创建一个纹理。
texture = new CCTexture2D();
//使用图片对象创建纹理。
if( texture &&
texture->initWithImage(&image) )
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
// 使用图片对象生成可变纹理
VolatileTexture::addImageTexture(texture,fullpath.c_str(),eImageFormat);
#endif
//利用路径名做为查询关键字将纹理存入字典。
m_pTextures->setObject(texture,pathKey.c_str());
//计数器减1。则刚存入字典的纹理的引用计数器值标记为尚未使用。
texture->release();
}
else
{
//失败则打印错误。
CCLOG("cocos2d: Couldn't add image:%s in CCTextureCache",path);
}
}
} while (0);
}
//与上面屏蔽加锁一样,这里屏蔽解锁。
//pthread_mutex_unlock(m_pDictLock);
return texture;
}
//如果支持PVR的压缩格式。
#ifdef CC_SUPPORT_PVRTC
//加载一个PVR的压缩格式的图片。
CCTexture2D* CCTextureCache::addPVRTCImage(const char* path,int width)
{
//参数有效性判断
CCAssert(path != NULL,"TextureCache: fileimage MUST not be nill");
//压缩类型参数有效性判断,只能为2或4
CCAssert( bpp==2 || bpp==4,"TextureCache: bpp must be either 2 or 4");
//定义一个新的纹理指针
CCTexture2D * texture;
//定义临时字符串存储相对路径名。
std::string temp(path);
//查询字典中是否已经有此纹理。有则取得并直接返回纹理。
if ( (texture = (CCTexture2D*)m_pTextures->objectForKey(temp.c_str())) )
{
return texture;
}
// 取得文件的全路径字符串。
std::string fullpath( CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(path) );
//新建变量nLeng用于存储读取到的数据大小。初始化为0。
unsigned long nLen = 0;
//新建字符指针pData用于存储读取到的数据并实际读取文件数据。
unsigned char* pData = CCFileUtils::sharedFileUtils()->getFileData(fullpath.c_str(),&nLen);
//新建创一个纹理。
texture = new CCTexture2D();
//使用读取到的数据创建纹理。
if( texture->initWithPVRTCData(pData,bpp,hasAlpha,(bpp==2 ? kCCTexture2DPixelFormat_PVRTC2 : kCCTexture2DPixelFormat_PVRTC4)))
{
//将纹理以文件名做为关键字存入字典。
m_pTextures->setObject(texture,temp.c_str());
//将纹理交由内存管理器处理。
texture->autorelease();
}
else
{
//如果创建失败或读取PVR文件失败,打印错误日志。\
CCLOG("cocos2d: Couldn't add PVRTCImage:%s in CCTextureCache",path);
}
//释放读取文件的数据所占用的内存。
CC_SAFE_DELETE_ARRAY(pData);
return texture;
}
#endif // CC_SUPPORT_PVRTC
//加载一个普通的PVR图片文件
CCTexture2D * CCTextureCache::addPVRImage(const char* path)
{
//文件名参数有效性判断。
CCAssert(path != NULL,"TextureCache: fileimage MUST not be nill");
//新建纹理指针变量置空。
CCTexture2D* texture = NULL;
//定义临时字符串存储相对路径名。
std::string key(path);
//先使用文件名查询是否字典中已经有此纹理了。如果有直接取得并返回纹理。
if( (texture = (CCTexture2D*)m_pTextures->objectForKey(key.c_str())) )
{
return texture;
}
// 由文件名字符串取得图片的全路径字符串。
std::string fullpath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(key.c_str());
//动态创建一个纹理。
texture = new CCTexture2D();
//如果创建成功,则读取相应的PVR文件来初始化纹理。
if(texture != NULL && texture->initWithPVRFile(fullpath.c_str()) )
{
//初始化成功。
#if CC_ENABLE_CACHE_TEXTURE_DATA
// 使用纹理和图片信息生成可变纹理。
VolatileTexture::addImageTexture(texture,CCImage::kFmtRawData);
#endif
//将纹理以文件名做为查询关键字存入字典。
m_pTextures->setObject(texture,key.c_str());
texture->autorelease();
}
else
{
//如果创建失败或读取PVR文件失败,打印错误日志。
CCLOG("cocos2d: Couldn't add PVRImage:%s in CCTextureCache",key.c_str());
CC_SAFE_DELETE(texture);
}
return texture;
}
//加载一个图片生成纹理,指定参数key做为字典的查询对应关键字。
CCTexture2D* CCTextureCache::addUIImage(CCImage *image,const char *key)
{
//参数有效性判断
CCAssert(image != NULL,"TextureCache: image MUST not be nill");
//定义纹理指针变量texure做为返回值。这里初始化为空。
CCTexture2D * texture = NULL;
//定义字符串变量forKey用来存储完整的图片路径名称。
std::string forKey;
if (key)
{
//取得文件名所对应的全路径名,呵呵,这个key也还是个相对路径名啊。
forKey = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(key);
}
do
{
// 查询字典是否已经有此纹理了。如果有,取出纹理返回给texture,中断退出。
if(key && (texture = (CCTexture2D *)m_pTextures->objectForKey(forKey.c_str())))
{
break;
}
//动态创建一个纹理对象.返回给texture。
texture = new CCTexture2D();
//使用image来初始化纹理.注意:这一句应该移到下面的if中。
texture->initWithImage(image);
//初始化完成后以路径名做为查询关键字将纹理存入字典。
if(key && texture)
{
m_pTextures->setObject(texture,forKey.c_str());
texture->autorelease();
}
else
{
//如果key为空或texture为空打印错误
CCLOG("cocos2d: Couldn't add UIImage in CCTextureCache");
}
} while (0);
#if CC_ENABLE_CACHE_TEXTURE_DATA
//使用纹理和CCImage对象生成可变纹理。
VolatileTexture::addCCImage(texture,image);
#endif
return texture;
}
//清空字典,释放所有纹理。
void CCTextureCache::removeAllTextures()
{
m_pTextures->removeAllObjects();
}
//清除未被外部使用的纹理
void CCTextureCache::removeUnusedTextures()
{
/*原来的做法,因为有问题给屏蔽了,仍然解释下:
//定义字典词汇指针变量pElement。
CCDictElement* pElement = NULL;
//遍历字典
CCDICT_FOREACH(m_pTextures,pElement)
{
//打印词汇信息
CCLOG("cocos2d: CCTextureCache: texture: %s",pElement->getStrKey());
//取得词汇对应的纹理
CCTexture2D *value = (CCTexture2D*)pElement->getObject();
//如果引用计数器值为1,从字典中删除。
if (value->retainCount() == 1)
{
CCLOG("cocos2d: CCTextureCache: removing unused texture: %s",pElement->getStrKey());
m_pTextures->removeObjectForElememt(pElement);
}
}
*/
//现在的做法
// 判断字典不为空
if (m_pTextures->count())
{
//定义字典词汇指针变量pElement。
CCDictElement* pElement = NULL;
//定义一个list容器用来存储未被外部使用的纹理指针。
list<CCDictElement*> elementToRemove;
//遍历字典
CCDICT_FOREACH(m_pTextures,pElement)
{
//打印词汇信息
CCLOG("cocos2d: CCTextureCache: texture: %s",pElement->getStrKey());
//取得词汇对应的纹理
CCTexture2D *value = (CCTexture2D*)pElement->getObject();
if (value->retainCount() == 1)
{
//如果引用计数器值为1,先存入容器中。
elementToRemove.push_back(pElement);
}
}
// 遍历list中的元素从字典中删除
for (list<CCDictElement*>::iterator iter = elementToRemove.begin(); iter != elementToRemove.end(); ++iter)
{
//打印删除元素日志。
CCLOG("cocos2d: CCTextureCache: removing unused texture: %s",(*iter)->getStrKey());
//从字典中删除
m_pTextures->removeObjectForElememt(*iter);
}
}
//好吧,答案是因为CCDICT_FOREACH和removeObjectForElememt会互相影响,CCDICT_FOREACH中会调用HASH_ITER循环遍历。而循环的计数器是位置,通过地址对比来找下一个结点位置。而removeObjectForElememt会调用HASH_DELETE删除元素导致链表的重构。重构后会影响到HASK_ITER的查询。
}
//移除一个纹理
void CCTextureCache::removeTexture(CCTexture2D* texture)
{
//参数有效性判断
if( ! texture )
{
return;
}
//查询所有对应此纹理的词汇
CCArray* keys = m_pTextures->allKeysForObject(texture);
//从字典中把这些词汇及相应纹理删除。
m_pTextures->removeObjectsForKeys(keys);
}
//由字典查询关键字找到相应纹理并移除。
void CCTextureCache::removeTextureForKey(const char *textureKeyName)
{
//参数有效性判断
if (textureKeyName == NULL)
{
return;
}
//查询关键字实际是文件的相对路径,这里取得全路径。
string fullPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(textureKeyName);
//将全路径做为查询关键字从字典中删除相应词汇及纹理
m_pTextures->removeObjectForKey(fullPath.c_str());
}
//由字典查询关键字找到相应纹理
CCTexture2D* CCTextureCache::textureForKey(const char* key)
{
return (CCTexture2D*)m_pTextures->objectForKey(CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(key));
}
//重新载入所有的纹理
void CCTextureCache::reloadAllTextures()
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
//调用可变纹理的静态函数重新载入所有的纹理
VolatileTexture::reloadAllTextures();
#endif
}
//打印字典中的纹理统计信息。
void CCTextureCache::dumpCachedTextureInfo()
{
unsigned int count = 0;
unsigned int totalBytes = 0;
CCDictElement* pElement = NULL;
//遍历字典中的所有词汇信息
CCDICT_FOREACH(m_pTextures,pElement)
{
//取得词汇对应的纹理
CCTexture2D* tex = (CCTexture2D*)pElement->getObject();
//取得纹理对应贴图的色深
unsigned int bpp = tex->bitsPerPixelForFormat();
// 生成贴图占用的内存大小(字节数量)
unsigned int bytes = tex->getPixelsWide() * tex->getPixelsHigh() * bpp / 8;
// 统计内存总大小
totalBytes += bytes;
count++;
//打印纹理信息
CCLOG("cocos2d: \"%s\" rc=%lu id=%lu %lu x %lu @ %ld bpp => %lu KB",pElement->getStrKey(),//查询关键字
(long)tex->retainCount(),//使用次数
(long)tex->getName(),//图片名称
(long)tex->getPixelsWide(),//对应贴图的宽度
(long)tex->getPixelsHigh(),//对应贴图的高度
(long)bpp,//对应贴图色深
(long)bytes / 1024); //占用内存大小(千字节数量)
}
//打印总的数量数量,占用内存数量。
CCLOG("cocos2d: CCTextureCache dumpDebugInfo: %ld textures,for %lu KB (%.2f MB)",(long)count,(long)totalBytes / 1024,totalBytes / (1024.0f*1024.0f));
}
//如果开启了使用多变纹理
#if CC_ENABLE_CACHE_TEXTURE_DATA
//定义全局的list容器,用来存储产生的多变纹理对象指针
std::list<VolatileTexture*> VolatileTexture::textures;
//定义布尔变量标记是否在全部重新载入
bool VolatileTexture::isReloading = false;
//构造函数
VolatileTexture::VolatileTexture(CCTexture2D *t)
: texture(t),m_eCashedImageType(kInvalid),m_pTextureData(NULL),m_PixelFormat(kTexture2DPixelFormat_RGBA8888),m_strFileName(""),m_FmtImage(CCImage::kFmtPng),m_alignment(kCCTextAlignmentCenter),m_vAlignment(kCCVerticalTextAlignmentCenter),m_strFontName(""),m_strText(""),uiImage(NULL),m_fFontSize(0.0f)
{
m_size = CCSizeMake(0,0);
textures.push_back(this);
}
//析构函数
VolatileTexture::~VolatileTexture()
{
textures.remove(this);
CC_SAFE_RELEASE(uiImage);
}
//通过纹理图片属性信息生成可变纹理。
void VolatileTexture::addImageTexture(CCTexture2D *tt,CCImage::EImageFormat format)
{
//如果正在重新载入过程中,直接返回。
if (isReloading)
{
return;
}
//通过纹理指针找到相应的可变纹理,如果没有则new一个返回其指针。
VolatileTexture *vt = findVolotileTexture(tt);
//设置相关属性,注意:这里最好对vt做下有效性检查,如果为NULL的话会崩溃的。
vt->m_eCashedImageType = kImageFile;
vt->m_strFileName = imageFileName;
vt->m_FmtImage = format;
vt->m_PixelFormat = tt->getPixelFormat();
}
//通过CCImage对象生成可变纹理。
void VolatileTexture::addCCImage(CCTexture2D *tt,CCImage *image)
{
//通过纹理指针找到相应的可变纹理,如果没有则new一个返回其指针。
VolatileTexture *vt = findVolotileTexture(tt);
image->retain();
//设置相关属性
vt->uiImage = image;
vt->m_eCashedImageType = kImage;
}
//通过纹理指针找到相应的可变纹理,如果没有则new出一个返回。
VolatileTexture* VolatileTexture::findVolotileTexture(CCTexture2D *tt)
{
VolatileTexture *vt = 0;
//遍历list容器,对比查询。
std::list<VolatileTexture *>::iterator i = textures.begin();
while (i != textures.end())
{
VolatileTexture *v = *i++;
if (v->texture == tt)
{
vt = v;
break;
}
}
//如果没有找到,则由纹理参数new出一个可变纹理,new会调用其带参数的拷贝构造函数设置其对应纹理。
if (! vt)
{
vt = new VolatileTexture(tt);
}
return vt;
}
//通过指定图像数据,像素格式和图片大小来生成可变纹理。
void VolatileTexture::addDataTexture(CCTexture2D *tt,const CCSize& contentSize)
{
//如果正在重新载入过程中,直接返回。
if (isReloading)
{
return;
}
//通过纹理指针找到相应的可变纹理,如果没有则new一个返回其指针。
VolatileTexture *vt = findVolotileTexture(tt);
//设置相关属性
vt->m_eCashedImageType = kImageData;
vt->m_pTextureData = data;
vt->m_PixelFormat = pixelFormat;
vt->m_TextureSize = contentSize;
}
//由字符串和相应信息生成可变纹理
void VolatileTexture::addStringTexture(CCTexture2D *tt,float fontSize)
{
//如果正在重新载入过程中,直接返回。
if (isReloading)
{
return;
}
//通过纹理指针找到相应的可变纹理,如果没有则new一个返回其指针。
VolatileTexture *vt = findVolotileTexture(tt);
//设置相关属性
vt->m_eCashedImageType = kString;
vt->m_size = dimensions;
vt->m_strFontName = fontName;
vt->m_alignment = alignment;
vt->m_vAlignment = vAlignment;
vt->m_fFontSize = fontSize;
vt->m_strText = text;
}
//通过纹理指针找到相应的可变纹理并删除。
void VolatileTexture::removeTexture(CCTexture2D *t)
{
std::list<VolatileTexture *>::iterator i = textures.begin();
while (i != textures.end())
{
VolatileTexture *vt = *i++;
if (vt->texture == t)
{
delete vt;
break;
}
}
}
//重新载入所有的纹理。
void VolatileTexture::reloadAllTextures()
{
//设置开始进行重新载入所有纹理。
isReloading = true;
CCLOG("reload all texture");
//通过迭代器遍历list容器
std::list<VolatileTexture *>::iterator iter = textures.begin();
while (iter != textures.end())
{
VolatileTexture *vt = *iter++;
//根据不同的格式进行纹理的重建
switch (vt->m_eCashedImageType)
{
case kImageFile:
{
//这里定义一个CCImage对象image
CCImage image;
//先将路径名都变成小写字符串。
std::string lowerCase(vt->m_strFileName.c_str());
for (unsigned int i = 0; i < lowerCase.length(); ++i)
{
lowerCase[i] = tolower(lowerCase[i]);
}
//扩展名对比,如果是PVR文件
if (std::string::npos != lowerCase.find(".pvr"))
{
//取得原来的默认带ALPHA通道的像素格式。
CCTexture2DPixelFormat oldPixelFormat = CCTexture2D::defaultAlphaPixelFormat();
//重设默认带ALPHA通道的像素格式。
CCTexture2D::setDefaultAlphaPixelFormat(vt->m_PixelFormat);
//纹理重新由PVR文件进行初始化。会用到新的默认带ALPHA通道的像素格式。
vt->texture->initWithPVRFile(vt->m_strFileName.c_str());
//重设原来的默认带ALPHA通道的像素格式。
CCTexture2D::setDefaultAlphaPixelFormat(oldPixelFormat);
}
else
{
//如果是非PVR文件。
unsigned long nSize = 0;
//通过文件工具集中的接口读入图片文件并返回数据地址。
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(vt->m_strFileName.c_str(),&nSize);
//使用数据地址对前面定义的CCImage对象image进行初始化。
if (image.initWithImageData((void*)pBuffer,vt->m_FmtImage))
{
//取得原来的默认带ALPHA通道的像素格式。
CCTexture2DPixelFormat oldPixelFormat = CCTexture2D::defaultAlphaPixelFormat();
//重设默认带ALPHA通道的像素格式。CCTexture2D::setDefaultAlphaPixelFormat(vt->m_PixelFormat);
//纹理重新由图片对象初始化。会用到新的默认带ALPHA通道的像素格式。
vt->texture->initWithImage(&image);
//重设原来的默认带ALPHA通道的像素格式。
CCTexture2D::setDefaultAlphaPixelFormat(oldPixelFormat);
}
CC_SAFE_DELETE_ARRAY(pBuffer);
}
}
break;
case kImageData:
{
//纹理重新由图片数据初始化。
vt->texture->initWithData(vt->m_pTextureData,vt->m_PixelFormat,vt->m_TextureSize.width,vt->m_TextureSize.height,vt->m_TextureSize);
}
break;
case kString:
{
//纹理重新由字符串初始化。
vt->texture->initWithString(vt->m_strText.c_str(),vt->m_size,vt->m_alignment,vt->m_vAlignment,vt->m_strFontName.c_str(),vt->m_fFontSize);
}
break;
case kImage:
{
//纹理重新由图片对象初始化。
vt->texture->initWithImage(vt->uiImage);
}
break;
default:
break;
}
}
//设置重新载入完成
isReloading = false;
}
#endif // CC_ENABLE_CACHE_TEXTURE_DATA
NS_CC_END
四.CCTextureAtlas:
这个类在之前的博文:
Cocos2d-x中图字原理之深入分析
http://blog.csdn.net/honghaier/article/details/7931914#comments
已经有介绍过。与当前处理本改变不大。在此不再详细赘述。有兴趣的朋友可以自已研究。
原文地址:http://blog.csdn.net/honghaier/article/details/8068895