code:
/****************************************************************************
Copyright (c) 2010 cocos2d-x.org

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 "CCTextFieldTTF.h"

#include "CCDirector.h"
#include "CCEGLView.h"

NS_CC_BEGIN;

static int _calcCharCount(const char * pszText)
{
    int n = 0;
    char ch = 0;
    while ((ch = *pszText))
    {
        CC_BREAK_IF(! ch);

        if (0x80 != (0xC0 & ch))
        {
            ++n;
        }
        ++pszText;
    }
    return n;
}

//////////////////////////////////////////////////////////////////////////
// constructor and destructor
//////////////////////////////////////////////////////////////////////////

CCTextFieldTTF::CCTextFieldTTF()
: m_pDelegate(0)
, m_nCharCount(0)
, m_pInputText(new std::string)
, m_pPlaceHolder(new std::string)   // prevent CCLabelTTF initWithString assertion
{
    m_ColorSpaceHolder.r = m_ColorSpaceHolder.g = m_ColorSpaceHolder.b = 127;
}

CCTextFieldTTF::~CCTextFieldTTF()
{
    CC_SAFE_DELETE(m_pInputText);
    CC_SAFE_DELETE(m_pPlaceHolder);
}

//////////////////////////////////////////////////////////////////////////
// static constructor
//////////////////////////////////////////////////////////////////////////

CCTextFieldTTF * CCTextFieldTTF::textFieldWithPlaceHolder(const char *placeholder, const CCSize& dimensions, CCTextAlignment alignment, const char *fontName, float fontSize)
{
    CCTextFieldTTF *pRet = new CCTextFieldTTF();
    if(pRet && pRet->initWithPlaceHolder("", dimensions, alignment, fontName, fontSize))
    {
        pRet->autorelease();
        if (placeholder)
        {
            pRet->setPlaceHolder(placeholder);
        }
        return pRet;
    }
    CC_SAFE_DELETE(pRet);
    return NULL;
}

CCTextFieldTTF * CCTextFieldTTF::textFieldWithPlaceHolder(const char *placeholder, const char *fontName, float fontSize)
{
    CCTextFieldTTF *pRet = new CCTextFieldTTF();
    if(pRet && pRet->initWithString("", fontName, fontSize))
    {
        pRet->autorelease();
        if (placeholder)
        {
            pRet->setPlaceHolder(placeholder);
        }
        return pRet;
    }
    CC_SAFE_DELETE(pRet);
    return NULL;
}

//////////////////////////////////////////////////////////////////////////
// initialize
//////////////////////////////////////////////////////////////////////////

bool CCTextFieldTTF::initWithPlaceHolder(const char *placeholder, const CCSize& dimensions, CCTextAlignment alignment, const char *fontName, float fontSize)
{
    if (placeholder)
    {
        CC_SAFE_DELETE(m_pPlaceHolder);
        m_pPlaceHolder = new std::string(placeholder);
    }
    return CCLabelTTF::initWithString(m_pPlaceHolder->c_str(), dimensions, alignment, fontName, fontSize);
}
bool CCTextFieldTTF::initWithPlaceHolder(const char *placeholder, const char *fontName, float fontSize)
{
    if (placeholder)
    {
        CC_SAFE_DELETE(m_pPlaceHolder);
        m_pPlaceHolder = new std::string(placeholder);
    }
    return CCLabelTTF::initWithString(m_pPlaceHolder->c_str(), fontName, fontSize);
}

//////////////////////////////////////////////////////////////////////////
// CCIMEDelegate
//////////////////////////////////////////////////////////////////////////

bool CCTextFieldTTF::attachWithIME()
{
    bool bRet = CCIMEDelegate::attachWithIME();
    if (bRet)
    {
        // open keyboard
        CCEGLView * pGlView = CCDirector::sharedDirector()->getOpenGLView();
        if (pGlView)
        {
            pGlView->setIMEKeyboardState(true);
        }
    }
    return bRet;
}

bool CCTextFieldTTF::detachWithIME()
{
    bool bRet = CCIMEDelegate::detachWithIME();
    if (bRet)
    {
        // close keyboard
        CCEGLView * pGlView = CCDirector::sharedDirector()->getOpenGLView();
        if (pGlView)
        {
            pGlView->setIMEKeyboardState(false);
        }
    }
    return bRet;
}

bool CCTextFieldTTF::canAttachWithIME()
{
    return (m_pDelegate) ? (! m_pDelegate->onTextFieldAttachWithIME(this)) : true;
}

bool CCTextFieldTTF::canDetachWithIME()
{
    return (m_pDelegate) ? (! m_pDelegate->onTextFieldDetachWithIME(this)) : true;
}

void CCTextFieldTTF::insertText(const char * text, int len)
{
    std::string sInsert(text, len);

    // insert \n means input end
    int nPos = sInsert.find('\n');
    if ((int)sInsert.npos != nPos)
    {
        len = nPos;
        sInsert.erase(nPos);
    }

    if (len > 0)
    {
        if (m_pDelegate && m_pDelegate->onTextFieldInsertText(this, sInsert.c_str(), len))
        {
            // delegate doesn't want insert text
            return;
        }

        m_nCharCount += _calcCharCount(sInsert.c_str());
        std::string sText(*m_pInputText);
        sText.append(sInsert);
        setString(sText.c_str());
    }

    if ((int)sInsert.npos == nPos) {
        return;
    }

    // '\n' has inserted,  let delegate process first
    if (m_pDelegate && m_pDelegate->onTextFieldInsertText(this, "\n", 1))
    {
        return;
    }

    // if delegate hasn't process, detach with ime as default
    detachWithIME();
}

void CCTextFieldTTF::deleteBackward()
{
    int nStrLen = m_pInputText->length();
    if (! nStrLen)
    {
        // there is no string
        return;
    }

    // get the delete byte number
    int nDeleteLen = 1;    // default, erase 1 byte

    while(0x80 == (0xC0 & m_pInputText->at(nStrLen - nDeleteLen)))
    {
        ++nDeleteLen;
    }

    if (m_pDelegate && m_pDelegate->onTextFieldDeleteBackward(this, m_pInputText->c_str() + nStrLen - nDeleteLen, nDeleteLen))
    {
        // delegate don't wan't delete backward
        return;
    }

    // if delete all text, show space holder string
    if (nStrLen <= nDeleteLen)
    {
        CC_SAFE_DELETE(m_pInputText);
        m_pInputText = new std::string;
        m_nCharCount = 0;
        CCLabelTTF::setString(m_pPlaceHolder->c_str());
        return;
    }

    // set new input text
    std::string sText(m_pInputText->c_str(), nStrLen - nDeleteLen);
    setString(sText.c_str());
}

const char * CCTextFieldTTF::getContentText()
{
    return m_pInputText->c_str();
}

void CCTextFieldTTF::draw()
{
    if (m_pDelegate && m_pDelegate->onDraw(this))
    {
        return;
    }
    if (m_pInputText->length())
    {
        CCLabelTTF::draw();
        return;
    }

    // draw placeholder
    ccColor3B color = getColor();
    setColor(m_ColorSpaceHolder);
    CCLabelTTF::draw();
    setColor(color);
}

//////////////////////////////////////////////////////////////////////////
// properties
//////////////////////////////////////////////////////////////////////////

// input text property
void CCTextFieldTTF::setString(const char *text)
{
    CC_SAFE_DELETE(m_pInputText);

    if (text)
    {
        m_pInputText = new std::string(text);
    }
    else
    {
        m_pInputText = new std::string;
    }

    // if there is no input text, display placeholder instead
    if (! m_pInputText->length())
    {
        CCLabelTTF::setString(m_pPlaceHolder->c_str());
    }
    else
    {
        CCLabelTTF::setString(m_pInputText->c_str());
    }
    m_nCharCount = _calcCharCount(m_pInputText->c_str());
}

const char* CCTextFieldTTF::getString(void)
{
    return m_pInputText->c_str();
}

// place holder text property
void CCTextFieldTTF::setPlaceHolder(const char * text)
{
    CC_SAFE_DELETE(m_pPlaceHolder);
    m_pPlaceHolder = (text) ? new std::string(text) : new std::string;
    if (! m_pInputText->length())
    {
        CCLabelTTF::setString(m_pPlaceHolder->c_str());
    }
}

const char * CCTextFieldTTF::getPlaceHolder(void)
{
    return m_pPlaceHolder->c_str();
}

NS_CC_END;


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2008      Apple Inc. All Rights Reserved.

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.
****************************************************************************/

/*
* Support for RGBA_4_4_4_4 and RGBA_5_5_5_1 was copied from:
* https://devforums.apple.com/message/37855#37855 by a1studmuffin
*/

#include "CCTexture2D.h"

#include "ccConfig.h"
#include "ccMacros.h"
#include "CCConfiguration.h"
#include "platform/platform.h"
#include "CCImage.h"
#include "CCGL.h"
#include "support/ccUtils.h"
#include "platform/CCPlatformMacros.h"
#include "CCTexturePVR.h"
#include "CCDirector.h"

