实现屏幕截屏

实现屏幕截屏

DionysosLai2014-4-11

         今天在工作时,参与游戏讨论时,策划人员突然要求实现游戏截屏的功能(呜呜,策划要求无止境啊)。于是,就屁颠屁颠的跑去研究截屏功能。

         Cocos2dx使用全新OpenGL ES 2.0绘图,在一些基本的绘图操作,我们可以调用基本的OpengL语音。在Opengl中,为我们提供了读取像素块函数,glReadPixels 。

void glReadPixels(GLint x,GLinty,GLsizesi width,GLsizei height, GLenum format,GLenum type,GLvoid *pixel);

函数参数(x, y)定义图像区域左下角点的坐标,width和height分别是图像的高度和宽度,*pixel是一个指针,指向存储图像数据的数组。参数format指出所读象素数据元素的格式(索引值或R、G、B、A值,如下面表所示),而参数type指出每个元素的数据类型。具体参数如下所示:

+++++++++++像素格式++++++++++++++++++++++

GL_INDEX 单个颜色索引

GL_RGB 先是红色分量,再是绿色分量,然后是蓝色分量

GL_RED 单个红色分量

GL_GREEN 单个绿色分量

GL_BLUE 单个蓝色分量

GL_ALPHA 单个Alpha

GL_LUMINANCE_ALPHA 先是亮度分量,然后是Alpha

GL_STENCIL_INDEX 单个的模板索引

GL_DEPTH_COMPONENT 单个深度分量

++++++++++++++++++++++++++++++++++++++++++

+++++++++++++++++++ 数据类型+++++++++++++++++++

GL_UNSIGNED_BYTE 无符号的8位整数

GL_BYTE 8位整数

GL_BITMAP 无符号的8位整数数组中的单个数位

GL_UNSIGNED_SHORT 无符号的16位整数

GL_SHORT 16位整数

GL_UNSIGNED_INT 无符号的32位整数

GL_INT 32位整数

GL_FLOAT 单精度浮点数

+++++++++++++++++++++++++++++++++++++++

同时,我们知道OpenGL的绘制是从上到下的,因此我们获得像素数据后,要将数据倒序排列下;

实现代码如下:

unsigned char screenBuffer[1024 * 1024 * 8];
void HelloWorld::screenPlot2(bool upsidedown)
{
	CCSize winSize = CCDirector::sharedDirector()->getWinSizeInPixels();
	int w = winSize.width;
	int h = winSize.height;
	int  myDataLength = w * h * 4;

	GLubyte* buffer = screenBuffer;
	/// 获取屏幕像素
	glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

	CCImage* image = new CCImage();
	if(upsidedown)
	{
		GLubyte* buffer2 = (GLubyte*) malloc(myDataLength);
		/// 像素数据倒序排列
		for(int y = 0; y <h; y++)
		{
			for(int x = 0; x <w * 4; x++)
			{
				buffer2[(h - 1 - y) * w * 4 + x] = buffer[y * 4 * w + x];
			}
		}
		image->initWithImageData(buffer2, myDataLength, CCImage::kFmtRawData, w, h);
		free(buffer2);
	}
	else 
	{
		image->initWithImageData(buffer, myDataLength, CCImage::kFmtRawData, w, h);
	}

	image->saveToFile("12.png");		/// 将截屏数据保存
	image->release();
}

我们将这段代码放入update函数中,时刻运行,查看效率如下:


发现效率并不是很高。

         另一个方面,cocos2d-x中,统一实现了一个渲染纹理类CCRenderTexture,其作用是将绘图设备从屏幕转移到一张纹理上,从而使得一段连续的绘图被保存到纹理中。

///@brief 截屏
///@param[in] r0、r1是要截屏的区域
///@return true---截取成功, false---截取失败
///@author DionysosLai,906391500@qq.com
///@retval 
///@post
bool HelloWorld::screenPlot1(const CCPoint r0, const CCPoint r1)
{	
	/// 判断r0 、r1能否组成一个矩形
	if (r0.x == r1.x || r0.y == r1.y)
	{
		return false;
	}
	
	CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();

	//根据要截取屏幕大小,定义一个渲染纹理  
	CCRenderTexture* renderTexture = CCRenderTexture::create(abs(r0.x - r1.x), abs(r0.y - r1.y));

	CCScene* pCurScene = CCDirector::sharedDirector()->getRunningScene();
	/*	CCScene*/
	CCPoint ancPos = pCurScene->getAnchorPoint();

	//渲染纹理开始捕捉  
	renderTexture->begin();

	//绘制当前场景  
	pCurScene->visit();
	//结束  
	renderTexture->end();
 
	renderTexture->saveToFile("13.png");

	renderTexture->setPosition(ccp(visibleSize.width/2.0f, visibleSize.height/2.0f));
	this->addChild(renderTexture, 1);

//	CC_SAFE_RELEASE(renderTexture);

	return true;
} 

在这里有个注意点,就是我将renderTexture释放语句给注释掉了,之所以要注释,原因在这里我们主动调用了 导演类的绘制场景功能。根据引擎的接口规范,我们不建议这样做,因为每次都产生了CCNode类的visit函数的调用,但只要遵守不再visit中更改绘图相关状态的规范,可以保证不对后续绘图产生影响。

         同样,我们每帧都运行这个代码,看起效率怎样:运行是数字显示时6点多,效率更低一点。



但效率差不多。其实,这时因为在CCRenderTexture的内部实现,其到处纹理的过程实际上也是利用glReadPixels函数来获取像素信息的。因此两个方法,我们可以任意取一个,但建议用第二个。

Ps:这里的效率其实,有很大一部分,是浪费在saveToFile这条语句上。

         ==,有没有发现,这张图和前面的图不一样。是不是少了左下角数字。这个问题,留着下次再说吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值