2010-08-02 10:58:00 +08:00
|
|
|
/****************************************************************************
|
2011-03-19 14:45:51 +08:00
|
|
|
Copyright (c) 2010-2011 cocos2d-x.org
|
|
|
|
Copyright (c) 2008-2010 Ricardo Quesada
|
2011-07-05 10:47:25 +08:00
|
|
|
Copyright (c) 2011 Zynga Inc.
|
2010-08-02 10:58:00 +08:00
|
|
|
|
|
|
|
http://www.cocos2d-x.org
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <stack>
|
|
|
|
#include <string>
|
2010-07-26 17:32:23 +08:00
|
|
|
#include <cctype>
|
2011-11-22 16:47:24 +08:00
|
|
|
#include <queue>
|
2010-07-16 16:28:11 +08:00
|
|
|
#include "CCTextureCache.h"
|
|
|
|
#include "CCTexture2D.h"
|
|
|
|
#include "ccMacros.h"
|
2011-03-07 17:11:57 +08:00
|
|
|
#include "CCData.h"
|
2010-07-30 16:35:07 +08:00
|
|
|
#include "CCDirector.h"
|
2010-08-25 14:14:31 +08:00
|
|
|
#include "platform/platform.h"
|
2011-03-07 17:11:57 +08:00
|
|
|
#include "CCFileUtils.h"
|
|
|
|
#include "CCImage.h"
|
2011-07-28 17:32:09 +08:00
|
|
|
#include "support/ccUtils.h"
|
2011-11-16 11:04:29 +08:00
|
|
|
#include "CCScheduler.h"
|
|
|
|
#include "pthread.h"
|
2011-11-18 09:55:47 +08:00
|
|
|
#include "CCThread.h"
|
2011-11-25 16:14:06 +08:00
|
|
|
#include "semaphore.h"
|
2012-03-20 15:04:53 +08:00
|
|
|
#include "CCString.h"
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-03-20 15:04:53 +08:00
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
NS_CC_BEGIN
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2011-11-16 11:04:29 +08:00
|
|
|
typedef struct _AsyncStruct
|
2010-07-16 16:28:11 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
std::string filename;
|
|
|
|
CCObject *target;
|
|
|
|
SEL_CallFuncO selector;
|
2011-11-16 11:04:29 +08:00
|
|
|
} AsyncStruct;
|
|
|
|
|
2011-11-22 16:47:24 +08:00
|
|
|
typedef struct _ImageInfo
|
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
AsyncStruct *asyncStruct;
|
|
|
|
CCImage *image;
|
|
|
|
CCImage::EImageFormat imageType;
|
2011-11-22 16:47:24 +08:00
|
|
|
} ImageInfo;
|
|
|
|
|
|
|
|
static pthread_t s_loadingThread;
|
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
static pthread_mutex_t s_asyncStructQueueMutex;
|
2011-11-22 16:47:24 +08:00
|
|
|
static pthread_mutex_t s_ImageInfoMutex;
|
|
|
|
|
2011-11-25 16:14:06 +08:00
|
|
|
static sem_t s_sem;
|
2012-01-30 13:10:49 +08:00
|
|
|
static bool need_quit;
|
2011-11-22 16:47:24 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
static std::queue<AsyncStruct*> *s_pAsyncStructQueue;
|
|
|
|
static std::queue<ImageInfo*> *s_pImageQueue;
|
2011-11-16 11:04:29 +08:00
|
|
|
|
2011-12-22 15:38:31 +08:00
|
|
|
static CCImage::EImageFormat computeImageFormatType(string& filename)
|
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCImage::EImageFormat ret = CCImage::kFmtUnKnown;
|
|
|
|
|
|
|
|
if ((std::string::npos != filename.find(".jpg")) || (std::string::npos != filename.find(".jpeg")))
|
|
|
|
{
|
|
|
|
ret = CCImage::kFmtJpg;
|
|
|
|
}
|
|
|
|
else if ((std::string::npos != filename.find(".png")) || (std::string::npos != filename.find(".PNG")))
|
|
|
|
{
|
|
|
|
ret = CCImage::kFmtPng;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2011-12-22 15:38:31 +08:00
|
|
|
}
|
|
|
|
|
2011-11-16 11:04:29 +08:00
|
|
|
static void* loadImage(void* data)
|
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
// create autorelease pool for iOS
|
|
|
|
CCThread thread;
|
|
|
|
thread.createAutoreleasePool();
|
2011-11-18 09:55:47 +08:00
|
|
|
|
2011-11-22 16:47:24 +08:00
|
|
|
AsyncStruct *pAsyncStruct = NULL;
|
2011-11-16 11:04:29 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
// wait for rendering thread to ask for loading if s_pAsyncStructQueue is empty
|
|
|
|
sem_wait(&s_sem);
|
2011-11-16 11:04:29 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
std::queue<AsyncStruct*> *pQueue = s_pAsyncStructQueue;
|
2011-11-22 16:47:24 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
pthread_mutex_lock(&s_asyncStructQueueMutex);// get async struct from queue
|
2011-11-25 16:14:06 +08:00
|
|
|
if (pQueue->empty())
|
|
|
|
{
|
2011-11-22 16:47:24 +08:00
|
|
|
pthread_mutex_unlock(&s_asyncStructQueueMutex);
|
2012-04-19 14:35:52 +08:00
|
|
|
if (need_quit)
|
|
|
|
break;
|
|
|
|
else
|
|
|
|
continue;
|
2011-11-25 16:14:06 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pAsyncStruct = pQueue->front();
|
|
|
|
pQueue->pop();
|
|
|
|
pthread_mutex_unlock(&s_asyncStructQueueMutex);
|
2012-04-19 14:35:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *filename = pAsyncStruct->filename.c_str();
|
|
|
|
|
|
|
|
// compute image type
|
|
|
|
CCImage::EImageFormat imageType = computeImageFormatType(pAsyncStruct->filename);
|
|
|
|
if (imageType == CCImage::kFmtUnKnown)
|
|
|
|
{
|
|
|
|
CCLOG("unsupportted format %s",filename);
|
|
|
|
delete pAsyncStruct;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate image
|
|
|
|
CCImage *pImage = new CCImage();
|
|
|
|
if (! pImage->initWithImageFileThreadSafe(filename, imageType))
|
|
|
|
{
|
|
|
|
delete pImage;
|
|
|
|
CCLOG("can not load %s", filename);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate image info
|
|
|
|
ImageInfo *pImageInfo = new ImageInfo();
|
|
|
|
pImageInfo->asyncStruct = pAsyncStruct;
|
|
|
|
pImageInfo->image = pImage;
|
|
|
|
pImageInfo->imageType = imageType;
|
|
|
|
|
|
|
|
// put the image info into the queue
|
|
|
|
pthread_mutex_lock(&s_ImageInfoMutex);
|
|
|
|
s_pImageQueue->push(pImageInfo);
|
|
|
|
pthread_mutex_unlock(&s_ImageInfoMutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2011-11-16 11:04:29 +08:00
|
|
|
}
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
// implementation CCTextureCache
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
// TextureCache - Alloc, Init & Dealloc
|
|
|
|
static CCTextureCache *g_sharedTextureCache;
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
CCTextureCache * CCTextureCache::sharedTextureCache()
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
if (!g_sharedTextureCache)
|
|
|
|
g_sharedTextureCache = new CCTextureCache();
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
return g_sharedTextureCache;
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
CCTextureCache::CCTextureCache()
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCAssert(g_sharedTextureCache == NULL, "Attempted to allocate a second instance of a singleton.");
|
|
|
|
|
|
|
|
m_pTextures = new CCDictionary();
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
CCTextureCache::~CCTextureCache()
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCLOGINFO("cocos2d: deallocing CCTextureCache.");
|
|
|
|
need_quit = true;
|
|
|
|
sem_post(&s_sem);
|
|
|
|
CC_SAFE_RELEASE(m_pTextures);
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
void CCTextureCache::purgeSharedTextureCache()
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CC_SAFE_RELEASE_NULL(g_sharedTextureCache);
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-14 19:11:57 +08:00
|
|
|
const char* CCTextureCache::description()
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
return CCString::stringWithFormat("<CCTextureCache | Number of textures = %u>", m_pTextures->count())->getCString();
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2012-01-08 21:03:16 +08:00
|
|
|
void CCTextureCache::addImageAsync(const char *path, CCObject *target, SEL_CallFuncO selector)
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCAssert(path != NULL, "TextureCache: fileimage MUST not be NULL");
|
|
|
|
|
|
|
|
CCTexture2D *texture = NULL;
|
|
|
|
|
|
|
|
// optimization
|
|
|
|
|
|
|
|
std::string pathKey = path;
|
|
|
|
CCFileUtils::removeSuffixFromFile(pathKey);
|
|
|
|
|
|
|
|
pathKey = CCFileUtils::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 (target)
|
|
|
|
{
|
|
|
|
target->retain();
|
|
|
|
}
|
|
|
|
|
|
|
|
// lazy init
|
|
|
|
static bool firstRun = true;
|
|
|
|
if (firstRun)
|
|
|
|
{
|
2011-11-22 16:47:24 +08:00
|
|
|
s_pAsyncStructQueue = new queue<AsyncStruct*>();
|
2012-04-19 14:35:52 +08:00
|
|
|
s_pImageQueue = new queue<ImageInfo*>();
|
2011-11-22 16:47:24 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
pthread_mutex_init(&s_asyncStructQueueMutex, NULL);
|
|
|
|
sem_init(&s_sem, 0, 0);
|
|
|
|
pthread_mutex_init(&s_ImageInfoMutex, NULL);
|
|
|
|
pthread_create(&s_loadingThread, NULL, loadImage, NULL);
|
2011-11-16 11:04:29 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
CCDirector::sharedDirector()->getScheduler()->scheduleSelector(schedule_selector(CCTextureCache::addImageAsyncCallBack), this, 0, false);
|
2012-03-14 14:55:17 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
need_quit = false;
|
|
|
|
firstRun = false;
|
|
|
|
}
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
// generate async struct
|
|
|
|
AsyncStruct *data = new AsyncStruct();
|
|
|
|
data->filename = fullpath.c_str();
|
|
|
|
data->target = target;
|
|
|
|
data->selector = selector;
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
// add async struct into queue
|
|
|
|
pthread_mutex_lock(&s_asyncStructQueueMutex);
|
|
|
|
s_pAsyncStructQueue->push(data);
|
|
|
|
pthread_mutex_unlock(&s_asyncStructQueueMutex);
|
2011-11-25 16:14:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
sem_post(&s_sem);
|
2011-11-16 11:04:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void CCTextureCache::addImageAsyncCallBack(ccTime dt)
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
// the image is generated in loading thread
|
|
|
|
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 *pImage = pImageInfo->image;
|
|
|
|
|
|
|
|
CCObject *target = pAsyncStruct->target;
|
|
|
|
SEL_CallFuncO selector = pAsyncStruct->selector;
|
|
|
|
const char* filename = pAsyncStruct->filename.c_str();
|
|
|
|
|
|
|
|
// generate texture in render thread
|
|
|
|
CCTexture2D *texture = new CCTexture2D();
|
2012-03-21 10:36:20 +08:00
|
|
|
#if 0 //TODO: (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
2012-04-19 14:35:52 +08:00
|
|
|
texture->initWithImage(pImage, kCCResolutioniPhone);
|
2012-03-21 10:36:20 +08:00
|
|
|
#else
|
|
|
|
texture->initWithImage(pImage);
|
|
|
|
#endif
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2011-12-01 17:04:56 +08:00
|
|
|
#if CC_ENABLE_CACHE_TEXTTURE_DATA
|
|
|
|
// cache the texture file name
|
2012-04-19 14:35:52 +08:00
|
|
|
if (pImageInfo->imageType == CCImage::kFmtJpg)
|
|
|
|
{
|
|
|
|
VolatileTexture::addImageTexture(texture, filename, CCImage::kFmtJpg);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VolatileTexture::addImageTexture(texture, filename, CCImage::kFmtPng);
|
|
|
|
}
|
2011-12-01 17:04:56 +08:00
|
|
|
#endif
|
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
// cache the texture
|
|
|
|
m_pTextures->setObject(texture, filename);
|
|
|
|
texture->autorelease();
|
2011-11-16 11:04:29 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
if (target && selector)
|
|
|
|
{
|
|
|
|
(target->*selector)(texture);
|
|
|
|
target->release();
|
|
|
|
}
|
2011-11-16 11:04:29 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
delete pImage;
|
|
|
|
delete pAsyncStruct;
|
|
|
|
delete pImageInfo;
|
|
|
|
}
|
2011-11-16 11:04:29 +08:00
|
|
|
}
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2010-09-08 09:28:28 +08:00
|
|
|
CCTexture2D * CCTextureCache::addImage(const char * path)
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCAssert(path != NULL, "TextureCache: fileimage MUST not be NULL");
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
CCTexture2D * texture = NULL;
|
|
|
|
// Split up directory and filename
|
|
|
|
// MUTEX:
|
|
|
|
// Needed since addImageAsync calls this method from a different thread
|
|
|
|
|
|
|
|
//pthread_mutex_lock(m_pDictLock);
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
// remove possible -HD suffix to prevent caching the same image twice (issue #1040)
|
2011-03-09 12:00:04 +08:00
|
|
|
std::string pathKey = path;
|
2012-04-19 14:35:52 +08:00
|
|
|
ccResolutionType resolution = kCCResolutionUnknown;
|
|
|
|
CCFileUtils::removeSuffixFromFile(pathKey);
|
2010-12-30 17:30:11 +08:00
|
|
|
|
2011-03-11 11:04:22 +08:00
|
|
|
pathKey = CCFileUtils::fullPathFromRelativePath(pathKey.c_str());
|
2012-04-19 14:35:52 +08:00
|
|
|
texture = (CCTexture2D*)m_pTextures->objectForKey(pathKey.c_str());
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2011-03-11 11:04:22 +08:00
|
|
|
std::string fullpath = pathKey; // (CCFileUtils::fullPathFromRelativePath(path));
|
2012-04-19 14:35:52 +08:00
|
|
|
if( ! texture )
|
|
|
|
{
|
|
|
|
std::string lowerCase(path);
|
|
|
|
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
|
|
|
|
{
|
|
|
|
if (std::string::npos != lowerCase.find(".pvr"))
|
|
|
|
{
|
|
|
|
texture = this->addPVRImage(fullpath.c_str());
|
|
|
|
}
|
|
|
|
// Issue #886: TEMPORARY FIX FOR TRANSPARENT JPEGS IN IOS4
|
|
|
|
else if (std::string::npos != lowerCase.find(".jpg") || std::string::npos != lowerCase.find(".jpeg"))
|
|
|
|
{
|
|
|
|
CCImage image;
|
2011-03-29 11:41:44 +08:00
|
|
|
CCFileData data(fullpath.c_str(), "rb");
|
|
|
|
unsigned long nSize = data.getSize();
|
|
|
|
unsigned char* pBuffer = data.getBuffer();
|
2011-03-29 17:32:52 +08:00
|
|
|
CC_BREAK_IF(! image.initWithImageData((void*)pBuffer, nSize, CCImage::kFmtJpg));
|
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
texture = new CCTexture2D();
|
|
|
|
texture->initWithImage(&image, resolution);
|
2010-10-12 11:23:47 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
if( texture )
|
|
|
|
{
|
2011-04-06 16:29:58 +08:00
|
|
|
#if CC_ENABLE_CACHE_TEXTTURE_DATA
|
|
|
|
// cache the texture file name
|
|
|
|
VolatileTexture::addImageTexture(texture, fullpath.c_str(), CCImage::kFmtJpg);
|
|
|
|
#endif
|
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
m_pTextures->setObject(texture, pathKey.c_str());
|
|
|
|
// autorelease prevents possible crash in multithreaded environments
|
|
|
|
texture->autorelease();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CCLOG("cocos2d: Couldn't add image:%s in CCTextureCache", path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// prevents overloading the autorelease pool
|
|
|
|
CCImage image;
|
2011-03-29 11:41:44 +08:00
|
|
|
CCFileData data(fullpath.c_str(), "rb");
|
|
|
|
unsigned long nSize = data.getSize();
|
|
|
|
unsigned char* pBuffer = data.getBuffer();
|
2011-03-29 17:32:52 +08:00
|
|
|
CC_BREAK_IF(! image.initWithImageData((void*)pBuffer, nSize, CCImage::kFmtPng));
|
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
texture = new CCTexture2D();
|
|
|
|
texture->initWithImage(&image, resolution);
|
2011-04-06 16:29:58 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
if( texture )
|
|
|
|
{
|
2011-04-06 16:29:58 +08:00
|
|
|
#if CC_ENABLE_CACHE_TEXTTURE_DATA
|
|
|
|
// cache the texture file name
|
|
|
|
VolatileTexture::addImageTexture(texture, fullpath.c_str(), CCImage::kFmtPng);
|
|
|
|
#endif
|
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
m_pTextures->setObject(texture, pathKey.c_str());
|
|
|
|
// autorelease prevents possible crash in multithreaded environments
|
|
|
|
texture->autorelease();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CCLOG("cocos2d: Couldn't add image:%s in CCTextureCache", path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//pthread_mutex_unlock(m_pDictLock);
|
|
|
|
return texture;
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2011-07-19 15:14:59 +08:00
|
|
|
#ifdef CC_SUPPORT_PVRTC
|
2010-07-22 11:15:25 +08:00
|
|
|
CCTexture2D* CCTextureCache::addPVRTCImage(const char* path, int bpp, bool hasAlpha, int width)
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCAssert(path != NULL, "TextureCache: fileimage MUST not be nill");
|
|
|
|
CCAssert( bpp==2 || bpp==4, "TextureCache: bpp must be either 2 or 4");
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
CCTexture2D * texture;
|
2011-07-19 15:14:59 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
std::string temp(path);
|
2012-04-08 14:16:29 +08:00
|
|
|
CCFileUtils::removeSuffixFromFile(temp);
|
2011-07-19 15:14:59 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
if ( (texture = (CCTexture2D*)m_pTextures->objectForKey(temp.c_str())) )
|
|
|
|
{
|
|
|
|
return texture;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Split up directory and filename
|
|
|
|
std::string fullpath( CCFileUtils::fullPathFromRelativePath(path) );
|
|
|
|
|
|
|
|
CCData * data = CCData::dataWithContentsOfFile(fullpath);
|
|
|
|
texture = new CCTexture2D();
|
|
|
|
|
|
|
|
if( texture->initWithPVRTCData(data->bytes(), 0, bpp, hasAlpha, width,
|
2011-07-19 15:14:59 +08:00
|
|
|
(bpp==2 ? kCCTexture2DPixelFormat_PVRTC2 : kCCTexture2DPixelFormat_PVRTC4)))
|
2012-04-19 14:35:52 +08:00
|
|
|
{
|
|
|
|
m_pTextures->setObject(texture, temp.c_str());
|
|
|
|
texture->autorelease();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CCLOG("cocos2d: Couldn't add PVRTCImage:%s in CCTextureCache",path);
|
|
|
|
}
|
|
|
|
CC_SAFE_DELETE(data);
|
|
|
|
|
|
|
|
return texture;
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
2011-07-19 15:14:59 +08:00
|
|
|
#endif // CC_SUPPORT_PVRTC
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2011-07-19 15:14:59 +08:00
|
|
|
CCTexture2D * CCTextureCache::addPVRImage(const char* path)
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCAssert(path != NULL, "TextureCache: fileimage MUST not be nill");
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
CCTexture2D* tex = NULL;
|
|
|
|
std::string key(path);
|
2011-07-19 15:14:59 +08:00
|
|
|
// remove possible -HD suffix to prevent caching the same image twice (issue #1040)
|
2012-04-08 14:16:29 +08:00
|
|
|
CCFileUtils::removeSuffixFromFile(key);
|
2011-07-19 15:14:59 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
if( (tex = (CCTexture2D*)m_pTextures->objectForKey(key.c_str())) )
|
|
|
|
{
|
|
|
|
return tex;
|
|
|
|
}
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2011-07-19 15:14:59 +08:00
|
|
|
// Split up directory and filename
|
|
|
|
std::string fullpath = CCFileUtils::fullPathFromRelativePath(key.c_str());
|
2012-04-19 14:35:52 +08:00
|
|
|
tex = new CCTexture2D();
|
|
|
|
if(tex != NULL && tex->initWithPVRFile(fullpath.c_str()) )
|
|
|
|
{
|
2012-04-08 14:16:29 +08:00
|
|
|
#if CC_ENABLE_CACHE_TEXTTURE_DATA
|
|
|
|
// cache the texture file name
|
|
|
|
VolatileTexture::addImageTexture(tex, fullpath.c_str(), CCImage::kFmtRawData);
|
2012-02-07 11:18:39 +08:00
|
|
|
#endif
|
2012-04-19 14:35:52 +08:00
|
|
|
m_pTextures->setObject(tex, key.c_str());
|
|
|
|
tex->autorelease();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CCLOG("cocos2d: Couldn't add PVRImage:%s in CCTextureCache",key.c_str());
|
2012-04-10 18:17:08 +08:00
|
|
|
CC_SAFE_DELETE(tex);
|
2012-04-19 14:35:52 +08:00
|
|
|
}
|
2010-07-21 11:13:32 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
return tex;
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2011-03-07 17:11:57 +08:00
|
|
|
CCTexture2D* CCTextureCache::addUIImage(CCImage *image, const char *key)
|
2010-08-26 14:06:41 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCAssert(image != NULL, "TextureCache: image MUST not be nill");
|
|
|
|
|
|
|
|
CCTexture2D * texture = NULL;
|
|
|
|
// textureForKey() use full path,so the key should be full path
|
|
|
|
std::string forKey;
|
|
|
|
if (key)
|
|
|
|
{
|
|
|
|
forKey = CCFileUtils::fullPathFromRelativePath(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't have to lock here, because addImageAsync() will not
|
|
|
|
// invoke opengl function in loading thread.
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
// If key is nil, then create a new texture each time
|
|
|
|
if(key && (texture = (CCTexture2D *)m_pTextures->objectForKey(forKey.c_str())))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// prevents overloading the autorelease pool
|
|
|
|
texture = new CCTexture2D();
|
|
|
|
texture->initWithImage(image, kCCResolutionUnknown);
|
|
|
|
|
|
|
|
if(key && texture)
|
|
|
|
{
|
|
|
|
m_pTextures->setObject(texture, forKey.c_str());
|
|
|
|
texture->autorelease();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CCLOG("cocos2d: Couldn't add UIImage in CCTextureCache");
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
return texture;
|
2010-08-26 14:06:41 +08:00
|
|
|
}
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
// TextureCache - Remove
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
void CCTextureCache::removeAllTextures()
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
m_pTextures->removeAllObjects();
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2010-07-16 16:28:11 +08:00
|
|
|
void CCTextureCache::removeUnusedTextures()
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
CCDictElement* pElement = NULL;
|
|
|
|
CCDICT_FOREACH(m_pTextures, pElement)
|
|
|
|
{
|
|
|
|
CCLOG("cocos2d: CCTextureCache: texture: %s", pElement->getStrKey());
|
|
|
|
CCTexture2D *value = (CCTexture2D*)pElement->getObject();
|
|
|
|
if (value->retainCount() == 1)
|
|
|
|
{
|
|
|
|
CCLOG("cocos2d: CCTextureCache: removing unused texture: %s", pElement->getStrKey());
|
|
|
|
m_pTextures->removeObjectForElememt(pElement);
|
|
|
|
}
|
|
|
|
}
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2010-08-06 14:32:32 +08:00
|
|
|
void CCTextureCache::removeTexture(CCTexture2D* texture)
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
if( ! texture )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
CCArray* keys = m_pTextures->allKeysForObject(texture);
|
|
|
|
m_pTextures->removeObjectsForKeys(keys);
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2010-12-30 17:30:11 +08:00
|
|
|
void CCTextureCache::removeTextureForKey(const char *textureKeyName)
|
2010-07-16 14:15:06 +08:00
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
if (textureKeyName == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2010-07-16 14:15:06 +08:00
|
|
|
|
2011-07-01 09:32:15 +08:00
|
|
|
string fullPath = CCFileUtils::fullPathFromRelativePath(textureKeyName);
|
2012-04-19 14:35:52 +08:00
|
|
|
m_pTextures->removeObjectForKey(fullPath.c_str());
|
2010-12-30 17:30:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
CCTexture2D* CCTextureCache::textureForKey(const char* key)
|
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
return (CCTexture2D*)m_pTextures->objectForKey(CCFileUtils::fullPathFromRelativePath(key));
|
2010-07-16 14:15:06 +08:00
|
|
|
}
|
|
|
|
|
2011-04-06 16:29:58 +08:00
|
|
|
void CCTextureCache::reloadAllTextures()
|
|
|
|
{
|
|
|
|
#if CC_ENABLE_CACHE_TEXTTURE_DATA
|
|
|
|
VolatileTexture::reloadAllTextures();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-07-19 15:14:59 +08:00
|
|
|
void CCTextureCache::dumpCachedTextureInfo()
|
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
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();
|
2011-11-25 10:48:05 +08:00
|
|
|
// Each texture takes up width * height * bytesPerPixel bytes.
|
2012-04-19 14:35:52 +08:00
|
|
|
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));
|
2011-07-05 10:47:25 +08:00
|
|
|
}
|
|
|
|
|
2011-04-06 16:29:58 +08:00
|
|
|
#if CC_ENABLE_CACHE_TEXTTURE_DATA
|
|
|
|
|
2011-11-25 10:48:05 +08:00
|
|
|
std::list<VolatileTexture*> VolatileTexture::textures;
|
2011-04-06 16:29:58 +08:00
|
|
|
bool VolatileTexture::isReloading = false;
|
2011-11-25 10:48:05 +08:00
|
|
|
|
|
|
|
VolatileTexture::VolatileTexture(CCTexture2D *t)
|
|
|
|
: texture(t)
|
|
|
|
, m_eCashedImageType(kInvalid)
|
|
|
|
, m_pTextureData(NULL)
|
|
|
|
, m_PixelFormat(kTexture2DPixelFormat_RGBA8888)
|
|
|
|
, m_strFileName("")
|
|
|
|
, m_FmtImage(CCImage::kFmtPng)
|
|
|
|
, m_alignment(CCTextAlignmentCenter)
|
|
|
|
, m_strFontName("")
|
|
|
|
, m_strText("")
|
|
|
|
, m_fFontSize(0.0f)
|
|
|
|
{
|
|
|
|
m_size = CCSizeMake(0, 0);
|
|
|
|
textures.push_back(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
VolatileTexture::~VolatileTexture()
|
|
|
|
{
|
|
|
|
textures.remove(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VolatileTexture::addImageTexture(CCTexture2D *tt, const char* imageFileName, CCImage::EImageFormat format)
|
|
|
|
{
|
2011-04-06 16:29:58 +08:00
|
|
|
if (isReloading)
|
2011-11-25 10:48:05 +08:00
|
|
|
return;
|
|
|
|
|
2011-04-06 16:29:58 +08:00
|
|
|
VolatileTexture *vt = 0;
|
|
|
|
std::list<VolatileTexture *>::iterator i = textures.begin();
|
|
|
|
while( i != textures.end() )
|
|
|
|
{
|
|
|
|
VolatileTexture *v = *i++;
|
|
|
|
if (v->texture == tt) {
|
|
|
|
vt = v;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vt)
|
2011-11-25 10:48:05 +08:00
|
|
|
vt = new VolatileTexture(tt);
|
|
|
|
|
|
|
|
vt->m_eCashedImageType = kImageFile;
|
|
|
|
vt->m_strFileName = imageFileName;
|
|
|
|
vt->m_FmtImage = format;
|
2011-12-29 16:00:36 +08:00
|
|
|
vt->m_PixelFormat = tt->getPixelFormat();
|
2011-11-25 10:48:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void VolatileTexture::addDataTexture(CCTexture2D *tt, void* data, CCTexture2DPixelFormat pixelFormat, const CCSize& contentSize)
|
|
|
|
{
|
2012-04-19 14:35:52 +08:00
|
|
|
if (isReloading)
|
|
|
|
return;
|
|
|
|
|
|
|
|
VolatileTexture *vt = 0;
|
|
|
|
std::list<VolatileTexture *>::iterator i = textures.begin();
|
|
|
|
while( i != textures.end() )
|
|
|
|
{
|
|
|
|
VolatileTexture *v = *i++;
|
|
|
|
if (v->texture == tt) {
|
|
|
|
vt = v;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vt)
|
|
|
|
vt = new VolatileTexture(tt);
|
|
|
|
|
|
|
|
vt->m_eCashedImageType = kImageData;
|
|
|
|
vt->m_pTextureData = data;
|
|
|
|
vt->m_PixelFormat = pixelFormat;
|
|
|
|
vt->m_TextureSize = contentSize;
|
2011-11-25 10:48:05 +08:00
|
|
|
}
|
|
|
|
|
2011-08-17 21:19:57 +08:00
|
|
|
void VolatileTexture::addStringTexture(CCTexture2D *tt, const char* text, const CCSize& dimensions, CCTextAlignment alignment, const char *fontName, float fontSize)
|
2011-04-06 16:29:58 +08:00
|
|
|
{
|
|
|
|
if (isReloading)
|
|
|
|
return;
|
|
|
|
|
|
|
|
VolatileTexture *vt = 0;
|
|
|
|
std::list<VolatileTexture *>::iterator i = textures.begin();
|
|
|
|
while( i != textures.end() )
|
|
|
|
{
|
|
|
|
VolatileTexture *v = *i++;
|
|
|
|
if (v->texture == tt) {
|
|
|
|
vt = v;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vt)
|
|
|
|
vt = new VolatileTexture(tt);
|
|
|
|
|
2011-07-28 17:32:09 +08:00
|
|
|
vt->m_eCashedImageType = kString;
|
|
|
|
vt->m_size = dimensions;
|
2011-04-06 16:29:58 +08:00
|
|
|
vt->m_strFontName = fontName;
|
|
|
|
vt->m_alignment = alignment;
|
|
|
|
vt->m_fFontSize = fontSize;
|
|
|
|
vt->m_strText = text;
|
|
|
|
}
|
2011-11-25 10:48:05 +08:00
|
|
|
|
|
|
|
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()
|
2011-07-19 15:14:59 +08:00
|
|
|
{
|
|
|
|
isReloading = true;
|
|
|
|
|
|
|
|
CCLOG("reload all texture");
|
|
|
|
std::list<VolatileTexture *>::iterator i = textures.begin();
|
|
|
|
|
|
|
|
while( i != textures.end() )
|
|
|
|
{
|
|
|
|
VolatileTexture *vt = *i++;
|
|
|
|
|
2012-04-19 14:35:52 +08:00
|
|
|
switch (vt->m_eCashedImageType)
|
|
|
|
{
|
2012-04-08 14:16:29 +08:00
|
|
|
case kImageFile:
|
|
|
|
{
|
|
|
|
CCImage image;
|
|
|
|
std::string lowerCase(vt->m_strFileName.c_str());
|
|
|
|
for (unsigned int i = 0; i < lowerCase.length(); ++i)
|
|
|
|
{
|
|
|
|
lowerCase[i] = tolower(lowerCase[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (std::string::npos != lowerCase.find(".pvr"))
|
|
|
|
{
|
|
|
|
CCTexture2DPixelFormat oldPixelFormat = CCTexture2D::defaultAlphaPixelFormat();
|
|
|
|
CCTexture2D::setDefaultAlphaPixelFormat(vt->m_PixelFormat);
|
|
|
|
|
|
|
|
vt->texture->initWithPVRFile(vt->m_strFileName.c_str());
|
|
|
|
CCTexture2D::setDefaultAlphaPixelFormat(oldPixelFormat);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CCFileData data(vt->m_strFileName.c_str(), "rb");
|
|
|
|
unsigned long nSize = data.getSize();
|
|
|
|
unsigned char* pBuffer = data.getBuffer();
|
|
|
|
|
|
|
|
if (image.initWithImageData((void*)pBuffer, nSize, vt->m_FmtImage))
|
|
|
|
{
|
|
|
|
CCTexture2DPixelFormat oldPixelFormat = CCTexture2D::defaultAlphaPixelFormat();
|
|
|
|
CCTexture2D::setDefaultAlphaPixelFormat(vt->m_PixelFormat);
|
|
|
|
vt->texture->initWithImage(&image);
|
|
|
|
CCTexture2D::setDefaultAlphaPixelFormat(oldPixelFormat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-07 11:18:39 +08:00
|
|
|
break;
|
2012-04-19 14:35:52 +08:00
|
|
|
case kImageData:
|
|
|
|
{
|
|
|
|
unsigned int nPOTWide, nPOTHigh;
|
|
|
|
nPOTWide = ccNextPOT((int)vt->m_TextureSize.width);
|
|
|
|
nPOTHigh = ccNextPOT((int)vt->m_TextureSize.height);
|
|
|
|
vt->texture->initWithData(vt->m_pTextureData, vt->m_PixelFormat, nPOTWide, nPOTHigh, vt->m_TextureSize);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kString:
|
|
|
|
{
|
|
|
|
vt->texture->initWithString(vt->m_strText.c_str(),
|
|
|
|
vt->m_size,
|
|
|
|
vt->m_alignment,
|
|
|
|
vt->m_strFontName.c_str(),
|
|
|
|
vt->m_fFontSize);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2011-11-25 10:48:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
isReloading = false;
|
2011-04-06 16:29:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif // CC_ENABLE_CACHE_TEXTTURE_DATA
|
|
|
|
|
2012-03-20 15:04:53 +08:00
|
|
|
NS_CC_END
|
2010-07-16 16:28:11 +08:00
|
|
|
|