#if CC_ENABLE_CACHE_TEXTTURE_DATA
    #include "CCTextureCache.h"
#endif

namespace   cocos2d {

#if CC_FONT_LABEL_SUPPORT
// FontLabel support
#endif// CC_FONT_LABEL_SUPPORT

//CLASS IMPLEMENTATIONS:

// If the image has alpha, you can create RGBA8 (32-bit) or RGBA4 (16-bit) or RGB5A1 (16-bit)
// Default is: RGBA8888 (32-bit textures)
static CCTexture2DPixelFormat g_defaultAlphaPixelFormat = kCCTexture2DPixelFormat_Default;

// By default PVR images are treated as if they don't have the alpha channel premultiplied
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_bPVRHaveAlphaPremultiplied(true)
{
}

CCTexture2D::~CCTexture2D()
{
#if CC_ENABLE_CACHE_TEXTTURE_DATA
    VolatileTexture::removeTexture(this);
#endif

	CCLOGINFO("cocos2d: deallocing CCTexture2D %u.", m_uName);
	if(m_uName)
	{
		glDeleteTextures(1, &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;
}

const CCSize& CCTexture2D::getContentSizeInPixels()
{
	return m_tContentSize;
}

CCSize CCTexture2D::getContentSize()
{
	CCSize ret;
	ret.width = m_tContentSize.width / CC_CONTENT_SCALE_FACTOR();
	ret.height = m_tContentSize.height / CC_CONTENT_SCALE_FACTOR();

	return ret;
}

GLfloat CCTexture2D::getMaxS()
{
	return m_fMaxS;
}

void CCTexture2D::setMaxS(GLfloat maxS)
{
	m_fMaxS = maxS;
}

GLfloat CCTexture2D::getMaxT()
{
	return m_fMaxT;
}

ccResolutionType CCTexture2D::getResolutionType()
{
    return m_eResolutionType;
}

void CCTexture2D::setResolutionType(ccResolutionType resolution)
{
    m_eResolutionType = resolution;
}

void CCTexture2D::setMaxT(GLfloat maxT)
{
	m_fMaxT = maxT;
}

void CCTexture2D::releaseData(void *data)
{
    free(data);
}

void* CCTexture2D::keepData(void *data, unsigned int length)
{
    CC_UNUSED_PARAM(length);
	//The texture data mustn't be saved becuase it isn't a mutable texture.
	return data;
}

bool CCTexture2D::getHasPremultipliedAlpha()
{
	return m_bHasPremultipliedAlpha;
}

bool CCTexture2D::initWithData(const void *data, CCTexture2DPixelFormat pixelFormat, unsigned int pixelsWide, unsigned int pixelsHigh, const CCSize& contentSize)
{
	glPixelStorei(GL_UNPACK_ALIGNMENT,1);
	glGenTextures(1, &m_uName);
	glBindTexture(GL_TEXTURE_2D, m_uName);

	this->setAntiAliasTexParameters();

	// Specify OpenGL texture image

	switch(pixelFormat)
	{
	case kCCTexture2DPixelFormat_RGBA8888:
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
		break;
	case kCCTexture2DPixelFormat_RGB888:
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
		break;
	case kCCTexture2DPixelFormat_RGBA4444:
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
		break;
	case kCCTexture2DPixelFormat_RGB5A1:
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, data);
		break;
	case kCCTexture2DPixelFormat_RGB565:
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
		break;
	case kCCTexture2DPixelFormat_AI88:
		glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data);
		break;
	case kCCTexture2DPixelFormat_A8:
		glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, (GLsizei)pixelsWide, (GLsizei)pixelsHigh, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
		break;
	default:
		CCAssert(0, "NSInternalInconsistencyException");

	}

	m_tContentSize = contentSize;
	m_uPixelsWide = pixelsWide;
	m_uPixelsHigh = pixelsHigh;
	m_ePixelFormat = pixelFormat;
	m_fMaxS = contentSize.width / (float)(pixelsWide);
	m_fMaxT = contentSize.height / (float)(pixelsHigh);

	m_bHasPremultipliedAlpha = false;

	m_eResolutionType = kCCResolutionUnknown;

	return true;
}

char * CCTexture2D::description(void)
{
	char *ret = new char[100];
	sprintf(ret, "<CCTexture2D | Name = %u | Dimensions = %u x %u | Coordinates = (%.2f, %.2f)>", m_uName, m_uPixelsWide, m_uPixelsHigh, m_fMaxS, m_fMaxT);
	return ret;
}

// implementation CCTexture2D (Image)

bool CCTexture2D::initWithImage(CCImage *uiImage)
{
	return initWithImage(uiImage, kCCResolutionUnknown);
}

bool CCTexture2D::initWithImage(CCImage * uiImage, ccResolutionType resolution)
{
	unsigned int POTWide, POTHigh;

	if(uiImage == NULL)
	{
		CCLOG("cocos2d: CCTexture2D. Can't create Texture. UIImage is nil");
		this->release();
		return false;
	}

	CCConfiguration *conf = CCConfiguration::sharedConfiguration();

#if CC_TEXTURE_NPOT_SUPPORT
	if( conf->isSupportsNPOT() )
	{
		POTWide = uiImage->getWidth();
		POTHigh = uiImage->getHeight();
	}
	else
#endif
	{
		POTWide = ccNextPOT(uiImage->getWidth());
		POTHigh = ccNextPOT(uiImage->getHeight());
	}

	unsigned maxTextureSize = conf->getMaxTextureSize();
	if( POTHigh > maxTextureSize || POTWide > maxTextureSize )
	{
		CCLOG("cocos2d: WARNING: Image (%u x %u) is bigger than the supported %u x %u", POTWide, POTHigh, maxTextureSize, maxTextureSize);
		this->release();
		return NULL;
	}

	m_eResolutionType = resolution;

	// always load premultiplied images
	return initPremultipliedATextureWithImage(uiImage, POTWide, POTHigh);
}
bool CCTexture2D::initPremultipliedATextureWithImage(CCImage *image, unsigned int POTWide, unsigned int POTHigh)
{
	unsigned char*			data = NULL;
	unsigned char*			tempData =NULL;
	unsigned int*			inPixel32 = NULL;
	unsigned short*			outPixel16 = NULL;
	bool					hasAlpha;
	CCSize					imageSize;
	CCTexture2DPixelFormat	pixelFormat;

	hasAlpha = image->hasAlpha();

	size_t bpp = image->getBitsPerComponent();

    // compute pixel format
	if(hasAlpha)
	{
		pixelFormat = g_defaultAlphaPixelFormat;
	}
	else
	{
		if (bpp >= 8)
		{
			pixelFormat = kCCTexture2DPixelFormat_RGB888;
		}
		else
		{
			CCLOG("cocos2d: CCTexture2D: Using RGB565 texture since image has no alpha");
			pixelFormat = kCCTexture2DPixelFormat_RGB565;
		}
	}

	imageSize = CCSizeMake((float)(image->getWidth()), (float)(image->getHeight()));

	switch(pixelFormat) {
		case kCCTexture2DPixelFormat_RGBA8888:
		case kCCTexture2DPixelFormat_RGBA4444:
		case kCCTexture2DPixelFormat_RGB5A1:
		case kCCTexture2DPixelFormat_RGB565:
		case kCCTexture2DPixelFormat_A8:
			tempData = (unsigned char*)(image->getData());
			CCAssert(tempData != NULL, "NULL image data.");

			if(image->getWidth() == (short)POTWide && image->getHeight() == (short)POTHigh)
			{
				data = new unsigned char[POTHigh * POTWide * 4];
				memcpy(data, tempData, POTHigh * POTWide * 4);
			}
			else
			{
				data = new unsigned char[POTHigh * POTWide * 4];
				memset(data, 0, POTHigh * POTWide * 4);

				unsigned char* pPixelData = (unsigned char*) tempData;
				unsigned char* pTargetData = (unsigned char*) data;

                int imageHeight = image->getHeight();
				for(int y = 0; y < imageHeight; ++y)
				{
					memcpy(pTargetData+POTWide*4*y, pPixelData+(image->getWidth())*4*y, (image->getWidth())*4);
				}
			}

			break;
		case kCCTexture2DPixelFormat_RGB888:
			tempData = (unsigned char*)(image->getData());
			CCAssert(tempData != NULL, "NULL image data.");
			if(image->getWidth() == (short)POTWide && image->getHeight() == (short)POTHigh)
			{
				data = new unsigned char[POTHigh * POTWide * 3];
				memcpy(data, tempData, POTHigh * POTWide * 3);
			}
			else
			{
				data = new unsigned char[POTHigh * POTWide * 3];
				memset(data, 0, POTHigh * POTWide * 3);

				unsigned char* pPixelData = (unsigned char*) tempData;
				unsigned char* pTargetData = (unsigned char*) data;

				int imageHeight = image->getHeight();
				for(int y = 0; y < imageHeight; ++y)
				{
					memcpy(pTargetData+POTWide*3*y, pPixelData+(image->getWidth())*3*y, (image->getWidth())*3);
				}
			}
			break;
		default:
			CCAssert(0, "Invalid pixel format");
	}

	// Repack the pixel data into the right format

	if(pixelFormat == kCCTexture2DPixelFormat_RGB565) {
		//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGGBBBBB"
		tempData = new unsigned char[POTHigh * POTWide * 2];
		inPixel32 = (unsigned int*)data;
		outPixel16 = (unsigned short*)tempData;

		unsigned int length = POTWide * POTHigh;
		for(unsigned int i = 0; i < length; ++i, ++inPixel32)
		{
			*outPixel16++ =
				((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) |  // R
				((((*inPixel32 >> 8) & 0xFF) >> 2) << 5) |   // G
				((((*inPixel32 >> 16) & 0xFF) >> 3) << 0);   // B
		}

		delete [] data;
		data = tempData;
	}
	else if (pixelFormat == kCCTexture2DPixelFormat_RGBA4444) {
		//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRGGGGBBBBAAAA"
		tempData = new unsigned char[POTHigh * POTWide * 2];
		inPixel32 = (unsigned int*)data;
		outPixel16 = (unsigned short*)tempData;

		unsigned int length = POTWide * POTHigh;
		for(unsigned int i = 0; i < length; ++i, ++inPixel32)
		{
			*outPixel16++ =
			((((*inPixel32 >> 0) & 0xFF) >> 4) << 12) | // R
			((((*inPixel32 >> 8) & 0xFF) >> 4) << 8) | // G
			((((*inPixel32 >> 16) & 0xFF) >> 4) << 4) | // B
			((((*inPixel32 >> 24) & 0xFF) >> 4) << 0); // A
		}

		delete [] data;
		data = tempData;
	}
	else if (pixelFormat == kCCTexture2DPixelFormat_RGB5A1) {
		//Convert "RRRRRRRRRGGGGGGGGBBBBBBBBAAAAAAAA" to "RRRRRGGGGGBBBBBA"
		tempData = new unsigned char[POTHigh * POTWide * 2];
		inPixel32 = (unsigned int*)data;
		outPixel16 = (unsigned short*)tempData;

		unsigned int length = POTWide * POTHigh;
		for(unsigned int i = 0; i < length; ++i, ++inPixel32)
		{
			*outPixel16++ =
			((((*inPixel32 >> 0) & 0xFF) >> 3) << 11) | // R
			((((*inPixel32 >> 8) & 0xFF) >> 3) << 6) | // G
			((((*inPixel32 >> 16) & 0xFF) >> 3) << 1) | // B
			((((*inPixel32 >> 24) & 0xFF) >> 7) << 0); // A
		}

		delete []data;
		data = tempData;
	}
	else if (pixelFormat == kCCTexture2DPixelFormat_A8)
	{
		// fix me, how to convert to A8
		pixelFormat = kCCTexture2DPixelFormat_RGBA8888;

		/*
		 * The code can not work, how to convert to A8?
		 *
		tempData = new unsigned char[POTHigh * POTWide];
		inPixel32 = (unsigned int*)data;
		outPixel8 = tempData;

		unsigned int length = POTWide * POTHigh;
		for(unsigned int i = 0; i < length; ++i, ++inPixel32)
		{
			*outPixel8++ = (*inPixel32 >> 24) & 0xFF;
		}

		delete []data;
		data = tempData;
		*/
	}

	if (data)
	{
		this->initWithData(data, pixelFormat, POTWide, POTHigh, imageSize);

		// should be after calling super init
		m_bHasPremultipliedAlpha = image->isPremultipliedAlpha();

		//CGContextRelease(context);
		delete [] data;
	}
	return true;
}

// implementation CCTexture2D (Text)
bool CCTexture2D::initWithString(const char *text, const char *fontName, float fontSize)
{
	return initWithString(text, CCSizeMake(0,0), CCTextAlignmentCenter, fontName, fontSize);
}
bool CCTexture2D::initWithString(const char *text, const CCSize& dimensions, CCTextAlignment alignment, const char *fontName, float fontSize)
{
#if CC_ENABLE_CACHE_TEXTTURE_DATA
    // cache the texture data
    VolatileTexture::addStringTexture(this, text, dimensions, alignment, fontName, fontSize);
#endif

	CCImage image;
    CCImage::ETextAlign eAlign = (CCTextAlignmentCenter == alignment) ? CCImage::kAlignCenter
        : (CCTextAlignmentLeft == alignment) ? CCImage::kAlignLeft : CCImage::kAlignRight;

    if (! image.initWithString(text, (int)dimensions.width, (int)dimensions.height, eAlign, fontName, (int)fontSize))
    {
        return false;
    }
    return initWithImage(&image);
}

// implementation CCTexture2D (Drawing)

void CCTexture2D::drawAtPoint(const CCPoint& point)
{
	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,	0.0f,
		width + point.x,	point.y,	0.0f,
		point.x,			height  + point.y,	0.0f,
		width + point.x,	height  + point.y,	0.0f };

	glBindTexture(GL_TEXTURE_2D, m_uName);
	glVertexPointer(3, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

void CCTexture2D::drawInRect(const CCRect& rect)
{
	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*/ };

	glBindTexture(GL_TEXTURE_2D, m_uName);
	glVertexPointer(2, GL_FLOAT, 0, vertices);
	glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

#ifdef CC_SUPPORT_PVRTC
// implementation CCTexture2D (PVRTC);
bool CCTexture2D::initWithPVRTCData(const void *data, int level, int bpp, bool hasAlpha, int length, CCTexture2DPixelFormat pixelFormat)
{
	if( !(CCConfiguration::sharedConfiguration()->isSupportsPVRTC()) )
	{
		CCLOG("cocos2d: WARNING: PVRTC images is not supported.");
		this->release();
		return false;
	}

	glGenTextures(1, &m_uName);
	glBindTexture(GL_TEXTURE_2D, m_uName);

	this->setAntiAliasTexParameters();

	GLenum format;
	GLsizei size = length * length * bpp / 8;
	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

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

        this->setAntiAliasTexParameters();
        pvr->release();
    }
    else
    {
        CCLOG("cocos2d: Couldn't load PVR image %s", file);
    }

    return bRet;
}

void CCTexture2D::PVRImagesHavePremultipliedAlpha(bool haveAlphaPremultiplied)
{
    PVRHaveAlphaPremultiplied_ = haveAlphaPremultiplied;
}

//
// Use to apply MIN/MAG filter
//
// implementation CCTexture2D (GLFilter)

void CCTexture2D::generateMipmap()
{
	CCAssert( m_uPixelsWide == ccNextPOT(m_uPixelsWide) && m_uPixelsHigh == ccNextPOT(m_uPixelsHigh), "Mimpap texture only works in POT textures");
	glBindTexture( GL_TEXTURE_2D, this->m_uName );
	ccglGenerateMipmap(GL_TEXTURE_2D);
}

void CCTexture2D::setTexParameters(ccTexParams *texParams)
{
	CCAssert( (m_uPixelsWide == ccNextPOT(m_uPixelsWide) && m_uPixelsHigh == ccNextPOT(m_uPixelsHigh)) ||
		(texParams->wrapS == GL_CLAMP_TO_EDGE && texParams->wrapT == GL_CLAMP_TO_EDGE),
		"GL_CLAMP_TO_EDGE should be used in NPOT textures");
	glBindTexture( GL_TEXTURE_2D, this->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()
{
	ccTexParams texParams = { GL_NEAREST, GL_NEAREST, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
	this->setTexParameters(&texParams);
}

void CCTexture2D::setAntiAliasTexParameters()
{
	ccTexParams texParams = { GL_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE };
	this->setTexParameters(&texParams);
}

//
// Texture options for images that contains alpha
//
// implementation CCTexture2D (PixelFormat)

void CCTexture2D::setDefaultAlphaPixelFormat(CCTexture2DPixelFormat format)
{
	g_defaultAlphaPixelFormat = format;
}

CCTexture2DPixelFormat CCTexture2D::defaultAlphaPixelFormat()
{
	return g_defaultAlphaPixelFormat;
}

unsigned int CCTexture2D::bitsPerPixelForFormat()
{
	unsigned int ret = 0;

	switch (m_ePixelFormat)
	{
		case kCCTexture2DPixelFormat_RGBA8888:
			ret = 32;
			break;
		case kCCTexture2DPixelFormat_RGB565:
			ret = 16;
			break;
		case kCCTexture2DPixelFormat_A8:
			ret = 8;
			break;
		case kCCTexture2DPixelFormat_RGBA4444:
			ret = 16;
			break;
		case kCCTexture2DPixelFormat_RGB5A1:
			ret = 16;
			break;
		case kCCTexture2DPixelFormat_PVRTC4:
			ret = 4;
			break;
		case kCCTexture2DPixelFormat_PVRTC2:
			ret = 2;
			break;
		case kCCTexture2DPixelFormat_I8:
			ret = 8;
			break;
		case kCCTexture2DPixelFormat_AI88:
			ret = 16;
			break;
        case kCCTexture2DPixelFormat_RGB888:
            ret = 24;
            break;
		default:
			ret = -1;
			CCAssert(false, "illegal pixel format");
			CCLOG("bitsPerPixelForFormat: %d, cannot give useful result", m_ePixelFormat);
			break;
	}
	return ret;
}

}//namespace   cocos2d

license: mit


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011      Zynga Inc.

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.
****************************************************************************/

// cocos2d
#include "CCTextureAtlas.h"
#include "CCTextureCache.h"
#include "ccMacros.h"

// support
#include "CCTexture2D.h"

#include <stdlib.h>

//According to some tests GL_TRIANGLE_STRIP is slower, MUCH slower. Probably I'm doing something very wrong

// implementation CCTextureAtlas

namespace   cocos2d {

CCTextureAtlas::CCTextureAtlas()
    :m_pIndices(NULL)
#if CC_USES_VBO
    , m_bDirty(false)
#endif
    ,m_pTexture(NULL)
	,m_pQuads(NULL)
{}

CCTextureAtlas::~CCTextureAtlas()
{
//	CCLOGINFO("cocos2d: deallocing CCTextureAtlas.");

	CC_SAFE_FREE(m_pQuads)
	CC_SAFE_FREE(m_pIndices)

#if CC_USES_VBO
	glDeleteBuffers(2, m_pBuffersVBO);
#endif // CC_USES_VBO

	CC_SAFE_RELEASE(m_pTexture);
}

unsigned int CCTextureAtlas::getTotalQuads()
{
	return m_uTotalQuads;
}

unsigned int CCTextureAtlas::getCapacity()
{
	return m_uCapacity;
}

CCTexture2D* CCTextureAtlas::getTexture()
{
	return m_pTexture;
}

void CCTextureAtlas::setTexture(CCTexture2D * var)
{
	CC_SAFE_RETAIN(var);
	CC_SAFE_RELEASE(m_pTexture);
	m_pTexture = var;
}

ccV3F_C4B_T2F_Quad* CCTextureAtlas::getQuads()
{
	return m_pQuads;
}

void CCTextureAtlas::setQuads(ccV3F_C4B_T2F_Quad *var)
{
	m_pQuads = var;
}

// TextureAtlas - alloc & init

CCTextureAtlas * CCTextureAtlas::textureAtlasWithFile(const char* file, unsigned int capacity)
{
	CCTextureAtlas * pTextureAtlas = new CCTextureAtlas();
	if(pTextureAtlas && pTextureAtlas->initWithFile(file, capacity))
	{
		pTextureAtlas->autorelease();
		return pTextureAtlas;
	}
	CC_SAFE_DELETE(pTextureAtlas);
	return NULL;
}

CCTextureAtlas * CCTextureAtlas::textureAtlasWithTexture(CCTexture2D *texture, unsigned int capacity)
{
	CCTextureAtlas * pTextureAtlas = new CCTextureAtlas();
	if (pTextureAtlas && pTextureAtlas->initWithTexture(texture, capacity))
	{
		pTextureAtlas->autorelease();
		return pTextureAtlas;
	}
	CC_SAFE_DELETE(pTextureAtlas);
	return NULL;
}

bool CCTextureAtlas::initWithFile(const char * file, unsigned int capacity)
{
	// retained in property
	CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage(file);

	if (texture)
	{
        return initWithTexture(texture, capacity);
	}
	else
	{
		CCLOG("cocos2d: Could not open file: %s", file);
		delete this;

		return NULL;
	}
}

bool CCTextureAtlas::initWithTexture(CCTexture2D *texture, unsigned int capacity)
{
	CCAssert(texture != NULL, "texture should not be null");
	m_uCapacity = capacity;
	m_uTotalQuads = 0;

	// retained in property
	this->m_pTexture = texture;
	CC_SAFE_RETAIN(m_pTexture);

	// Re-initialization is not allowed
	CCAssert(m_pQuads == NULL && m_pIndices == NULL, "");

	m_pQuads = (ccV3F_C4B_T2F_Quad*)calloc( sizeof(ccV3F_C4B_T2F_Quad) * m_uCapacity, 1 );
	m_pIndices = (GLushort *)calloc( sizeof(GLushort) * m_uCapacity * 6, 1 );

	if( ! ( m_pQuads && m_pIndices) && m_uCapacity > 0) {
		//CCLOG("cocos2d: CCTextureAtlas: not enough memory");
		CC_SAFE_FREE(m_pQuads)
		CC_SAFE_FREE(m_pIndices)

		// release texture, should set it to null, because the destruction will
		// release it too. see cocos2d-x issue #484
		CC_SAFE_RELEASE_NULL(m_pTexture);
		return false;
	}

#if CC_USES_VBO
	// initial binding
	glGenBuffers(2, &m_pBuffersVBO[0]);
	m_bDirty = true;
#endif // CC_USES_VBO

	this->initIndices();

	return true;
}

char * CCTextureAtlas::description()
{
	char *ret = new char[100];
	sprintf(ret, "<CCTextureAtlas | totalQuads = %u>", m_uTotalQuads);
	return ret;
}

void CCTextureAtlas::initIndices()
{
	if (m_uCapacity == 0)
		return;

	for( unsigned int i=0; i < m_uCapacity; i++)
	{
#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
		m_pIndices[i*6+0] = i*4+0;
		m_pIndices[i*6+1] = i*4+0;
		m_pIndices[i*6+2] = i*4+2;
		m_pIndices[i*6+3] = i*4+1;
		m_pIndices[i*6+4] = i*4+3;
		m_pIndices[i*6+5] = i*4+3;
#else
		m_pIndices[i*6+0] = (GLushort)(i*4+0);
		m_pIndices[i*6+1] = (GLushort)(i*4+1);
		m_pIndices[i*6+2] = (GLushort)(i*4+2);

		// inverted index. issue #179
		m_pIndices[i*6+3] = (GLushort)(i*4+3);
		m_pIndices[i*6+4] = (GLushort)(i*4+2);
		m_pIndices[i*6+5] = (GLushort)(i*4+1);
		//		m_pIndices[i*6+3] = i*4+2;
		//		m_pIndices[i*6+4] = i*4+3;
		//		m_pIndices[i*6+5] = i*4+1;
#endif
	}

#if CC_USES_VBO
	glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0]) * m_uCapacity, m_pQuads, GL_DYNAMIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uCapacity * 6, m_pIndices, GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#endif // CC_USES_VBO
}

// TextureAtlas - Update, Insert, Move & Remove

void CCTextureAtlas::updateQuad(ccV3F_C4B_T2F_Quad *quad, unsigned int index)
{
	CCAssert( index >= 0 && index < m_uCapacity, "updateQuadWithTexture: Invalid index");

	m_uTotalQuads = max( index+1, m_uTotalQuads);

	m_pQuads[index] = *quad;

#if CC_USES_VBO
	m_bDirty = true;
#endif
}

void CCTextureAtlas::insertQuad(ccV3F_C4B_T2F_Quad *quad, unsigned int index)
{
	CCAssert( index < m_uCapacity, "insertQuadWithTexture: Invalid index");

	m_uTotalQuads++;
	CCAssert( m_uTotalQuads <= m_uCapacity, "invalid totalQuads");

	// issue #575. index can be > totalQuads
	unsigned int remaining = (m_uTotalQuads-1) - index;

	// last object doesn't need to be moved
	if( remaining > 0) {
		// texture coordinates
		memmove( &m_pQuads[index+1],&m_pQuads[index], sizeof(m_pQuads[0]) * remaining );
	}

	m_pQuads[index] = *quad;

#if CC_USES_VBO
	m_bDirty = true;
#endif
}

void CCTextureAtlas::insertQuadFromIndex(unsigned int oldIndex, unsigned int newIndex)
{
	CCAssert( newIndex >= 0 && newIndex < m_uTotalQuads, "insertQuadFromIndex:atIndex: Invalid index");
	CCAssert( oldIndex >= 0 && oldIndex < m_uTotalQuads, "insertQuadFromIndex:atIndex: Invalid index");

	if( oldIndex == newIndex )
		return;

	// because it is ambigious in iphone, so we implement abs ourself
	// unsigned int howMany = abs( oldIndex - newIndex);
	unsigned int howMany = (oldIndex - newIndex) > 0 ? (oldIndex - newIndex) :  (newIndex - oldIndex);
	unsigned int dst = oldIndex;
	unsigned int src = oldIndex + 1;
	if( oldIndex > newIndex) {
		dst = newIndex+1;
		src = newIndex;
	}

	// texture coordinates
	ccV3F_C4B_T2F_Quad quadsBackup = m_pQuads[oldIndex];
	memmove( &m_pQuads[dst],&m_pQuads[src], sizeof(m_pQuads[0]) * howMany );
	m_pQuads[newIndex] = quadsBackup;

#if CC_USES_VBO
	m_bDirty = true;
#endif
}

void CCTextureAtlas::removeQuadAtIndex(unsigned int index)
{
	CCAssert( index < m_uTotalQuads, "removeQuadAtIndex: Invalid index");

	unsigned int remaining = (m_uTotalQuads-1) - index;

	// last object doesn't need to be moved
	if( remaining ) {
		// texture coordinates
		memmove( &m_pQuads[index],&m_pQuads[index+1], sizeof(m_pQuads[0]) * remaining );
	}

	m_uTotalQuads--;

#if CC_USES_VBO
	m_bDirty = true;
#endif
}

void CCTextureAtlas::removeAllQuads()
{
	m_uTotalQuads = 0;
}

// TextureAtlas - Resize
bool CCTextureAtlas::resizeCapacity(unsigned int newCapacity)
{
	if( newCapacity == m_uCapacity )
		return true;

	// update capacity and totolQuads
	m_uTotalQuads = min(m_uTotalQuads, newCapacity);
	m_uCapacity = newCapacity;

	void * tmpQuads = NULL;
	void * tmpIndices = NULL;

	// when calling initWithTexture(fileName, 0) on bada device, calloc(0, 1) will fail and return NULL,
	// so here must judge whether m_pQuads and m_pIndices is NULL.
	if (m_pQuads == NULL)
		tmpQuads = calloc(sizeof(m_pQuads[0]) * m_uCapacity, 1);
	else
		tmpQuads = realloc( m_pQuads, sizeof(m_pQuads[0]) * m_uCapacity );

	if (m_pIndices == NULL)
		tmpIndices = calloc(sizeof(m_pIndices[0]) * m_uCapacity * 6, 1);
	else
		tmpIndices = realloc( m_pIndices, sizeof(m_pIndices[0]) * m_uCapacity * 6 );

	if( ! ( tmpQuads && tmpIndices) ) {
		//CCLOG("cocos2d: CCTextureAtlas: not enough memory");
		if( tmpQuads )
			free(tmpQuads);
		else
			free(m_pQuads);

		if( tmpIndices )
			free(tmpIndices);
		else
			free(m_pIndices);

		m_pQuads = NULL;
		m_pIndices = NULL;
		m_uCapacity = m_uTotalQuads = 0;
		return false;
	}

	m_pQuads = (ccV3F_C4B_T2F_Quad *)tmpQuads;
	m_pIndices = (GLushort *)tmpIndices;

#if CC_USES_VBO
	glDeleteBuffers(2, m_pBuffersVBO);
	// initial binding
	glGenBuffers(2, &m_pBuffersVBO[0]);
	m_bDirty = true;
#endif // CC_USES_VBO

	this->initIndices();

#if CC_USES_VBO
	m_bDirty = true;
#endif

	return true;
}

// TextureAtlas - Drawing

void CCTextureAtlas::drawQuads()
{
	this->drawNumberOfQuads(m_uTotalQuads, 0);
}

void CCTextureAtlas::drawNumberOfQuads(unsigned int n)
{
	this->drawNumberOfQuads(n, 0);
}

void CCTextureAtlas::drawNumberOfQuads(unsigned int n, unsigned int start)
{
	// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	// Unneeded states: -
	if (0 == n)
		return;

	glBindTexture(GL_TEXTURE_2D, m_pTexture->getName());

#define kQuadSize sizeof(m_pQuads[0].bl)

#if CC_USES_VBO

    glBindBuffer(GL_ARRAY_BUFFER, m_pBuffersVBO[0]);

#if CC_ENABLE_CACHE_TEXTTURE_DATA
    glBufferData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0]) * m_uCapacity, m_pQuads, GL_DYNAMIC_DRAW);
#endif

