最近使用Cocos2dx 2.2.3进行IOS游戏开发,碰到了关于截屏路径的一个问题,cocos2dx本身自带了截屏的功能,代码如下:
CCSize size = CCDirector::sharedDirector()->getWinSize();
CCRenderTexture* renderTexture = CCRenderTexture::create(size.width, size.height);
CCScene* curScene = CCDirector::sharedDirector()->getRunningScene();
renderTexture->begin();
curScene->visit();
renderTexture->end();
renderTexture->saveToFile("curScene.png", kCCImageFormatPNG);
但是saveToFile()函数在IOS下面默认保存的路径是应用程序的Document目录,用户无法在自己的相册中进行查看,并且IOS下的权限控制很严格,想要访问相册的目录估计是无法在游戏内部进行了,于是在上网搜索半天无果之后,只好通过C++和OC混合编译,调用IOS的API来进行。苦于没有IOS开发基础,上网搜索了一段OC代码:
-(UIImage *) glToUIImage {
NSInteger myDataLength = 1024 * 768 * 4;
// allocate array and read pixels into it.
GLubyte *buffer = (GLubyte *) malloc(myDataLength);
glReadPixels(0, 0, 1024, 768, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// gl renders "upside down" so swap top to bottom into new array.
// there's gotta be a better way, but this works.
GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
for(int y = 0; y <768; y++)
{
for(int x = 0; x <1024 * 4; x++)
{
buffer2[(767 - y) * 1024 * 4 + x] = buffer[y * 4 * 1024 + x];
}
}
// make data provider with data.
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
// prep the ingredients
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * 1024;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
// make the cgimage
CGImageRef imageRef = CGImageCreate(1024, 768, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
// then make the uiimage from that
UIImage *myImage = [UIImage imageWithCGImage:imageRef];
return myImage;
UIImageWriteToSavedPhotosAlbum(myImage, self, nil, nil);
}
这段代码通过glReadPixels来读取屏幕的像素值,然后转换为UIImage对象,最后通过UIImageWriteToSavedPhotosAlbum()保存到相册中间,只需要稍稍修改一下屏幕的尺寸能使用。在OC和C++混编的时候还有一些小问题要注意,就是在C++中包含OC头文件的时候不能直接包含:
#include "ScreenShot.h"
这样会报错,必须加上预编译的处理:
#ifdef __OBJC__
#include "ScreenShot.h"
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
[ScreenShot saveImage];
#else
到了这里我以为大功告成了,但是试验之后发现在模拟器中完美截屏,真机测试的时候却发现截图全是黑的,所有像素都是0。谷歌一番之后,找到了问题所在,stackoverflow上的原问题,glReadPixels读取的是缓冲区中的内容,而在ios6.0之后,系统会将已经显示过的内容从缓冲区中清空,而在游戏中调用glReadPixels时屏幕的内容可能已经显示过了,这是Cocos2dx内部的机制,我们无法更改,导致无法正确截图。
仔细思考一下,其实我们调用OC代码的部分也只有保存到相册的那一部分而已,至于截图,完全可以用Cocos2dx自带的功能,所以只要能获取renderTexture的像素信息,再在OC部分中转换为UIImage保存即可,由此诞生了最终版本:
//C++部分的代码
CCSize size = CCDirector::sharedDirector()->getWinSize();
CCRenderTexture* renderTexture = CCRenderTexture::create(size.width, size.height);
CCScene* curScene = CCDirector::sharedDirector()->getRunningScene();
renderTexture->begin();
curScene->visit();
renderTexture->end();
CCImage* image = renderTexture->newCCImage();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
[ScreenShot saveImage:image->getData() width:image->getWidth() height:image->getHeight()];
#else
renderTexture->saveToFile("curScene.png", kCCImageFormatPNG);
#endif
//OC部分的代码
@interface ScreenShot : NSObject
+(void) saveImage: (GLubyte*)imageData width: (int)width height:(int)height;
@end
@implementation ScreenShot
+(void) saveImage: (GLubyte*)imageData width: (int)width height:(int)height{
NSInteger myDataLength = width * height * 4;
// make data provider with data.
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, imageData, myDataLength, NULL);
// prep the ingredients
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
// make the cgimage
CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
// then make the uiimage from that
UIImage *myImage = [UIImage imageWithCGImage:imageRef];
UIImageWriteToSavedPhotosAlbum(myImage, nil, nil, nil);
}
@end
通过renderTexture->newCCImage()->getData()获取unsigned char* 像素数据,然后再saveImage函数中转换为UIImage对象最后保存,由此实现在IOS平台中将截图保存到相册的功能。