mirror of https://github.com/axmolengine/axmol.git
455 lines
15 KiB
C++
Executable File
455 lines
15 KiB
C++
Executable File
/****************************************************************************
|
|
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 "CCConfiguration.h"
|
|
#include "support/ccUtils.h"
|
|
#include "CCStdC.h"
|
|
#include "CCFileUtils.h"
|
|
#include "support/zip_support/ZipUtils.h"
|
|
#include "ccGLStateCache.h"
|
|
#include <cctype>
|
|
|
|
NS_CC_BEGIN
|
|
|
|
#define PVR_TEXTURE_FLAG_TYPE_MASK 0xff
|
|
|
|
// 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
|
|
};
|
|
|
|
static char gPVRTexIdentifier[5] = "PVR!";
|
|
|
|
enum
|
|
{
|
|
kPVRTexturePixelTypeRGBA_4444= 0x10,
|
|
kPVRTexturePixelTypeRGBA_5551,
|
|
kPVRTexturePixelTypeRGBA_8888,
|
|
kPVRTexturePixelTypeRGB_565,
|
|
kPVRTexturePixelTypeRGB_555, // unsupported
|
|
kPVRTexturePixelTypeRGB_888,
|
|
kPVRTexturePixelTypeI_8,
|
|
kPVRTexturePixelTypeAI_88,
|
|
kPVRTexturePixelTypePVRTC_2,
|
|
kPVRTexturePixelTypePVRTC_4,
|
|
kPVRTexturePixelTypeBGRA_8888,
|
|
kPVRTexturePixelTypeA_8,
|
|
};
|
|
|
|
static const unsigned int tableFormats[][7] = {
|
|
|
|
// - PVR texture format
|
|
// - OpenGL internal format
|
|
// - OpenGL format
|
|
// - OpenGL type
|
|
// - bpp
|
|
// - compressed
|
|
// - Cocos2d texture format constant
|
|
{ kPVRTexturePixelTypeRGBA_4444, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 16, false, kCCTexture2DPixelFormat_RGBA4444 },
|
|
{ kPVRTexturePixelTypeRGBA_5551, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, 16, false, kCCTexture2DPixelFormat_RGB5A1 },
|
|
{ kPVRTexturePixelTypeRGBA_8888, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 32, false, kCCTexture2DPixelFormat_RGBA8888 },
|
|
{ kPVRTexturePixelTypeRGB_565, GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 16, false, kCCTexture2DPixelFormat_RGB565 },
|
|
{ kPVRTexturePixelTypeRGB_888, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, 24, false, kCCTexture2DPixelFormat_RGB888 },
|
|
{ kPVRTexturePixelTypeA_8, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, 8, false, kCCTexture2DPixelFormat_A8 },
|
|
{ kPVRTexturePixelTypeI_8, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, 8, false, kCCTexture2DPixelFormat_I8 },
|
|
{ kPVRTexturePixelTypeAI_88, GL_LUMINANCE_ALPHA, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,16, false, kCCTexture2DPixelFormat_AI88 },
|
|
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
|
{ kPVRTexturePixelTypePVRTC_2, GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, -1, -1, 2, true, kCCTexture2DPixelFormat_PVRTC2 },
|
|
{ kPVRTexturePixelTypePVRTC_4, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, -1, -1, 4, true, kCCTexture2DPixelFormat_PVRTC4 },
|
|
|
|
{ kPVRTexturePixelTypeBGRA_8888, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, 32, false, kCCTexture2DPixelFormat_RGBA8888 },
|
|
#endif // iphone only
|
|
};
|
|
|
|
//Tells How large is tableFormats
|
|
#define MAX_TABLE_ELEMENTS (sizeof(tableFormats) / sizeof(tableFormats[0]))
|
|
|
|
enum {
|
|
kCCInternalPVRTextureFormat,
|
|
kCCInternalOpenGLInternalFormat,
|
|
kCCInternalOpenGLFormat,
|
|
kCCInternalOpenGLType,
|
|
kCCInternalBPP,
|
|
kCCInternalCompressedImage,
|
|
kCCInternalCCTexture2DPixelFormat,
|
|
};
|
|
|
|
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)
|
|
, m_uWidth(0)
|
|
, m_uHeight(0)
|
|
, m_bRetainName(false)
|
|
, m_bHasAlpha(false)
|
|
, m_uName(0)
|
|
, m_eFormat(kCCTexture2DPixelFormat_Default)
|
|
{
|
|
}
|
|
|
|
CCTexturePVR::~CCTexturePVR()
|
|
{
|
|
CCLOGINFO( "cocos2d: deallocing CCTexturePVR" );
|
|
|
|
if (m_uName != 0 && ! m_bRetainName)
|
|
{
|
|
ccGLDeleteTexture(m_uName);
|
|
}
|
|
}
|
|
|
|
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))
|
|
{
|
|
CCLOG("Unsupported PVR format. Use the Legacy format until the new format is supported");
|
|
return false;
|
|
}
|
|
|
|
CCConfiguration *configuration = CCConfiguration::sharedConfiguration();
|
|
|
|
flags = CC_SWAP_INT32_LITTLE_TO_HOST(header->flags);
|
|
formatFlags = flags & PVR_TEXTURE_FLAG_TYPE_MASK;
|
|
bool flipped = (flags & kPVRTextureFlagVerticalFlip) ? true : false;
|
|
if (flipped)
|
|
{
|
|
CCLOG("cocos2d: WARNING: Image is flipped. Regenerate it using PVRTexTool");
|
|
}
|
|
|
|
if (! configuration->supportsNPOT() &&
|
|
(header->width != ccNextPOT(header->width) || header->height != ccNextPOT(header->height)))
|
|
{
|
|
CCLOG("cocos2d: ERROR: Loding an NPOT texture (%dx%d) but is not supported on this device", header->width, header->height);
|
|
return false;
|
|
}
|
|
|
|
for (m_uTableFormatIndex = 0; m_uTableFormatIndex < (unsigned int)MAX_TABLE_ELEMENTS; m_uTableFormatIndex++)
|
|
{
|
|
//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 kPVRTexturePixelTypePVRTC_2:
|
|
blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
|
|
widthBlocks = width / 8;
|
|
heightBlocks = height / 4;
|
|
break;
|
|
case kPVRTexturePixelTypePVRTC_4:
|
|
blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
|
|
widthBlocks = width / 4;
|
|
heightBlocks = height / 4;
|
|
break;
|
|
case kPVRTexturePixelTypeBGRA_8888:
|
|
if (CCConfiguration::sharedConfiguration()->supportsBGRA8888() == false)
|
|
{
|
|
CCLOG("cocos2d: TexturePVR. BGRA8888 not supported on this device");
|
|
return false;
|
|
}
|
|
default:
|
|
blockSize = 1;
|
|
widthBlocks = width;
|
|
heightBlocks = height;
|
|
break;
|
|
}
|
|
|
|
// Clamp to minimum number of blocks
|
|
if (widthBlocks < 2)
|
|
{
|
|
widthBlocks = 2;
|
|
}
|
|
if (heightBlocks < 2)
|
|
{
|
|
heightBlocks = 2;
|
|
}
|
|
|
|
dataSize = widthBlocks * heightBlocks * ((blockSize * bpp) / 8);
|
|
float 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 (! success)
|
|
{
|
|
CCLOG("cocos2d: WARNING: Unsupported PVR Pixel Format: 0x%2x. Re-encode it with a OpenGL pixel format variant", 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)
|
|
{
|
|
ccGLDeleteTexture(m_uName);
|
|
}
|
|
|
|
// From PVR sources: "PVR files are never row aligned."
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
|
|
|
|
glGenTextures(1, &m_uName);
|
|
glBindTexture(GL_TEXTURE_2D, m_uName);
|
|
|
|
// Default: Anti alias.
|
|
if (m_uNumberOfMipmaps == 1)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}
|
|
else
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
|
}
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
CHECK_GL_ERROR_DEBUG(); // clean possible GL error
|
|
|
|
GLenum internalFormat = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLInternalFormat];
|
|
GLenum format = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLFormat];
|
|
GLenum type = tableFormats[m_uTableFormatIndex][kCCInternalOpenGLType];
|
|
bool compressed = tableFormats[m_uTableFormatIndex][kCCInternalCompressedImage];
|
|
|
|
// Generate textures with mipmaps
|
|
for (unsigned int i = 0; i < m_uNumberOfMipmaps; ++i)
|
|
{
|
|
if (compressed && ! CCConfiguration::sharedConfiguration()->supportsPVRTC())
|
|
{
|
|
CCLOG("cocos2d: WARNING: PVRTC images are not supported");
|
|
return false;
|
|
}
|
|
|
|
unsigned char *data = m_asMipmaps[i].address;
|
|
unsigned int datalen = m_asMipmaps[i].len;
|
|
|
|
if (compressed)
|
|
{
|
|
glCompressedTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, datalen, data);
|
|
}
|
|
else
|
|
{
|
|
glTexImage2D(GL_TEXTURE_2D, i, internalFormat, width, height, 0, format, type, data);
|
|
}
|
|
|
|
if (i > 0 && (width != height || ccNextPOT(width) != width ))
|
|
{
|
|
CCLOG("cocos2d: TexturePVR. WARNING. Mipmap level %u is not squared. Texture won't render correctly. width=%u != height=%u", i, width, height);
|
|
}
|
|
|
|
err = glGetError();
|
|
if (err != GL_NO_ERROR)
|
|
{
|
|
CCLOG("cocos2d: TexturePVR: Error uploading compressed texture level: %u . glError: 0x%04X", i, err);
|
|
return false;
|
|
}
|
|
|
|
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::sharedFileUtils()->getFileData(path, "rb", (unsigned long *)(&pvrlen));
|
|
}
|
|
|
|
if (pvrlen < 0)
|
|
{
|
|
this->release();
|
|
return false;
|
|
}
|
|
|
|
m_uNumberOfMipmaps = 0;
|
|
|
|
m_uName = 0;
|
|
m_uWidth = m_uHeight = 0;
|
|
m_bHasAlpha = false;
|
|
|
|
m_bRetainName = false; // 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 (pTexture)
|
|
{
|
|
if (pTexture->initWithContentsOfFile(path))
|
|
{
|
|
pTexture->autorelease();
|
|
}
|
|
else
|
|
{
|
|
delete pTexture;
|
|
pTexture = NULL;
|
|
}
|
|
}
|
|
|
|
return pTexture;
|
|
}
|
|
|
|
NS_CC_END
|
|
|