	// XXX: update is done in draw... perhaps it should be done in a timer
	if (m_bDirty)
	{
		glBufferSubData(GL_ARRAY_BUFFER, sizeof(m_pQuads[0]) * start, sizeof(m_pQuads[0]) * n, &m_pQuads[start]);
		m_bDirty = false;
	}

	// vertices
	glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, vertices));

	// colors
	glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, colors));

	// texture coords
	glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*) offsetof( ccV3F_C4B_T2F, texCoords));

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_pBuffersVBO[1]);

#if CC_ENABLE_CACHE_TEXTTURE_DATA
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(m_pIndices[0]) * m_uCapacity * 6, m_pIndices, GL_STATIC_DRAW);
#endif

#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
	glDrawElements(GL_TRIANGLE_STRIP, (GLsizei)n*6, GL_UNSIGNED_SHORT, (GLvoid*)(start * 6 * sizeof(m_pIndices[0])));
#else
	glDrawElements(GL_TRIANGLES, (GLsizei)n*6, GL_UNSIGNED_SHORT, (GLvoid*)(start * 6 * sizeof(m_pIndices[0])));
#endif // CC_USES_VBO

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

#else // ! CC_USES_VBO

	unsigned int offset = (unsigned int)m_pQuads;

	// vertex
	unsigned int diff = offsetof( ccV3F_C4B_T2F, vertices);
	glVertexPointer(3, GL_FLOAT, kQuadSize, (GLvoid*) (offset + diff) );

	// color
	diff = offsetof( ccV3F_C4B_T2F, colors);
	glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (GLvoid*)(offset + diff));

	// texture coords
	diff = offsetof( ccV3F_C4B_T2F, texCoords);
	glTexCoordPointer(2, GL_FLOAT, kQuadSize, (GLvoid*)(offset + diff));

