最近项目中遇到这样一个问题:cocos2dx渲染图片的效率比较低,为了提高渲染效率有几种办法。
1. 多线程进行渲染,cocos2dx 3.0 就采用的这个办法。
2. 在程序启动时,统一进行渲染图片并将渲染的结果放到内存池中,真正用的时候从内存池中获取。
首先我来解释一下cocos2dx 2.0.4 的渲染机制:首先先读取图片,然后将图片转换为显卡可以识别的格式,最后让显卡显示图片。
anriod和ios平台是有差别的,实现的代码如下:
图片渲染机制 ios平台
// 读取ccbi文件会用到一个函数
// parsePropTypeSpriteFrame
frameCache->addSpriteFramesWithFile(spriteSheet->getCString());
// 多数是调用到这一行 这行比较耗时
pTexture = CCTextureCache::sharedTextureCache()->addImage(texturePath.c_str());
bool CCImage::initWithImageData(...)
{
bool bRet = false;
tImageInfo info = {0};
do
{
CC_BREAK_IF(! pData || nDataLen <= 0);
if (eFmt == kFmtRawData)
{ // 运行这里渲染效果会很快pvr格式,ios特有
bRet = _initWithRawData(pData, nDataLen, nWidth, nHeight, nBitsPerComponent);
}
else // init with png or jpg file data
{
bRet = _initWithData(pData, nDataLen, &info);
...
}
} while (0);
return bRet;
}
static bool _initWithData(void * pBuffer, int length, tImageInfo *pImageinfo)
{
bool ret = false;
if (pBuffer)
{
CGImageRef CGImage;
NSData *data;
data = [NSData dataWithBytes:pBuffer length:length];
CGImage = [[UIImage imageWithData:data] CGImage];
ret = _initWithImage(CGImage, pImageinfo);
}
return ret;
}
static bool _initWithImage(CGImageRef cgImage, tImageInfo *pImageinfo)
{
if(cgImage == NULL)
{
return false;
}
// get image info
pImageinfo->width = CGImageGetWidth(cgImage);
pImageinfo->height = CGImageGetHeight(cgImage);
CGImageAlphaInfo info = CGImageGetAlphaInfo(cgImage);
pImageinfo->hasAlpha = (info == kCGImageAlphaPremultipliedLast)
|| (info == kCGImageAlphaPremultipliedFirst)
|| (info == kCGImageAlphaLast)
|| (info == kCGImageAlphaFirst);
CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
if (colorSpace)
{
if (pImageinfo->hasAlpha)
{
info = kCGImageAlphaPremultipliedLast;
pImageinfo->isPremultipliedAlpha = true;
}
else
{
info = kCGImageAlphaNoneSkipLast;
pImageinfo->isPremultipliedAlpha = false;
}
}
else
{
return false;
}
// change to RGBA8888
pImageinfo->hasAlpha = true;
pImageinfo->bitsPerComponent = 8;
pImageinfo->data = new unsigned char[pImageinfo->width * pImageinfo->height * 4];
colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pImageinfo->data,
pImageinfo->width,
pImageinfo->height,
8,
4 * pImageinfo->width,
colorSpace,
info | kCGBitmapByteOrder32Big);
CGContextClearRect(context, CGRectMake(0, 0, pImageinfo->width, pImageinfo->height));
//CGContextTranslateCTM(context, 0, 0);
CGContextDrawImage(context, CGRectMake(0, 0, pImageinfo->width, pImageinfo->height), cgImage);
CGContextRelease(context);
CFRelease(colorSpace);
return true;
}
anroid平台的渲染图片的方式会有很大的不同,实际测试效果anriod平台的处理速度要比ios慢的特别多,经过测试在debug版本的anroid慢得要800ms
但在ios下只用20ms。
下面是andriod图片耗时的函数,主要集中在png_read_image函数的调用和if (channel == 4)这个分支中。代码中可以明显的观察到图片的尺寸对效率是有很大影响的。
bool CCImage::_initWithPngData(void * pData, int nDatalen)
{
// length of bytes to check if it is a valid png file
#define PNGSIGSIZE 8
bool bRet = false;
png_byte header[PNGSIGSIZE] = {0};
png_structp png_ptr = 0;
png_infop info_ptr = 0;
do
{
// png header len is 8 bytes
CC_BREAK_IF(nDatalen < PNGSIGSIZE);
// check the data is png or not
memcpy(header, pData, PNGSIGSIZE);
CC_BREAK_IF(png_sig_cmp(header, 0, PNGSIGSIZE));
// init png_struct
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
CC_BREAK_IF(! png_ptr);
// init png_info
info_ptr = png_create_info_struct(png_ptr);
CC_BREAK_IF(!info_ptr);
#if (CC_TARGET_PLATFORM != CC_PLATFORM_BADA)
CC_BREAK_IF(setjmp(png_jmpbuf(png_ptr)));
#endif
// set the read call back function
tImageSource imageSource;
imageSource.data = (unsigned char*)pData;
imageSource.size = nDatalen;
imageSource.offset = 0;
png_set_read_fn(png_ptr, &imageSource, pngReadCallback);
// read png header info
// read png file info
png_read_info(png_ptr, info_ptr);
m_nWidth = png_get_image_width(png_ptr, info_ptr);
m_nHeight = png_get_image_height(png_ptr, info_ptr);
m_nBitsPerComponent = png_get_bit_depth(png_ptr, info_ptr);
png_uint_32 color_type = png_get_color_type(png_ptr, info_ptr);
//CCLOG("color type %u", color_type);
// force palette images to be expanded to 24-bit RGB
// it may include alpha channel
if (color_type == PNG_COLOR_TYPE_PALETTE)
{
png_set_palette_to_rgb(png_ptr);
}
// low-bit-depth grayscale images are to be expanded to 8 bits
if (color_type == PNG_COLOR_TYPE_GRAY && m_nBitsPerComponent < 8)
{
png_set_expand_gray_1_2_4_to_8(png_ptr);
}
// expand any tRNS chunk data into a full alpha channel
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(png_ptr);
}
// reduce images with 16-bit samples to 8 bits
if (m_nBitsPerComponent == 16)
{
png_set_strip_16(png_ptr);
}
// expand grayscale images to RGB
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
{
png_set_gray_to_rgb(png_ptr);
}
// read png data
// m_nBitsPerComponent will always be 8
m_nBitsPerComponent = 8;
png_uint_32 rowbytes;
png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * m_nHeight );
png_read_update_info(png_ptr, info_ptr);
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
m_pData = new unsigned char[rowbytes * m_nHeight];
CC_BREAK_IF(!m_pData);
for (unsigned short i = 0; i < m_nHeight; ++i)
{
row_pointers[i] = m_pData + i*rowbytes;
}
png_read_image(png_ptr, row_pointers);
png_read_end(png_ptr, NULL);
png_uint_32 channel = rowbytes/m_nWidth;
if (channel == 4)
{
m_bHasAlpha = true;
unsigned int *tmp = (unsigned int *)m_pData;
for(unsigned short i = 0; i < m_nHeight; i++)
{
for(unsigned int j = 0; j < rowbytes; j += 4)
{
*tmp++ = CC_RGB_PREMULTIPLY_ALPHA( row_pointers[i][j], row_pointers[i][j + 1],
row_pointers[i][j + 2], row_pointers[i][j + 3] );
}
}
m_bPreMulti = true;
}
CC_SAFE_FREE(row_pointers);
bRet = true;
} while (0);
if (png_ptr)
{
png_destroy_read_struct(&png_ptr, (info_ptr) ? &info_ptr : 0, 0);
}
return bRet;
}