在进行cocos2dx游戏开发时,我们有时候需要知道特定点的颜色,这里笔记下如何使用CCImage读取png文件来实现。
CONTENTS
CCImage简单分析:
这里我们先简单的分析下CCImage的实现。
首先看下【 CCImage::initWithImageFile 】的实现,在文件【 \cocos2d-x-2.2.3\cocos2dx\platform\CCImageCommon_cpp.h 】中:
bool CCImage::initWithImageFile(const char * strPath, EImageFormat eImgFmt/* = eFmtPng*/) { bool bRet = false; #ifdef EMSCRIPTEN 。。。。。。 。。。。。。 #else unsigned long nSize = 0; std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(strPath);//获取了传入文件的绝对路径 //读文件,pBuffer为文件内容,nSize为内容长度 unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(), "rb", &nSize); if (pBuffer != NULL && nSize > 0) {//进入函数进行处理,这里假设我们传入的eImgFmt为png类型的。 bRet = initWithImageData(pBuffer, nSize, eImgFmt); } CC_SAFE_DELETE_ARRAY(pBuffer); #endif // EMSCRIPTEN return bRet; } bool CCImage::initWithImageData(void * pData, int nDataLen, EImageFormat eFmt/* = eSrcFmtPng*/, int nWidth/* = 0*/, int nHeight/* = 0*/, int nBitsPerComponent/* = 8*/) { bool bRet = false; do { CC_BREAK_IF(! pData || nDataLen <= 0); if (kFmtPng == eFmt) { bRet = _initWithPngData(pData, nDataLen); break; } //这里我们仅仅分析png格式的读取,其他的先略掉 。。。。。。 。。。。。。 } while (0); return bRet; } //cocos2dx使用libpng来读取png图片,具体的使用方法这里不再展开笔记,只笔记下整体的流程 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; //先声明libpng需要的结构体 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_TARGET_PLATFORM != CC_PLATFORM_NACL) 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 //读取png文件信息 // 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); //将不同的png设置为RGB8888的格式。 // 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,即unsigned char* png_bytep* row_pointers = (png_bytep*)malloc( sizeof(png_bytep) * m_nHeight ); png_read_update_info(png_ptr, info_ptr); //获取存储单行的像素的数据的大小(字节),也就是宽*4(RGBA,每个占一字节) rowbytes = png_get_rowbytes(png_ptr, info_ptr); //根据行数(高)和单行的数据大小申请存储空间,每个元素类型为unsigned char* //用来存储rgba中的一个。即每4个元素构成一个完整的像素 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)//这里必须为4,RGBA { m_bHasAlpha = true; //用unsigned int 类型来处理m_pData,这样一次++就可以转到下一个像素点。 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) { //将RGBA转换下重新放到原位置,宏定义在下面 //这里需要注意的是tmp的位置和row_pointers[i][j]其实是一样的。 //只不过是类型不同,tmp类型为unsigned int,++一次移动4个字节,即一个像素。 //row_pointers[i][j]为png_bytep(unsigned char *)型,++一次移动1个字节。因此row_pointers[i][j + 1] //为row_pointers[i][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; } //非常重要的一个宏,通过这个宏我们可以知道rgba在内存中的分布。 // premultiply alpha, or the effect will wrong when want to use other pixel format in CCTexture2D, // such as RGB888, RGB5A1 #define CC_RGB_PREMULTIPLY_ALPHA(vr, vg, vb, va) \ (unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + 1)) >> 8) | \ ((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + 1) >> 8) << 8) | \ ((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + 1) >> 8) << 16) | \ ((unsigned)(unsigned char)(va) << 24)) // on ios, we should use platform/ios/CCImage_ios.mm instead
因此,根据最后的这个宏CC_RGB_PREMULTIPLY_ALPHA,我们就可以得到如下的函数,用来获取指定点的颜色:
获取特定点颜色的方法:
cocos2d::ccColor4B getImageColorOfPos(cocos2d::CCImage *pImage, cocos2d::CCPoint &pt) { ccColor4B c = {0, 0, 0, 0}; //cocos2dx使用的是openGL坐标系,y坐标是从下到上递增的。 //libpng读取png图片时是从上到下逐行读取的,因此这里的纵坐标需要处理下。 unsigned int x = pt.x, y = pImage->getHeight() - pt.y; int width = pImage->getWidth(); unsigned char *data_=pImage->getData(); unsigned int *pixel = (unsigned int *)data_;//以unsigned int来处理 pixel = pixel + (y * width) + x;//跳到指定的像素点。 c.a = (*pixel >> 24) & 0xff; float scale = (c.a + 1.0f) / 256 ; c.r = *pixel & 0xff; c.r = c.r / scale ; c.g = (*pixel >> 8) & 0xff; c.g = c.g / scale ; c.b = (*pixel >> 16) & 0xff; c.b = c.b / scale ; return c; } //代码示例: CCImage *pImage = new CCImage(); pImage->initWithImageFile("images.png",CCImage::kFmtPng); ccColor4B colorOfPos = getImageColorOfPos(pImage, ccp(0,0)); pImage->release();
备注:
由于获取颜色的代码中有很多数学运算,比较复杂,这里可以简化一下,如果png上所有像素的透明度全部为255,那么根据(255 + 1)>>8 = 1可以得出:
c.r = *pixel & 0xff; c.g = (*pixel >> 8) & 0xff; c.b = (*pixel >> 16) & 0xff; c.a = (*pixel >> 24) & 0xff;
这样可以简化掉相当多的运算。
后续参考加深资料:
【 http://www.cnblogs.com/xiaoxiaoboke/archive/2012/02/13/2349765.html 】
【 http://blog.csdn.net/honghaier/article/details/8032434 】
发表评论