#if CC_TEXTURE_ATLAS_USE_TRIANGLE_STRIP
	glDrawElements(GL_TRIANGLE_STRIP, n*6, GL_UNSIGNED_SHORT, m_pIndices + start * 6);
#else
	glDrawElements(GL_TRIANGLES, n*6, GL_UNSIGNED_SHORT, m_pIndices + start * 6);
#endif

#endif // CC_USES_VBO
}

}//namespace   cocos2d


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2008-2010 Ricardo Quesada
Copyright (c) 2011      Zynga Inc.

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>
#include <cctype>
#include <queue>
#include "CCTextureCache.h"
#include "CCTexture2D.h"
#include "ccMacros.h"
#include "CCData.h"
#include "CCDirector.h"
#include "platform/platform.h"
#include "CCFileUtils.h"
#include "CCImage.h"
#include "support/ccUtils.h"
#include "CCScheduler.h"
#include "pthread.h"
#include "CCThread.h"
#include "semaphore.h"

namespace   cocos2d {

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_sem;
static bool need_quit;

static std::queue<AsyncStruct*>		*s_pAsyncStructQueue;
static std::queue<ImageInfo*>		*s_pImageQueue;

static CCImage::EImageFormat computeImageFormatType(string& filename)
{
	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;
}

static void* loadImage(void* data)
{
	// create autorelease pool for iOS
	CCThread thread;
	thread.createAutoreleasePool();

    AsyncStruct *pAsyncStruct = NULL;

	while (true)
	{
		// wait for rendering thread to ask for loading if s_pAsyncStructQueue is empty
		sem_wait(&s_sem);

		std::queue<AsyncStruct*> *pQueue = s_pAsyncStructQueue;

		pthread_mutex_lock(&s_asyncStructQueueMutex);// get async struct from queue
        if (pQueue->empty())
        {
            pthread_mutex_unlock(&s_asyncStructQueueMutex);
	if (need_quit)
		break;
	else
		continue;
        }
        else
        {
            pAsyncStruct = pQueue->front();
            pQueue->pop();
            pthread_mutex_unlock(&s_asyncStructQueueMutex);
        }

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

// implementation CCTextureCache

// TextureCache - Alloc, Init & Dealloc
static CCTextureCache *g_sharedTextureCache;

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 CCMutableDictionary<std::string, CCTexture2D*>();
}

CCTextureCache::~CCTextureCache()
{
	CCLOGINFO("cocos2d: deallocing CCTextureCache.");
	need_quit = true;
	sem_post(&s_sem);
	CC_SAFE_RELEASE(m_pTextures);
}

void CCTextureCache::purgeSharedTextureCache()
{
	CC_SAFE_RELEASE_NULL(g_sharedTextureCache);
}

char * CCTextureCache::description()
{
	char *ret = new char[100];
	sprintf(ret, "<CCTextureCache | Number of textures = %u>", m_pTextures->count());
	return ret;
}

void CCTextureCache::addImageAsync(const char *path, CCObject *target, SEL_CallFuncO selector)
{
	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 = m_pTextures->objectForKey(pathKey);

	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)
	{
        s_pAsyncStructQueue = new queue<AsyncStruct*>();
	    s_pImageQueue = new queue<ImageInfo*>();

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

		CCScheduler::sharedScheduler()->scheduleSelector(schedule_selector(CCTextureCache::addImageAsyncCallBack), this, 0, false);
		need_quit = false;
		firstRun = false;
	}

	// generate async struct
	AsyncStruct *data = new AsyncStruct();
	data->filename = fullpath.c_str();
	data->target = target;
	data->selector = selector;

	// add async struct into queue
	pthread_mutex_lock(&s_asyncStructQueueMutex);
	s_pAsyncStructQueue->push(data);
	pthread_mutex_unlock(&s_asyncStructQueueMutex);

	sem_post(&s_sem);
}

void CCTextureCache::addImageAsyncCallBack(ccTime dt)
{
	// 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();
		texture->initWithImage(pImage);

#if CC_ENABLE_CACHE_TEXTTURE_DATA
        // cache the texture file name
		if (pImageInfo->imageType == CCImage::kFmtJpg)
		{
			VolatileTexture::addImageTexture(texture, filename, CCImage::kFmtJpg);
		}
		else
		{
			VolatileTexture::addImageTexture(texture, filename, CCImage::kFmtPng);
		}
#endif

		// cache the texture
		m_pTextures->setObject(texture, filename);
		texture->autorelease();

		if (target && selector)
		{
			(target->*selector)(texture);
			target->release();
		}

		delete pImage;
		delete pAsyncStruct;
		delete pImageInfo;
	}
}

