CCTextureCache类源码分析(1):
1、 CCTextureCache类:
这个类跟纹理缓存有关,我们跟着代码分析下这个类是怎么对纹理进行缓存的。
/** Returns the shared instance of the cache
* 单例
*/
static CCTextureCache * sharedTextureCache();
2、我们在创建精灵的时候会调用
CCTextureCache::sharedTextureCache()->addImage(pszFilename);
方法,加载精灵的纹理。
addImage --函数分析:
CCTexture2D * CCTextureCache::addImage(const char * path)
{
CCAssert(path != NULL, "TextureCache: fileimage MUST not be NULL");
CCTexture2D * texture = NULL;
CCImage* pImage = NULL;
// Split up directory and filename
// MUTEX:
// Needed since addImageAsync calls this method from a different thread
//pthread_mutex_lock(m_pDictLock);
std::string pathKey = path;
//获取文件的全路径
pathKey = CCFileUtils::sharedFileUtils()->fullPathForFilename(pathKey.c_str());
if (pathKey.size() == 0)
{
return NULL;
}
//CCDictionary* m_pTextures; 成员变量,字典类型,用来存放已经加载的纹理
//如果某个纹理加载过,这就会存放到这个字典中,那么下次就直接从这个
//字典中找到就可以了
texture = (CCTexture2D*)m_pTextures->objectForKey(pathKey.c_str());
std::string fullpath = pathKey; // (CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(path));
if (! texture) //如果这个纹理是个新的纹理(以前没有加载过,则需要加载)
{
//把pathKey即纹理的全路径转化为小写
std::string lowerCase(pathKey);
for (unsigned int i = 0; i < lowerCase.length(); ++i)
{
lowerCase[i] = tolower(lowerCase[i]);
}
// all images are handled by UIImage except PVR extension that is handled by our own handler
do
{
//如果纹理格式是.pvr,这是用addPVRImage方法加载,后面分析
if (std::string::npos != lowerCase.find(".pvr"))
{
texture = this->addPVRImage(fullpath.c_str());
}
//如果纹理格式是.pkm,这是用addETCImage方法加载,后面分析
else if (std::string::npos != lowerCase.find(".pkm"))
{
// ETC1 file format, only supportted on Android
texture = this->addETCImage(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;
}
else if (std::string::npos != lowerCase.find(".webp"))
{
eImageFormat = CCImage::kFmtWebp;
}
//CCImage解析各种格式图片的类,各个平台下有些实现方法不同
//跟平台相关,这个类也后面分析,这里只需要知道,这个类会根据不同
//的图片格式,去解析图片数据,把图片数据加载到内存中,继承子CCObject
pImage = new CCImage();
CC_BREAK_IF(NULL == pImage);
bool bRet = pImage->initWithImageFile(fullpath.c_str(), eImageFormat);
CC_BREAK_IF(!bRet);
//纹理类,继承自CCObject,此时引用计数 1,
//注意:这里并没有加入到自动释放池中
texture = new CCTexture2D();
//根据上面得到的CCImage初始化texture
if( texture &&
texture->initWithImage(pImage) )
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
//这个先不用关,跟android有关
// cache the texture file name
VolatileTexture::addImageTexture(texture, fullpath.c_str(), eImageFormat);
#endif
//得的texture纹理后,缓存到m_pTextures字典中,引用计数 +1 = 2
m_pTextures->setObject(texture, pathKey.c_str());
texture->release(); //引用计数 -1 = 1
}
else
{
CCLOG("cocos2d: Couldn't create texture for file:%s in CCTextureCache", path);
}
}
} while (0);
}
//释放pImage(CCImage),通过上面的initWithImage函数,我们
//得到了所需要的纹理,所以这里的CCImage类就可以释放了。
CC_SAFE_RELEASE(pImage);
//pthread_mutex_unlock(m_pDictLock);
return texture;
}
3、根据CCImage初始化texture
if( texture && texture->initWithImage(pImage) )
------ >>
bool CCTexture2D::initWithImage(CCImage *uiImage)
{
if (uiImage == NULL)
{
CCLOG("cocos2d: CCTexture2D. Can't create Texture. UIImage is nil");
return false;
}
unsigned int imageWidth = uiImage->getWidth();
unsigned int imageHeight = uiImage->getHeight();
CCConfiguration *conf = CCConfiguration::sharedConfiguration();
//纹理有个最大尺寸,判断是否超过最大尺寸
unsigned maxTextureSize = conf->getMaxTextureSize();
if (imageWidth > maxTextureSize || imageHeight > maxTextureSize)
{
CCLOG("cocos2d: WARNING: Image (%u x %u) is bigger than the supported %u x %u", imageWidth, imageHeight, maxTextureSize, maxTextureSize);
return false;
}
// always load premultiplied images
return initPremultipliedATextureWithImage(uiImage, imageWidth, imageHeight);
}
-->>initPremultipliedATextureWithImage函数:
bool CCTexture2D::initPremultipliedATextureWithImage(CCImage *image, unsigned int width, unsigned int height)
{
unsigned char* tempData = image->getData(); //图片数据
unsigned int* inPixel32 = NULL;
unsigned char* inPixel8 = NULL;
unsigned short* outPixel16 = NULL;
bool hasAlpha = image->hasAlpha(); //是否有alpha分量
CCSize imageSize = CCSizeMake((float)(image->getWidth()), (float)(image->getHeight()));
CCTexture2DPixelFormat pixelFormat; //纹理格式
size_t bpp = image->getBitsPerComponent();
// compute pixel format
// 根据是否有alpha分量,设置纹理格式
if (hasAlpha)
{
// 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;
//默认纹理格式RGBA8888
//这里可以通过setDefaultAlphaPixelFormat函数进行设置,而且在
//void CCDirector::setDefaultValues(void)函数中,会根据CCConfiguration类中的
//配置进行设置。
/*
//
// Texture options for images that contains alpha
//
// implementation CCTexture2D (PixelFormat)
void CCTexture2D::setDefaultAlphaPixelFormat(CCTexture2DPixelFormat format)
{
g_defaultAlphaPixelFormat = format;
}
*/
pixelFormat = g_defaultAlphaPixelFormat;
}
else
{
if (bpp >= 8)
{
pixelFormat = kCCTexture2DPixelFormat_RGB888;
}
else
{
pixelFormat = kCCTexture2DPixelFormat_RGB565;
}
}
// Repack the pixel data into the right format
//这里所做的工作:把图片数据 转化为 响应纹理格式的数据
//从这里 // Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
//可以看出来,我们的从image->getData()得到的图片数据都是每个颜色分量都有8位的
//数据格式,只不过有些数据有alpha分量,有些没有alpha分量
// width(图片宽度) * height(图片高度)
unsigned int length = width * height;
if (pixelFormat == kCCTexture2DPixelFormat_RGB565)
{
if (hasAlpha)
{
// Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
tempData = new unsigned char[width * height * 2];
outPixel16 = (unsigned short*)tempData;
inPixel32 = (unsigned int*)image->getData();
for(unsigned int i = 0; i < length; ++i, ++inPixel32)
{
*outPixel16++ =
((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R
((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) | // G
((((*inPixel32 >> 16) & 0xFF) >> 3) << 0); // B
}
}
else
{
// Convert "RRRRRRRRRGGGGGGGGBBBBBBBB" to "RRRRRGGGGGGBBBBB"
tempData = new unsigned char[width * height * 2];
outPixel16 = (unsigned short*)tempData;
inPixel8 = (unsigned char*)image->getData();
for(unsigned int i = 0; i < length; ++i)
{
*outPixel16++ =
(((*inPixel8++ & 0xFF) >> 3) << 11) | // R
(((*inPixel8++ & 0xFF) >> 2) << 5) | // G
(((*inPixel8++ & 0xFF) >> 3) << 0); // B
}
}
}
else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444)
{
// Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA"
inPixel32 = (unsigned int*)image->getData();
tempData = new unsigned char[width * height * 2];
outPixel16 = (unsigned short*)tempData;
for(unsigned int i = 0; i < length; ++i, ++inPixel32)
{
*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)
{
// Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA"
inPixel32 = (unsigned int*)image->getData();
tempData = new unsigned char[width * height * 2];
outPixel16 = (unsigned short*)tempData;
for(unsigned int i = 0; i < length; ++i, ++inPixel32)
{
*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)
{
// 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)
{
// 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
}
}
//看下面
initWithData(tempData, pixelFormat, width, height, imageSize);
if (tempData != image->getData())
{
delete [] tempData;
}
m_bHasPremultipliedAlpha = image->isPremultipliedAlpha();
return true;
}
----initWithData--->>
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;
//glPixelStore这组函数要改变的是像素的存储格式(摘自网路,OpenGL的东西,我也不太明白)
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);
}
//下面这些都是OpenGL纹理相关的内容,我现在也不是很清楚
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;
}
总结:
在CCTextureCache类中通过 m_pTextures 字典类型的成员变量存放加载过的
纹理CCTexture2D,以后如果再次加载相同的纹理,直接去m_pTextures缓存中读取,
而不用重走上面流程,从而达到缓存的目的。
CCTextureCache类源码分析(2):
在CCTextureCache类源码分析(1)中,我们分析了CCTextureCache如何实现
纹理缓存的,但是在分析的过程中,我们忽略了很多东西,比如CCImage类
如何加载纹理图片,这一篇我们分析一下CCImage:
源码分析:
1、CCImage继承自CCObject
2、成员变量,这些变量需要我们通过解析图片文件获得
unsigned char *m_pData; //图片数据
CC_SYNTHESIZE_READONLY(unsigned short, m_nWidth, Width); //宽高
CC_SYNTHESIZE_READONLY(unsigned short, m_nHeight, Height);
CC_SYNTHESIZE_READONLY(int, m_nBitsPerComponent, BitsPerComponent); //每个颜色分量的位数
bool m_bHasAlpha; //是否有alpha分量
3、
//对于.pvr 和 .pkm 图片格式文件需要特殊处理,后面分析
if (std::string::npos != lowerCase.find(".pvr"))
{
texture = this->addPVRImage(fullpath.c_str());
}
else if (std::string::npos != lowerCase.find(".pkm"))
{
// ETC1 file format, only supportted on Android
texture = this->addETCImage(fullpath.c_str());
}
else
{
//针对下面这些图片格式,我们需要使用CCImage类来读取
//图片文件数据到内存中,并进行分析,因为每一种图片格式
//文件对图片信息的存储方式是不同的,比如那几个字节存放图片的大小信息,
//那些字节存放图片颜色信息,我们需要根据每种文件格式进行不同的分析处理,
//从而得到我们需要的信息。
//从这里也可以看出cocos2dx支持的图片格式:
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;
}
else if (std::string::npos != lowerCase.find(".webp"))
{
eImageFormat = CCImage::kFmtWebp;
}
pImage = new CCImage();
CC_BREAK_IF(NULL == pImage);
bool bRet = pImage->initWithImageFile(fullpath.c_str(), eImageFormat);
CC_BREAK_IF(!bRet);
----initWithImageFile---->>
strPath : 文件路径
eImgFmt : 图片格式
bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = eFmtPng*/)
{
bool bRet = false;
#ifdef EMSCRIPTEN
....这部分就不管了
#else
//读取图片的文件数据
unsigned long nSize = 0;
std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(strPath);
unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(), "rb", &nSize);
if (pBuffer != NULL && nSize > 0)
{
bRet = initWithImageData(pBuffer, nSize, eImgFmt);
}
CC_SAFE_DELETE_ARRAY(pBuffer);
#endif // EMSCRIPTEN
return bRet;
}
------initWithImageData------>>
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)
{
//对png图片进行解析,_initWithPngData函数里面调用png解析的库
//对png文件进行解析,然后得到我们所需的所有信息
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;
}
总结:
CCImage所做的工作就是根据不同的图片格式调用不同的底层库,
如png,jpg解析库,通过这些库对图片文件进行分析,
从而得到我们所需要的所有信息,这里有个疑惑,就是通过底层库
解析图片文件之后得到的数据到底是什么格式存储的?
因为我在CCTextureCache类源码分析(1) 通过下面这行
// Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA"
猜测数据的存储都是每个颜色分量是8位,但是因为我对png这些图片的解析不熟悉,
所以在这里并没有很好的证据证明上面的猜测,所以把这个疑惑记录在此。
CCTextureCache类源码分析(3):
1、在 (1) 中有提到过两种特殊的图片格式,它们的处理方式和其他
格式的图片不同,这里就分析一下这两种特殊的图片格式的处理
//对于.pvr 和 .pkm 图片格式文件需要特殊处理
if (std::string::npos != lowerCase.find(".pvr"))
{
texture = this->addPVRImage(fullpath.c_str());
}
else if (std::string::npos != lowerCase.find(".pkm"))
{
// ETC1 file format, only supportted on Android
texture = this->addETCImage(fullpath.c_str());
}
2、.pvr 格式分析,包括压缩格式
---addPVRImage--->>:
CCTexture2D * CCTextureCache::addPVRImage(const char* path)
{
.....
// Split up directory and filename
std::string fullpath = CCFileUtils::sharedFileUtils()->fullPathForFilename(key.c_str());
texture = new CCTexture2D();
//主要的不同点就在这里
if(texture != NULL && texture->initWithPVRFile(fullpath.c_str()) ) -->>
{
#if CC_ENABLE_CACHE_TEXTURE_DATA
// cache the texture file name
VolatileTexture::addImageTexture(texture, fullpath.c_str(), CCImage::kFmtRawData);
#endif
m_pTextures->setObject(texture, key.c_str());
texture->autorelease();
}
else
{
CCLOG("cocos2d: Couldn't add PVRImage:%s in CCTextureCache",key.c_str());
CC_SAFE_DELETE(texture);
}
return texture;
}
-------initWithPVRFile-------->>>
bool CCTexture2D::initWithPVRFile(const char* file)
{
bool bRet = false;
// nothing to do with CCObject::init
//这里出现了一个新类CCTexturePVR,这个类专门用于处理PVR格式的
//图片文件,这个类就是通过分析这种格式的文件,从而获取我们所需要
//的纹理数据。
CCTexturePVR *pvr = new CCTexturePVR;
bRet = pvr->initWithContentsOfFile(file); --->>>
if (bRet)
{
//通过CCTexturePVR这个类对PVR格式的文件进行分析,然后把
//这些信息复制给CCTexture2D,因为下面会释放CCTexturePVR类的实例
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;
}
----------->>>
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]);
}
//看到这里就应该明白CCTexturePVR包含的对PVR压缩文件的处理
if (lowerCase.find(".ccz") != std::string::npos)
{
pvrlen = ZipUtils::ccInflateCCZFile(path, &pvrdata);
}
else if (lowerCase.find(".gz") != std::string::npos)
{
pvrlen = ZipUtils::ccInflateGZipFile(path, &pvrdata);
}
else
{
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_pPixelFormatInfo = NULL;
m_bHasAlpha = false;
m_bForcePremultipliedAlpha = false;
m_bHasPremultipliedAlpha = false;
m_bRetainName = false; // cocos2d integration
//createGLTexture这个函数创建的OpenGL的纹理,因为PVR格式的图片和PNG普通文件创建
//OpenGL纹理的参数不同,所以需要单独创建。
if (! ((unpackPVRv2Data(pvrdata, pvrlen) || unpackPVRv3Data(pvrdata, pvrlen)) && createGLTexture()) )
{
CC_SAFE_DELETE_ARRAY(pvrdata);
this->release();
return false;
}
CC_SAFE_DELETE_ARRAY(pvrdata);
return true;
}
3、.pkm 格式和上面的过程类似,就不分析。
CCTextureCache类源码分析(4)
上面三篇分析了CCTextureCache如何加载纹理和缓存纹理,
这篇分析下CCTextureCache如何移除已经缓存的纹理。
我们缓存过的纹理,如果手动移除,是不会自动移除的。
1、
void removeAllTextures(); //移除所有纹理缓存
void CCTextureCache::removeAllTextures()
{
m_pTextures->removeAllObjects();
}
2、
/** Removes unused textures
* Textures that have a retain count of 1 will be deleted
* It is convenient to call this method after when starting a new Scene
* @since v0.8
*/
//移除不使用的纹理缓存,那什么是不使用的纹理缓存呢?
//引用计数为1的纹理,就是不使用的纹理缓存,那为什么说
//引用计数为1就是不使用的呢?上面三篇分析的过程中提到了
//缓存纹理的引用计数,在CCTextureCache纹理缓存中的纹理引用
//计数为1,如果这个纹理被一个精灵使用,则纹理的引用计数加1,
//当这个精灵不使用这个纹理时,那么会对纹理的引用计数减1,
//所以如果纹理的引用计数为1,则表明没有精灵引用这个纹理,所以
//就为不使用的纹理。
Textures that have a retain count of 1 will be deleted
void removeUnusedTextures();
void CCTextureCache::removeUnusedTextures()
{
/** Inter engineer zhuoshi sun finds that this way will get better performance
*/
if (m_pTextures->count())
{
// find elements to be removed
CCDictElement* pElement = NULL;
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);
}
}
// remove elements
for (list<CCDictElement*>::iterator iter = elementToRemove.begin(); iter != elementToRemove.end(); ++iter)
{
CCLOG("cocos2d: CCTextureCache: removing unused texture: %s", (*iter)->getStrKey());
m_pTextures->removeObjectForElememt(*iter);
}
}
}
3、根据纹理或纹理的key(纹理图片的全路径),这里我们可以直接使用
纹理图片的文件名。
/** Deletes a texture from the cache given a texture
*/
void removeTexture(CCTexture2D* texture);
/** Deletes a texture from the cache given a its key name
@since v0.99.4
*/
void removeTextureForKey(const char *textureKeyName);