CCTextureCache类源码分析

 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);




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值