CCTexture2D * CCTextureCache::addImage(const char * path)
{
	CCAssert(path != NULL, "TextureCache: fileimage MUST not be NULL");

	CCTexture2D * texture = NULL;
	// Split up directory and filename
	// MUTEX:
	// Needed since addImageAsync calls this method from a different thread

	//pthread_mutex_lock(m_pDictLock);

	// remove possible -HD suffix to prevent caching the same image twice (issue #1040)
    std::string pathKey = path;
	CCFileUtils::removeSuffixFromFile(pathKey);

    pathKey = CCFileUtils::fullPathFromRelativePath(pathKey.c_str());
	texture = m_pTextures->objectForKey(pathKey);

    std::string fullpath = pathKey; // (CCFileUtils::fullPathFromRelativePath(path));
	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;
                CCFileData data(fullpath.c_str(), "rb");
                unsigned long nSize  = data.getSize();
                unsigned char* pBuffer = data.getBuffer();
                CC_BREAK_IF(! image.initWithImageData((void*)pBuffer, nSize, CCImage::kFmtJpg));

                ccResolutionType resolution;
                fullpath = CCFileUtils::fullPathFromRelativePath(fullpath.c_str(), &resolution);
				texture = new CCTexture2D();
				texture->initWithImage(&image, resolution);

				if( texture )
				{
#if CC_ENABLE_CACHE_TEXTTURE_DATA
                    // cache the texture file name
                    VolatileTexture::addImageTexture(texture, fullpath.c_str(), CCImage::kFmtJpg);
#endif

					m_pTextures->setObject(texture, pathKey);
					// 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;
                CCFileData data(fullpath.c_str(), "rb");
                unsigned long nSize  = data.getSize();
                unsigned char* pBuffer = data.getBuffer();
                CC_BREAK_IF(! image.initWithImageData((void*)pBuffer, nSize, CCImage::kFmtPng));

                ccResolutionType resolution;
                fullpath = CCFileUtils::fullPathFromRelativePath(fullpath.c_str(), &resolution);
				texture = new CCTexture2D();
				texture->initWithImage(&image, resolution);

				if( texture )
				{
#if CC_ENABLE_CACHE_TEXTTURE_DATA
                    // cache the texture file name
                    VolatileTexture::addImageTexture(texture, fullpath.c_str(), CCImage::kFmtPng);
#endif

					m_pTextures->setObject(texture, pathKey);
					// 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;
}

#ifdef CC_SUPPORT_PVRTC
CCTexture2D* CCTextureCache::addPVRTCImage(const char* path, int bpp, bool hasAlpha, int width)
{
	CCAssert(path != NULL, "TextureCache: fileimage MUST not be nill");
	CCAssert( bpp==2 || bpp==4, "TextureCache: bpp must be either 2 or 4");

	CCTexture2D * texture;

	std::string temp(path);
    CCFileUtils::removeSuffixFromFile(temp);

	if ( (texture = m_pTextures->objectForKey(temp)) )
	{
		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,
                                   (bpp==2 ? kCCTexture2DPixelFormat_PVRTC2 : kCCTexture2DPixelFormat_PVRTC4)))
	{
		m_pTextures->setObject(texture, temp);
		texture->autorelease();
	}
	else
	{
		CCLOG("cocos2d: Couldn't add PVRTCImage:%s in CCTextureCache",path);
	}
	CC_SAFE_DELETE(data);

	return texture;
}
#endif // CC_SUPPORT_PVRTC

CCTexture2D * CCTextureCache::addPVRImage(const char* path)
{
	CCAssert(path != NULL, "TextureCache: fileimage MUST not be nill");

	CCTexture2D * tex;
	std::string key(path);
    // remove possible -HD suffix to prevent caching the same image twice (issue #1040)
    CCFileUtils::removeSuffixFromFile(key);

	if( (tex = m_pTextures->objectForKey(key)) )
	{
		return tex;
	}

    // Split up directory and filename
    std::string fullpath = CCFileUtils::fullPathFromRelativePath(key.c_str());
	tex = new CCTexture2D();
	if( tex->initWithPVRFile(fullpath.c_str()) )
	{
#if CC_ENABLE_CACHE_TEXTTURE_DATA
        // cache the texture file name
        VolatileTexture::addImageTexture(tex, fullpath.c_str(), CCImage::kFmtRawData);
#endif
		m_pTextures->setObject(tex, key);
		tex->autorelease();
	}
	else
	{
		CCLOG("cocos2d: Couldn't add PVRImage:%s in CCTextureCache",key.c_str());
	}

	return tex;
}

CCTexture2D* CCTextureCache::addUIImage(CCImage *image, const char *key)
{
	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 = m_pTextures->objectForKey(forKey)))
		{
			break;
		}

		// prevents overloading the autorelease pool
		texture = new CCTexture2D();
		texture->initWithImage(image, kCCResolutionUnknown);

		if(key && texture)
		{
			m_pTextures->setObject(texture, forKey);
			texture->autorelease();
		}
		else
		{
			CCLOG("cocos2d: Couldn't add UIImage in CCTextureCache");
		}

	} while (0);

	return texture;
}

