码迷,mamicode.com
首页 > 其他好文 > 详细

cocos2dx中怎样把texture保存为pvr或者pvr.ccz格式的文件

时间:2014-11-24 22:21:43      阅读:1106      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   io   ar   color   os   使用   sp   

本文给大家介绍下在cocos2dx中怎样把texture保存为pvr或者pvr.ccz格式的文件 

  pvr格式的数据在IOS上直接交给显卡渲染的,而cocos2dx 中的texture是直接交给显卡渲染的,所以理论上将pvr格式的数据可以不进行任何数据的转换就可以生成一张texture,事实上确实是这样的。

一, 保存为pvr格式

    要保存pvr文件,我们首先分析cocos2dx引擎中是怎样解析pvr文件的。可以看到cocos2dx库中的CCTexturePVR类提供了两个方法来解析pvr格式图片分别是unpackPVRv2Data、unpackPVRv3Data,两个方法分别解析的是PVR的v2和v3版本。本文只分析v2版本:

bool CCTexturePVR::unpackPVRv2Data(unsigned char* data, unsigned int len)//data是直接从文件里读出来没有做任何处理的数据
{
    bool success = false;
    ccPVRv2TexHeader *header = NULL;
    unsigned int flags, pvrTag;
    unsigned int dataLength = 0, dataOffset = 0, dataSize = 0;
    unsigned int blockSize = 0, widthBlocks = 0, heightBlocks = 0;
    unsigned int width = 0, height = 0, bpp = 4;
    unsigned char *bytes = NULL;
    unsigned int formatFlags;

    
    // 用header指向data数据的头部(PVR v2格式的头部固定是52个字节,也就是data的前52个字节的数据,data剩下的数据就是生成texture的数据了)
    header = (ccPVRv2TexHeader *)data;

    // 这是头部的一个标识值为 "PVR!" 占4字节
    pvrTag = CC_SWAP_INT32_LITTLE_TO_HOST(header->pvrTag);

    if (gPVRTexIdentifier[0] != (char)(((pvrTag >>  0) & 0xff)) ||
        gPVRTexIdentifier[1] != (char)(((pvrTag >>  8) & 0xff)) ||
        gPVRTexIdentifier[2] != (char)(((pvrTag >> 16) & 0xff)) ||
        gPVRTexIdentifier[3] != (char)(((pvrTag >> 24) & 0xff)))
    {
        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 & kPVR2TextureFlagVerticalFlip) ? 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: Loading an NPOT texture (%dx%d) but is not supported on this device", header->width, header->height);
        return false;
    }
    
    unsigned int pvr2TableElements = PVR2_MAX_TABLE_ELEMENTS;
    if (! CCConfiguration::sharedConfiguration()->supportsPVRTC())
    {
        pvr2TableElements = 9;
    }

    for (unsigned int i = 0; i < pvr2TableElements; i++)
    {
        //Does image format in table fits to the one parsed from header?
        if (v2_pixel_formathash[i].pixelFormat == formatFlags)
        {
            m_pPixelFormatInfo = v2_pixel_formathash[i].pixelFormatInfo;
            
            //Reset num of mipmaps
            m_uNumberOfMipmaps = 0;

            //Get size of mipmap
            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);

            //跳过头部,bytes直接指向了图片数据部分
            bytes = ((unsigned char *)data) + sizeof(ccPVRv2TexHeader);
            m_eFormat = m_pPixelFormatInfo->ccPixelFormat;
            bpp = m_pPixelFormatInfo->bpp;
            
            // Calculate the data size for each texture level and respect the minimum number of blocks
            while (dataOffset < dataLength)
            {
                switch (formatFlags) {
                    case kPVR2TexturePixelFormat_PVRTC_2BPP_RGBA:
                        blockSize = 8 * 4; // Pixel by pixel block size for 2bpp
                        widthBlocks = width / 8;
                        heightBlocks = height / 4;
                        break;
                    case kPVR2TexturePixelFormat_PVRTC_4BPP_RGBA:
                        blockSize = 4 * 4; // Pixel by pixel block size for 4bpp
                        widthBlocks = width / 4;
                        heightBlocks = height / 4;
                        break;
                    case kPVR2TexturePixelFormat_BGRA_8888:
                        if (CCConfiguration::sharedConfiguration()->supportsBGRA8888() == false) 
                        {
                            CCLOG("cocos2d: TexturePVR. BGRA8888 not supported on this device");
                            return false;
                        }
                    default:
                        blockSize = 1;
                        widthBlocks = width;
                        heightBlocks = height;
                        break;
                }
                
                // Clamp to minimum number of blocks
                if (widthBlocks < 2)
                {
                    widthBlocks = 2;
                }
                if (heightBlocks < 2)
                {
                    heightBlocks = 2;
                }

                dataSize = widthBlocks * heightBlocks * ((blockSize  * bpp) / 8);
                unsigned int packetLength = (dataLength - dataOffset);
                packetLength = packetLength > dataSize ? dataSize : packetLength;
                
                // bytes指向的是data,而data是直接从文件里面读出来的数据,m_asMipmaps则是用来生成texture的数据,
                // 所以这里可以得出结论,pvr的图片数据到texture不需要经过任何转换,
                // 也就是说pvr格式的数据可以不进行任何数据的转换就可以生成一张texture
                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 mipmaps reached. Increase 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;
        }

 

  通过上面的分析,我们知道了要把texture保存成pvr格式的文件只需为这样texture添加一个头部即可。关于pvr头部在CCTexturePVR.cpp中我们可以看到 _PVRTexHeader 这个结构体,这个结构体就是PVR的头部格式,现在要做的工作就是分析出_PVRTexHeader的成员的含义。这个可以参考imageination关于pvr的文档,需要提醒的是要注意版本差别。这里截出了头部描述的部分:

bubuko.com,布布扣

     根据表格的解析,PVR的头部就比较容易写出了。

  具体步骤: 1. 设置头部数据

        2. 把头部数据和texture数据合并

        3. 输出

  这是我在CCImage中添加的保存PVR格式的方法,大家可以参考下:

bool CCImage::_saveImageToPVRCCZ(const char *pszFilePath)
{
    bool bRet = false;
    do 
    {
        CC_BREAK_IF(NULL == pszFilePath);

        unsigned int *pvrHeader = new unsigned int[52]; // pvr header V2
pvrHeader[0] = 52; //headerLength pvrHeader[1] = m_nHeight; // height 变量是CCImage中的成员变量 pvrHeader[2] = m_nWidth; //width 变量是CCImage中的成员变量 pvrHeader[3] = 0; //numMipmaps pvrHeader[4] = 0x8012;//flags pvrHeader[5] = 0; //dataLength pvrHeader[6] = 0x20; // bpp = 32 // R G B A 是连续的4个字节,每个自街上都赋值为0xff表明存在活使用该颜色值(如果不使用A通道直接把A通道的值置为0x0即可) //0xff ff ff ff pvrHeader[7] = 0xff; // bitmaskRed pvrHeader[8] = 0xff00; // bitmaskGreen G pvrHeader[9] = 0xff0000; // bitmaskBlue B pvrHeader[10] = 0xff000000; // bitmaskAlpha A pvrHeader[11] = 0x21525650; // pvrTag = PVR! pvrHeader[12] = 1; // numSurfs unsigned int blockSize = 1; // default pvrHeader[5] = m_nWidth * m_nHeight * ((blockSize * 32) / 8); unsigned long pCombineDataLength = m_nWidth * m_nHeight * 4 + 52; unsigned char *pCombineData = new unsigned char[pCombineDataLength]; // header memcpy(pCombineData, (unsigned char *)pvrHeader, 52); CC_SAFE_DELETE_ARRAY(pvrHeader); // body memcpy(pCombineData + 52, m_pData, m_nWidth * m_nHeight * 4); //m_pData就是用来渲染纹理的数据(pvr除了头部的那部分数据) m_bPreMulti = true; // pCombineData就是一个完成的pvr格式的数据了,在此处输出pCombineData即可 if (!ret) { CCLOG("cocos2d: CompressPvrToCCZFile failed!"); CC_SAFE_DELETE_ARRAY(pCombineData); return false; } CC_SAFE_DELETE_ARRAY(pCombineData); bRet = true; } while (0); return bRet; }

 

二,保存为pvr.ccz格式

  那么现在来分析保存为PVR.CCZ格式。我们可以跟踪一张pvr.ccz格式的图片,观察其是怎样生成texture的,最终发现在CCTexturePVR类的initWithContentsOfFile方法中可以看到这段语句

    if (lowerCase.find(".ccz") != std::string::npos)
    {
        pvrlen = ZipUtils::ccInflateCCZFile(path, &pvrdata);
    }

  除此之外pvr.ccz 和pvr格式的处理过程是一模一样的。ccInflateCCZFile相当于解压了pvr.ccz文件并把解压后的数据保存到了pvrdata中。所以要把pvr转成pvr.ccz我们需要先把pvr数据用ZipUtils库压缩,再为这个数据添加一个头部即可。ZipUtils.h定义了一个CCZHeader,这个就是pvr.ccz的头部格式,pvr.ccz的头部比较简单,只有4个字段,这4个字段的数据都可以从ccInflateCCZFile中了解到,这里及不再赘述了,具体步骤如下:

  1. 先把pvr数据压缩,利用ZipUtils的compress方法压缩后得到压缩后的数据outBuffer

  2. 为outBuffer添加一个头部:
    CCZHeader header = {{‘C‘, ‘C‘, ‘Z‘, ‘!‘}, CCZ_COMPRESSION_ZLIB , 256, 0, CC_SWAP_INT32_BIG_TO_HOST(inLength)};
    注意:inLength是pvr数据的长度,这里必须转换为大端的表示方法。(在ccInflateCCZFile中可以看到把这个长度转换为小端)

  3. 把两个数据一次输出到同一个文件即可。代码如下(此处参考了http://www.cnblogs.com/howeho/p/3586379.html):

bool ZipUtils::ccCompressPvrToCCZFile(unsigned char *inBuffer,unsigned long inLength, const char *pszFileName)// inBuffer是pvr数据,inLength是pvr数据的长度
{
    bool bRet = false;
    do 
    {
        if (NULL == pszFileName) {
            CCLOG("cocos2d: Error pszFileName NULL!");
        }

        if(NULL == inBuffer || 0 == inLength)
        {
            CCLOG("cocos2d: Error argument inBuffer or inLength");
            return false;
        }

        unsigned long bufferSize = inLength;
        char* outBuffer=new char[(uInt)bufferSize];
        memset(outBuffer, 0, bufferSize);
        
        int ret = compress((Bytef*)outBuffer,(uLongf*)&bufferSize,(const Bytef*)inBuffer,(uLongf)inLength);

        if (ret != Z_OK) {
            CCLOG("cocos2d: Failed to compress data");
            CC_SAFE_DELETE_ARRAY(outBuffer);
            return false;
        }

        CCZHeader header = {{C, C, Z, !}, CCZ_COMPRESSION_ZLIB , 256, 0, CC_SWAP_INT32_BIG_TO_HOST(inLength)};

        // writeFileData 是我在CCFIleUtils中自己添加的写文件的方法,大家可以自己添加或者直接在此处输出
        CCFileUtils::sharedFileUtils()->writeFileData(pszFileName, "w", (const char*)&header, sizeof(header));
        CCFileUtils::sharedFileUtils()->writeFileData(pszFileName, "ab+", (const char*)outBuffer, bufferSize);

        CC_SAFE_DELETE_ARRAY(outBuffer);

        bRet = true;
    } while (0);

    return bRet;
}

 

cocos2dx中怎样把texture保存为pvr或者pvr.ccz格式的文件

标签:style   blog   http   io   ar   color   os   使用   sp   

原文地址:http://www.cnblogs.com/elang/p/4119582.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!