Cocos2dx在IOS平台中将截屏保存到到手机相册
发表于2018-05-21
使用Cocos2dx 进行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 = 11024 * 7768 * 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 <11024 * 4; x++) { buffer2[(767 - y) * 11024 * 4 + x] = buffer[y * 44 * 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 = 44 * 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
然后将要混编的文件改为。mm文件,然后就能很方便的在C++中调用OC的代码了:
#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 = 44 * 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平台中将截图保存到相册的功能。