// TextureCache - Remove

void CCTextureCache::removeAllTextures()
{
	m_pTextures->removeAllObjects();
}

void CCTextureCache::removeUnusedTextures()
{
	std::vector<std::string> keys = m_pTextures->allKeys();
	std::vector<std::string>::iterator it;
	for (it = keys.begin(); it != keys.end(); ++it)
	{
		CCTexture2D *value = m_pTextures->objectForKey(*it);
		if (value->retainCount() == 1)
		{
			CCLOG("cocos2d: CCTextureCache: removing unused texture: %s", (*it).c_str());
			m_pTextures->removeObjectForKey(*it);
		}
	}
}

void CCTextureCache::removeTexture(CCTexture2D* texture)
{
	if( ! texture )
		return;

	std::vector<std::string> keys = m_pTextures->allKeysForObject(texture);

	for (unsigned int i = 0; i < keys.size(); i++)
	{
		m_pTextures->removeObjectForKey(keys[i]);
	}
}

void CCTextureCache::removeTextureForKey(const char *textureKeyName)
{
	if (textureKeyName == NULL)
	{
		return;
	}

    string fullPath = CCFileUtils::fullPathFromRelativePath(textureKeyName);
	m_pTextures->removeObjectForKey(fullPath);
}

CCTexture2D* CCTextureCache::textureForKey(const char* key)
{
    std::string strKey = CCFileUtils::fullPathFromRelativePath(key);
	return m_pTextures->objectForKey(strKey);
}

void CCTextureCache::reloadAllTextures()
{
#if CC_ENABLE_CACHE_TEXTTURE_DATA
    VolatileTexture::reloadAllTextures();
#endif
}

void CCTextureCache::dumpCachedTextureInfo()
{
	unsigned int count = 0;
	unsigned int totalBytes = 0;

	vector<string> keys = m_pTextures->allKeys();
	vector<string>::iterator iter;
	for (iter = keys.begin(); iter != keys.end(); iter++)
	{
		CCTexture2D *tex = m_pTextures->objectForKey(*iter);
		unsigned int bpp = tex->bitsPerPixelForFormat();
        // Each texture takes up width * height * bytesPerPixel bytes.
		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",
			   (*iter).c_str(),
			   (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_TEXTTURE_DATA

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(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)
{
    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 = kImageFile;
    vt->m_strFileName = imageFileName;
    vt->m_FmtImage    = format;
    vt->m_PixelFormat = tt->getPixelFormat();
}

void VolatileTexture::addDataTexture(CCTexture2D *tt, void* data, CCTexture2DPixelFormat pixelFormat, const CCSize& contentSize)
{
	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;
}

void VolatileTexture::addStringTexture(CCTexture2D *tt, const char* text, const CCSize& dimensions, CCTextAlignment alignment, const char *fontName, float fontSize)
{
    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 = kString;
    vt->m_size        = dimensions;
    vt->m_strFontName = fontName;
    vt->m_alignment   = alignment;
    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");
    std::list<VolatileTexture *>::iterator i = textures.begin();

    while( i != textures.end() )
    {
        VolatileTexture *vt = *i++;

		switch (vt->m_eCashedImageType)
		{
        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);
                    }
                }
            }
            break;
		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;
		}
    }

    isReloading = false;
}

#endif // CC_ENABLE_CACHE_TEXTTURE_DATA

}//namespace   cocos2d


