1. 将PNG图片分拆,也就是将一张PNG图片生成出一张带RGB的JPG+一张带Alpha的PNG,我使用的工具是imagemagick。这里以bg.png为例:
首先ImageMagick工具提取出Alpha通道,(命令: convert bg.png -channel A -alpha extract bgAlpha.png)
然后在将bg.png转成JPG格式 输出为bg.jpg。jpg格式已经不包含Alpha通道,而且jpg的压缩率比较高(这是采用这种方法可以减小图片大小的根本所在)。(convert bg.png bg.jpg)
这样就得到了bg.jpg 和bgAlpha.png,这两张图片就是我们需要的资源。这样转换之后bg.jpg+bgAlpha.png的大小大概比bg.png的 小2~3倍
2. 在程序中合成
由于在win,Android平台采用的JPG和PNG解析库和IOS上的不同,所以在程序中要分别处理,这里介绍下在IOS上的方法(win和Android平台解析参照CCImage的_initWithJpgData和_initWithPngData 方法,合并和IOS的平台类似,不同地方文中会指出), 这个方法添加在CCImage.mm 文件中即可使用(cocos2dx版本是2.2.2)。
1 bool CCImage::initWithRgbAndAlpha(const char * jpgFilePath, const char * pngFilePath) 2 { 3 bool bRet = false; 4 5 do { 6 7 // 获得JPG文件的数据 8 unsigned long nSizeJpg = 0; 9 unsigned char* pBufferJpg = CCFileUtils::sharedFileUtils()->getFileData( 10 CCFileUtils::sharedFileUtils()->fullPathForFilename(jpgFilePath).c_str(), 11 "rb", 12 &nSizeJpg); 13 // 保证JPG文件数据必须是存在的 14 if (pBufferJpg == NULL && nSizeJpg <= 0) {return true;} 15 16 17 // 获得Alpha文件的数据 18 unsigned long nSizePng = 0; 19 unsigned char* pBufferPng = CCFileUtils::sharedFileUtils()->getFileData( 20 CCFileUtils::sharedFileUtils()->fullPathForFilename(pngFilePath).c_str(), 21 "rb", 22 &nSizePng); 23 24 tImageInfo jpgInfo = {0}; 25 26 jpgInfo.hasShadow = false; 27 jpgInfo.hasStroke = false; 28 29 30 // 利用CCImage原有的方法解析JPG 31 bRet = _initWithData(pBufferJpg, nSizeJpg, &jpgInfo); 32 unsigned char* jpgData = jpgInfo.data; 33 34 if (!bRet) {return false;} 35 36 37 38 tImageInfo pngInfo = {0}; 39 40 pngInfo.hasShadow = false; 41 pngInfo.hasStroke = false; 42 43 //利用CCImage原有的方法解析Alpha文件 44 bRet = _initWithData(pBufferPng, nSizePng, &pngInfo); 45 unsigned char* pngData = pngInfo.data; 46 47 if (!bRet) {return false;} 48 49 50 unsigned short jpgHeight = (short)jpgInfo.height; 51 unsigned short jpgWidth = (short)jpgInfo.width; 52 53 int jpgDataLen = jpgHeight * jpgWidth; 54 55 int pngIndex = 0; 56 int jpgIndex = 0; 57 58 m_pData = new unsigned char[jpgHeight * jpgWidth * 4]; 59 unsigned int *tmpData = (unsigned int *)m_pData; 60 61 // 合并两个文件的数据 62 for(int i = 0; i < jpgDataLen; i++) 63 { 64 65 // 这个宏和CC_RGB_PREMULTIPLY_ALPHA是一样的,作用是将R,G,B 三个颜色值分别乘Alpha值得到合并的R,G,B值, 66 *tmpData++ = CC_RGB_PREMULTIPLY_ALPHA_IOS( 67 jpgData[jpgIndex], // R 68 jpgData[jpgIndex + 1], // G 69 jpgData[jpgIndex + 2], // B 70 pngData[pngIndex]); //A 71 72 // 两张图片在解析之后都为32位了,所以+4 (Android或者win上 不带Alpha通道的png文件和jpg文件解析之后是24位的,这是主要区别的地方) 73 jpgIndex += 4; 74 pngIndex += 4; 75 } 76 77 m_nHeight = (short)jpgInfo.height; 78 m_nWidth = (short)jpgInfo.width; 79 80 m_nBitsPerComponent = jpgInfo.bitsPerComponent; 81 m_bPreMulti = true; 82 m_bHasAlpha = true; 83 84 CC_SAFE_DELETE_ARRAY(pBufferPng); 85 CC_SAFE_DELETE_ARRAY(pBufferJpg); 86 CC_SAFE_DELETE_ARRAY(jpgData); 87 CC_SAFE_DELETE_ARRAY(pngData); 88 89 bRet = true; 90 91 } while (0); 92 93 return bRet; 94 }
这个方法执行之后bg.jpg和bgAlpha.png合并完成,当前的CCImage对象生成的纹理就和直接用bg.png初始化后生成的纹理效果是一样的了。
另外附上一个将png图片分拆成Jpg和Alpha的shell脚本(shell不太熟,将就着用^_^),使用这个脚本的前提是你已经安装了imagemagick,这个脚本运行之后会遍历当前目录下的所有png文件,然后分拆这些文件,并且会删除原png文件。(如果不想删除原png文件,把`rm $src`去掉即可)
1 #!/bin/sh 2 convert_img(){ 3 for file in `ls -a $1` 4 do 5 if [ x"$file" != x"." -a x"$file" != x".." ];then 6 str="$1/$file" 7 length=${#str} 8 9 sufLength=$(($length - 4)) 10 strSuf=${str:$sufLength:$length} 11 12 if [ x"$strSuf" = x".png" ];then 13 src=${str:2:$length} 14 dst=${str:2:$dstLength} 15 16 dstLength=$(($length - 6)) 17 18 dstSuf="mask" 19 dstJpg=".jpg" 20 21 convert $src -channel A -alpha extract $dst$dstSuf 22 convert $src $dst$dstJpg 23 24 `rm $src` 25 echo $src 26 27 elif [ -d "$1/$file" ];then 28 convert_img "$1/$file" 29 fi 30 fi 31 done 32 } 33 34 convert_img .