轉自:http://blog.csdn.net/honghaier/article/details/8068895
當一張圖片被加載到內存后,它是以紋理的形式存在的。紋理是什么東西呢?紋理就是一塊內存,這塊內存中存放的是按照指定的像素格式填充的圖片像素信息。它被最終作為三角面着色所依據的數據源。
我們來看一下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, const char *fontName, 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 pixelsWide, 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, CCTexture2DPixelFormat pixelFormat, unsigned int pixelsWide, unsigned int pixelsHigh, 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, 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); //默認不使用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_uName, m_uPixelsWide, m_uPixelsHigh, m_fMaxS, 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, imageWidth, 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,G,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,G,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, ++inPixel32) { //將inPixel32指向的unsigned int數據通過取出R,G,B,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, const char *fontName, 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, const CCSize& dimensions, CCTextAlignment hAlignment, CCVerticalTextAlignment vAlignment, const char *fontName, float fontSize) { //如果定義使用可變紋理 #if CC_ENABLE_CACHE_TEXTURE_DATA // cache the texture data VolatileTexture::addStringTexture(this, text, dimensions, hAlignment, vAlignment, fontName, 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, fontName, (int)fontSize)) { return false; } //再由CCImage實例對象來創建出OpenGL貼圖,初始化紋理。 return initWithImage(&image); } // 在指定的位置繪制貼圖。 void CCTexture2D::drawAtPoint(const CCPoint& point) { //定義貼圖中圖像區域的UV坐標。從左上至右下。 GLfloat coordinates[] = { 0.0f, m_fMaxT, m_fMaxS,m_fMaxT, 0.0f, 0.0f, m_fMaxS,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.y, point.x, height + point.y, width + point.x, 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, 0, vertices); //將coordinates設置為頂點的紋理坐標參數 glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, coordinates); //繪制三角形,參1為繪圖方式,參2為頂點起始索引,參3為三角形面數。 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } //繪制紋理上的一個區域。 void CCTexture2D::drawInRect(const CCRect& rect) { //定義貼圖中圖像區域的UV坐標。從左上至右下。 GLfloat coordinates[] = { 0.0f, m_fMaxT, m_fMaxS,m_fMaxT, 0.0f, 0.0f, m_fMaxS,0.0f }; //繪制到的區域 GLfloat vertices[] = { rect.origin.x, rect.origin.y, /*0.0f,*/ rect.origin.x + rect.size.width, rect.origin.y, /*0.0f,*/ rect.origin.x, rect.origin.y + rect.size.height, /*0.0f,*/ rect.origin.x + rect.size.width, 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, 2, GL_FLOAT, GL_FALSE, 0, vertices); //將coordinates設置為頂點的紋理坐標參數 glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, coordinates); //繪制三角形,參1為繪圖方式,參2為頂點起始索引,參3為三角形面數。 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } #ifdef CC_SUPPORT_PVRTC // 如果支持PVR文件的壓縮格式。提供的讀取PVR壓縮文件的函數。 bool CCTexture2D::initWithPVRTCData(const void *data, int level, int bpp, bool hasAlpha, int length, 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, length, 0, 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, GL_TEXTURE_MIN_FILTER, texParams->minFilter ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texParams->magFilter ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParams->wrapS ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParams->wrapT ); } //設置為非抗鋸齒紋理模式 void CCTexture2D::setAliasTexParameters() { //綁定紋理 ccGLBindTexture2D( m_uName ); //設置多級紋理 if( ! m_bHasMipmaps ) { //設置最小濾波方式為最近點采樣,這種方式最快,但有鋸齒。 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); } else { //設置最小濾波方式為多級紋理方式。 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST ); } //設置放大濾波方式為最近點采樣 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); } //設置為抗鋸齒紋理模式 void CCTexture2D::setAntiAliasTexParameters() { //綁定紋理 ccGLBindTexture2D( m_uName ); //設置多級紋理 if( ! m_bHasMipmaps ) { //設置最小濾波方式為線性過濾,這種方式紋理會有一定程度模糊。 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); } else { //設置最小濾波方式為多級紋理方式。 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST ); } //設置放大濾波方式為最近點采樣 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, 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, cannot give useful result", (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, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16, false, kCCTexture2DPixelFormat_RGBA4444 }, { kPVRTexturePixelTypeRGBA_5551, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16, false, kCCTexture2DPixelFormat_RGB5A1 }, { kPVRTexturePixelTypeRGBA_8888, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 32, false, kCCTexture2DPixelFormat_RGBA8888 }, { kPVRTexturePixelTypeRGB_565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16, false, kCCTexture2DPixelFormat_RGB565 }, { kPVRTexturePixelTypeRGB_888, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, 24, false, kCCTexture2DPixelFormat_RGB888 }, { kPVRTexturePixelTypeA_8, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 8, false, kCCTexture2DPixelFormat_A8 }, { kPVRTexturePixelTypeI_8, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 8, false, kCCTexture2DPixelFormat_I8 }, { kPVRTexturePixelTypeAI_88, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,16, false, kCCTexture2DPixelFormat_AI88 }, #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) //如果程序運行在IOS上,還可支持以下信息 { kPVRTexturePixelTypePVRTC_2, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, (unsigned int)-1, (unsigned int)-1, 2, true, kCCTexture2DPixelFormat_PVRTC2 }, { kPVRTexturePixelTypePVRTC_4, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, (unsigned int)-1, (unsigned int)-1, 4, true, kCCTexture2DPixelFormat_PVRTC4 }, { kPVRTexturePixelTypeBGRA_8888, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32, false, kCCTexture2DPixelFormat_RGBA8888 }, #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_uName(0) , 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_TEXTURE_MIN_FILTER, GL_LINEAR); } else { //設置最小濾波方式為最近點采樣,這種方式有鋸齒。 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } //設置最大濾波方式為線性過濾 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); } //檢錯 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, width, height, 0, datalen, data); } else {//產生一般格式的貼圖。 glTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, format, 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", i, width, height); } //檢錯 err = glGetError(); if (err != GL_NO_ERROR) { CCLOG("cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", i, 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 bpp, bool hasAlpha, 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, const CCSize& dimensions, CCTextAlignment alignment, CCVerticalTextAlignment vAlignment, const char *fontName, float fontSize); //通過圖片數據生成的紋理及相關信息生成一個多變紋理並將其指針放入容器。 static void addDataTexture(CCTexture2D *tt, void* data, CCTexture2DPixelFormat pixelFormat, 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, CCObject *target, 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, 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, 0, 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(), "rb", &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 bpp, bool hasAlpha, 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(), "rb", &nLen); //新建創一個紋理。 texture = new CCTexture2D(); //使用讀取到的數據創建紋理。 if( texture->initWithPVRTCData(pData, 0, bpp, hasAlpha, width, (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, fullpath.c_str(), 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, const char* imageFileName, 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, void* data, CCTexture2DPixelFormat pixelFormat, 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, const char* text, const CCSize& dimensions, CCTextAlignment alignment, CCVerticalTextAlignment vAlignment, const char *fontName, 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(), "rb", &nSize); //使用數據地址對前面定義的CCImage對象image進行初始化。 if (image.initWithImageData((void*)pBuffer, nSize, 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