code:
/****************************************************************************
Copyright (c) 2011		Jirka Fajfr for cocos2d-x
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2008      Apple Inc. All Rights Reserved.

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 "CCTexture2D.h"
#include "CCTexturePVR.h"
#include "ccMacros.h"
#include "CCData.h"
#include "CCConfiguration.h"
#include "support/ccUtils.h"
#include "CCStdC.h"
#include "CCFileUtils.h"
#include "support/zip_support/ZipUtils.h"

#include <cctype>

namespace   cocos2d {

#define PVR_TEXTURE_FLAG_TYPE_MASK	0xff
#define PVR_TEXTURE_FLAG_FLIPPED_MASK 0x10000

// Values taken from PVRTexture.h from http://www.imgtec.com
enum {
    kPVRTextureFlagMipmap		= (1<<8),		// has mip map levels
    kPVRTextureFlagTwiddle		= (1<<9),		// is twiddled
    kPVRTextureFlagBumpmap		= (1<<10),		// has normals encoded for a bump map
    kPVRTextureFlagTiling		= (1<<11),		// is bordered for tiled pvr
    kPVRTextureFlagCubemap		= (1<<12),		// is a cubemap/skybox
    kPVRTextureFlagFalseMipCol	= (1<<13),		// are there false coloured MIP levels
    kPVRTextureFlagVolume		= (1<<14),		// is this a volume texture
    kPVRTextureFlagAlpha		= (1<<15),		// v2.1 is there transparency info in the texture
    kPVRTextureFlagVerticalFlip	= (1<<16),		// v2.1 is the texture vertically flipped
};

/*
	PVR header contains special field called PVRTag. This tag
	is used only to determine that passed file is really pvrt.

	Its set of 4 numbers. Each number casted to the char
	must correspond to the "PVR!" string
*/
static unsigned int gPVRTexIdentifier[] = { 'P', 'V', 'R', '!'};

/*
	List of formats in pvr container
*/
enum
{
	kPVRTextureFlagTypeRGBA_4444= 0x10,
	kPVRTextureFlagTypeRGBA_5551,
	kPVRTextureFlagTypeRGBA_8888,
	kPVRTextureFlagTypeRGB_565,
	kPVRTextureFlagTypeRGB_555,				// unsupported
	kPVRTextureFlagTypeRGB_888,				// unsupported
	kPVRTextureFlagTypeI_8,
	kPVRTextureFlagTypeAI_88,
	kPVRTextureFlagTypePVRTC_2,
	kPVRTextureFlagTypePVRTC_4,
	kPVRTextureFlagTypeBGRA_8888,
	kPVRTextureFlagTypeA_8,
};

/*
	Helps us to convert pvr format to the gl compatible bitspaces

	WARNING!! OpenGL ES 1.1. does not support GL_BGRA format. Its PowerVR
	extension and it will work only on PowerVR chipsets. It means that

	kPVRTextureFlagTypeBGRA_8888 can be used only on apple devices (or PowerVR compatible)
*/
static const unsigned int tableFormats[][7] = {

	// - PVR texture format
	// - OpenGL internal format
	// - OpenGL format
	// - OpenGL type
	// - bpp
	// - compressed
    // - Cocos2d texture format constant
	{ kPVRTextureFlagTypeRGBA_4444, GL_RGBA,	GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,	16, false, kCCTexture2DPixelFormat_RGBA4444	},
	{ kPVRTextureFlagTypeRGBA_5551, GL_RGBA,	GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1,	16, false, kCCTexture2DPixelFormat_RGB5A1	},
	{ kPVRTextureFlagTypeRGBA_8888, GL_RGBA,	GL_RGBA, GL_UNSIGNED_BYTE,			32, false, kCCTexture2DPixelFormat_RGBA8888	},
	{ kPVRTextureFlagTypeRGB_565,	GL_RGB,		GL_RGB,	 GL_UNSIGNED_SHORT_5_6_5,	16, false, kCCTexture2DPixelFormat_RGB565	},
    { kPVRTextureFlagTypeA_8,		GL_ALPHA,	GL_ALPHA,	GL_UNSIGNED_BYTE,		8,	false, kCCTexture2DPixelFormat_A8	    },
	{ kPVRTextureFlagTypeI_8,		GL_LUMINANCE,	GL_LUMINANCE,	GL_UNSIGNED_BYTE,			8,	false, kCCTexture2DPixelFormat_I8	},
	{ kPVRTextureFlagTypeAI_88,		GL_LUMINANCE_ALPHA,	GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,	16,	false, kCCTexture2DPixelFormat_AI88 },
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
	{ kPVRTextureFlagTypePVRTC_2,	GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, -1, -1,	2,	true, kCCTexture2DPixelFormat_PVRTC2 },
	{ kPVRTextureFlagTypePVRTC_4,	GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, -1, -1,	4,	true, kCCTexture2DPixelFormat_PVRTC4 },
	{ kPVRTextureFlagTypeBGRA_8888, GL_RGBA,	GL_BGRA, GL_UNSIGNED_BYTE,			32,	false, kCCTexture2DPixelFormat_RGBA8888	},
#endif
};

//Tells How large is tableFormats
#define MAX_TABLE_ELEMENTS (sizeof(tableFormats) / sizeof(tableFormats[0]))

/*
	Helper enum to traverse tableFormats
*/
enum {
	kCCInternalPVRTextureFormat,
	kCCInternalOpenGLInternalFormat,
	kCCInternalOpenGLFormat,
	kCCInternalOpenGLType,
	kCCInternalBPP,
	kCCInternalCompressedImage,
    kCCInternalCCTexture2DPixelFormat,
};

/*
	Official PVRT header
*/
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;
	unsigned int pvrTag;
	unsigned int numSurfs;
} PVRTexHeader;

CCTexturePVR::CCTexturePVR() :
    m_uTableFormatIndex(0),
	m_uNumberOfMipmaps(0)
{
}

CCTexturePVR::~CCTexturePVR()
{
	CCLOGINFO( "cocos2d: deallocing CCTexturePVR" );

	if (m_uName != 0 && ! m_bRetainName )
		glDeleteTextures(1, &m_uName);
}

GLuint CCTexturePVR::getName()
{
	return m_uName;
}

unsigned int CCTexturePVR::getWidth()
{
	return m_uWidth;
}

unsigned int CCTexturePVR::getHeight()
{
	return m_uHeight;
}

CCTexture2DPixelFormat CCTexturePVR::getFormat()
{
	return m_eFormat;
}

bool CCTexturePVR::getHasAlpha()
{
	return m_bHasAlpha;
}

bool CCTexturePVR::getRetainName()
{
	return m_bRetainName;
}

void CCTexturePVR::setRetainName(bool var)
{
	m_bRetainName = var;
}

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;

	//Cast first sizeof(PVRTexHeader) bytes of data stream as PVRTexHeader
    header = (PVRTexHeader *)data;

	//Make sure that tag is in correct formatting
	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))
	{
		return false;
	}

    CCConfiguration *configuration = CCConfiguration::sharedConfiguration();

	//Get file flags (in correct byte order)
	flags = CC_SWAP_INT32_LITTLE_TO_HOST(header->flags);

	//Trim to only bites which are needed. Resulting flag is image format
	formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;

	/*
     When flags combined with flipped mask is not empty (zero).
     It means that image is compressed as flipped. We don't
     support automatic flipping.
     */
    bool flipped = (bool)(flags & kPVRTextureFlagVerticalFlip);
    if ( flipped )
    {
        CCLOG("cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool");
    }

    if ( ! configuration->isSupportsNPOT() &&
        (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;
    }

	//Check that sides of texture are power of two
	if(header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height))
	{
		CCLOG("cocos2d: WARNING: PVR NPOT textures are not supported. Regenerate it.");
		return false;
	}

	//Go thru format array
	for (m_uTableFormatIndex = 0; m_uTableFormatIndex < (unsigned int)MAX_TABLE_ELEMENTS; m_uTableFormatIndex++)
	{
		//Does image format in table fits to the one parsed from header?
		if (tableFormats[m_uTableFormatIndex][kCCInternalPVRTextureFormat] == formatFlags)
		{
			//Reset num of mipmaps
			m_uNumberOfMipmaps = 0;

			//Get size of maimap
			m_uWidth = width = CC_SWAP_INT32_LITTLE_TO_HOST(header->width);
			m_uHeight = height = CC_SWAP_INT32_LITTLE_TO_HOST(header->height);

			//Do we use alpha ?
			if (CC_SWAP_INT32_LITTLE_TO_HOST(header->bitmaskAlpha))
				m_bHasAlpha = true;
			else
				m_bHasAlpha = false;

			//Get ptr to where data starts..
			dataLength = CC_SWAP_INT32_LITTLE_TO_HOST(header->dataLength);

			//Move by size of header
			bytes = ((unsigned char *)data) + sizeof(PVRTexHeader);
            m_eFormat = (CCTexture2DPixelFormat)( tableFormats[m_uTableFormatIndex][kCCInternalCCTexture2DPixelFormat] );
            bpp = tableFormats[m_uTableFormatIndex][kCCInternalBPP];

			// Calculate the data size for each texture level and respect the minimum number of blocks
			while (dataOffset < dataLength)
			{
				switch (formatFlags) {
					case kPVRTextureFlagTypePVRTC_2:
						blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
						widthBlocks = width / 8;
						heightBlocks = height / 4;
						bpp = 2;
						break;
					case kPVRTextureFlagTypePVRTC_4:
						blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
						widthBlocks = width / 4;
						heightBlocks = height / 4;
						bpp = 4;
						break;
					case kPVRTextureFlagTypeBGRA_8888:
						if (CCConfiguration::sharedConfiguration()->isSupportsBGRA8888() == false)
						{
							CCLOG("cocos2d: TexturePVR. BGRA8888 not supported on this device");
							return false;
						}
					default:
						blockSize = 1;
						widthBlocks = width;
						heightBlocks = height;
						bpp = tableFormats[m_uTableFormatIndex][kCCInternalBPP];
						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;

				//Make record to the mipmaps array and increment coutner
				m_asMipmaps[m_uNumberOfMipmaps].address = bytes+dataOffset;
				m_asMipmaps[m_uNumberOfMipmaps].len = packetLength;
				m_uNumberOfMipmaps++;

				//Check that we didn't overflow
				CCAssert(m_uNumberOfMipmaps < CC_PVRMIPMAP_MAX,
                         "TexturePVR: Maximum number of mimpaps reached. Increate the CC_PVRMIPMAP_MAX value");

				dataOffset += packetLength;

				//Update width and height to the next lower power of two
				width = MAX(width >> 1, 1);
				height = MAX(height >> 1, 1);
			}

			//Mark pass as success
			success = true;
			break;
		}
	}

	if (false == success)
    {
		CCLOG("cocos2d: WARNING: Unssupported PVR Pixel Format: 0x%2x", formatFlags);
    }

	return success;
}

bool CCTexturePVR::createGLTexture()
{
	unsigned int width = m_uWidth;
	unsigned int height = m_uHeight;
	GLenum err;

	if (m_uNumberOfMipmaps > 0)
	{
		if (m_uName != 0)
        {
			glDeleteTextures(1, &m_uName);
        }

		glGenTextures(1, &m_uName);
		glBindTexture(GL_TEXTURE_2D, m_uName);
	}

	// Generate textures with mipmaps
	for (unsigned int i = 0; i < m_uNumberOfMipmaps; ++i)
	{
		GLenum internalFormat = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLInternalFormat];
		GLenum format = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLFormat];
		GLenum type = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLType];
		bool compressed = tableFormats[m_uTableFormatIndex][kCCInternalCompressedImage] == 1;

		if (compressed == true && CCConfiguration::sharedConfiguration()->isSupportsPVRTC() == false)
		{
			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 == true)
		{
			glCompressedTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, datalen, data);
		}
		else
		{
			glTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, format, type, data);
		}

		if(i > 0 && (width != height || ccNextPOT(width) != width ) )
        {
			CCLOG("cocos2d: TexturePVR. WARNING. Mipmap level %lu is not squared. Texture won't render correctly. width=%lu != height=%lu", i, width, height);
        }

		err = glGetError();
		if (err != GL_NO_ERROR)
		{
			CCLOG("cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", (unsigned int)i, err);
			return false;
		}

		//Update width and height to the next lower power of two
		width = MAX(width >> 1, 1);
		height = MAX(height >> 1, 1);
	}

	return true;
}

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

    if (lowerCase.find(".ccz") != std::string::npos)
    {
        pvrlen = ZipUtils::ccInflateCCZFile(path, &pvrdata);
    }
    else if (lowerCase.find(".gz") != std::string::npos)
    {
        pvrlen = ZipUtils::ccInflateGZipFile(path, &pvrdata);
    }
    else
    {
		pvrdata = CCFileUtils::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; // cocos2d integration

	if ( !unpackPVRData(pvrdata, pvrlen)  || !createGLTexture() )
	{
		delete [] pvrdata;
		this->release();
		return false;
	}

    delete [] pvrdata;

	return true;
}

CCTexturePVR * CCTexturePVR::pvrTextureWithContentsOfFile(const char* path)
{
	CCTexturePVR * pTexture = new CCTexturePVR();
	if(true == pTexture->initWithContentsOfFile(path))
    {
        pTexture->autorelease();
        return pTexture;
    }
    else
    {
        return NULL;
    }
}

}//namespace   cocos2d


code:
/****************************************************************************
Copyright (c) 2010-2011 cocos2d-x.org
Copyright (c) 2009-2010 Ricardo Quesada
Copyright (c) 2011      Zynga Inc.

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 "CCParallaxNode.h"
#include "CCPointExtension.h"
#include "support/data_support/ccCArray.h"

namespace cocos2d {

	class CCPointObject : CCObject
	{
		CC_SYNTHESIZE(CCPoint, m_tRatio, Ratio)
		CC_SYNTHESIZE(CCPoint, m_tOffset, Offset)
		CC_SYNTHESIZE(CCNode *,m_pChild, Child)	// weak ref

		static CCPointObject * pointWithCCPoint(CCPoint ratio, CCPoint offset)
		{
			CCPointObject *pRet = new CCPointObject();
			pRet->initWithCCPoint(ratio, offset);
			pRet->autorelease();
			return pRet;
		}
		bool initWithCCPoint(CCPoint ratio, CCPoint offset)
		{
			m_tRatio = ratio;
			m_tOffset = offset;
			m_pChild = NULL;
			return true;
		}
	};

	CCParallaxNode::CCParallaxNode()
	{
		m_pParallaxArray = ccArrayNew(5);
		m_tLastPosition = CCPointMake(-100,-100);
	}
	CCParallaxNode::~CCParallaxNode()
	{
		if( m_pParallaxArray )
		{
			ccArrayFree(m_pParallaxArray);
			m_pParallaxArray = NULL;
		}
	}
	CCParallaxNode * CCParallaxNode::node()
	{
		CCParallaxNode *pRet = new CCParallaxNode();
		pRet->autorelease();
		return pRet;
	}
	void CCParallaxNode::addChild(CCNode * child, int zOrder, int tag)
	{
        CC_UNUSED_PARAM(zOrder);
        CC_UNUSED_PARAM(child);
        CC_UNUSED_PARAM(tag);
		CCAssert(0,"ParallaxNode: use addChild:z:parallaxRatio:positionOffset instead");
	}
	void CCParallaxNode::addChild(CCNode *child, int z, const CCPoint& ratio, const CCPoint& offset)
	{
		CCAssert( child != NULL, "Argument must be non-nil");
		CCPointObject *obj = CCPointObject::pointWithCCPoint(ratio, offset);
		obj->setChild(child);
		ccArrayAppendObjectWithResize(m_pParallaxArray, (CCObject*)obj);

		CCPoint pos = m_tPosition;
		pos.x = pos.x * ratio.x + offset.x;
		pos.y = pos.y * ratio.y + offset.y;
		child->setPosition(pos);

		CCNode::addChild(child, z, child->getTag());
	}
	void CCParallaxNode::removeChild(CCNode* child, bool cleanup)
	{
		for( unsigned int i=0;i < m_pParallaxArray->num;i++)
		{
			CCPointObject *point = (CCPointObject*)m_pParallaxArray->arr[i];
			if( point->getChild()->isEqual(child))
			{
				ccArrayRemoveObjectAtIndex(m_pParallaxArray, i);
				break;
			}
		}
		CCNode::removeChild(child, cleanup);
	}
	void CCParallaxNode::removeAllChildrenWithCleanup(bool cleanup)
	{
		ccArrayRemoveAllObjects(m_pParallaxArray);
		CCNode::removeAllChildrenWithCleanup(cleanup);
	}
	CCPoint CCParallaxNode::absolutePosition()
	{
		CCPoint ret = m_tPosition;
		CCNode *cn = this;
		while (cn->getParent() != NULL)
		{
			cn = cn->getParent();
			ret = ccpAdd( ret,  cn->getPosition());
		}
		return ret;
	}

	/*
	The positions are updated at visit because:
	- using a timer is not guaranteed that it will called after all the positions were updated
	- overriding "draw" will only precise if the children have a z > 0
	*/
	void CCParallaxNode::visit()
	{
		//	CCPoint pos = position_;
		//	CCPoint	pos = [self convertToWorldSpace:CCPointZero];
		CCPoint pos = this->absolutePosition();
		if( ! CCPoint::CCPointEqualToPoint(pos, m_tLastPosition) )
		{
			for(unsigned int i=0; i < m_pParallaxArray->num; i++ )
			{
				CCPointObject *point = (CCPointObject*)m_pParallaxArray->arr[i];
				float x = -pos.x + pos.x * point->getRatio().x + point->getOffset().x;
				float y = -pos.y + pos.y * point->getRatio().y + point->getOffset().y;
				point->getChild()->setPosition(ccp(x,y));
			}
			m_tLastPosition = pos;
		}
		CCNode::visit();
	}

}// namespace cocos2d