项目中需要在游戏中截图并保存到相册下,花了一点时间研究了下,查看了很多文章,走了不少弯路,终于把截图功能做出来了.
在此记一下实现过程.
刚开始查看了cocos渲染相关的cc.RenderTexture类,代码如下:
screenShot: function () {
let renderTexture = cc.RenderTexture.create(1280, 720);//1280*720
renderTexture.begin();
//cc.Canvas.instance.node 是我们要截图的节点,如果要截整个屏幕,可以把 node 换成 Canvas 节点即可
cc.Canvas.instance.node._sgNode.visit();
renderTexture.end();
//保存为png格式 的图片
renderTexture.saveToFile("demo.png", cc.ImageFormat.PNG, true, function () {
cc.log("capture screen successfully!");
});
}
执行这段代码,截图会在存放在应用的私有目录下,相册是扫描不到这张png图片的,如果想要在相册中展示,
就需要使用jsb来保存到相册目录,但这样又有新问题,不同手机的Camera路径不同,不能写死.
所以放弃使用cc.RenderTexture,转而用Android原生实现.
关于RenderTexture可以看这个api文档:
http://api.cocos.org/d9/ddc/classcocos2d_1_1_render_texture.html#a123fd143b49aee0e1f1cd741d9857c51
———————————-华丽分割线—————————————————-
用Android实现:
creator中js和android通过jsb互相通信可以看这篇官方文档
http://www.cocos.com/docs/creator/advanced-topics/java-reflection.html
下面是具体的android截屏代码:
View decorView = getWindow().getDecorView();
Bitmap screenBitmap = Bitmap.createBitmap(decorView.getWidth(), decorView.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(screenBitmap);
decorView.draw(canvas);
MediaStore.Images.Media.insertImage(MainActivity.this.getContentResolver(), screenBitmap, "截图-20171018",
"这是我的截图");
重写个小Demo,把代码粘进去运行,发现可以把屏幕截图保存并且能被扫描到,心里那叫一个爽,这么简单,完全没一点难度嘛,紧接着就把这段代码移植到项目里.
运行->截图->查看图片->全黑…
//满脑袋的黑人问号脸…
卧槽!不对啊Demo运行的好好的,怎么移植到项目里就不行了,难道我敲代码时,坐的姿势不对?应该面朝南?
有重新构建一遍项目,问题依旧.
想了一会,忽然意识到,Demo的截图代码是运行在UI线程,也就是主线程,
游戏是运行在GL线程,是一个独立的线程…会不会跟这有关系?
在网上找了好久找了好几篇关于GlSurfaceView截图黑屏的文章
http://blog.csdn.net/afei__/article/details/51614375
http://blog.csdn.net/zh13544539220/article/details/45058945
GLSurfaceView截取图像的时候,往往传统的方法并不行得通,我们发现使用GLSurfaceView.getDrawingCache()等方法得到的往往是一张纯黑的图,这是由于GLSurfaceView和SurfaceView一样都有一块透明的缓存区域,所以我们截取的往往只是这块透明的缓存区域。
找到原因了.然后继续找方法截取glsurfaceview;
有关surfaceview和opengl的渲染的文章看得我晕头转向
不过最后终于摸索成功了:
首先这个onDrawFrame方法和update方法一样..也是每秒调用60次,这样,我们就可以在这个方法内实现截图功能.
截图代码如下:
@Override
public void onDrawFrame(final GL10 gl) {
if(isf){
Log.d("dog","onDrawFrame");
Bitmap bitmap = getBitmapFromGL(this.mScreenWidth,this.mScreenHeight, gl);
screen(bitmap );
isf=false;
}
}
/**
*
* 将GL10帧数据保存为图片
*
* @param w 截图宽
* @param h 截图高
* @param gl gl
* @return
*/
private Bitmap getBitmapFromGL(int w, int h, GL10 gl) {
int b[] = new int[w * (h)];
int bt[] = new int[w * h];
IntBuffer ib = IntBuffer.wrap(b);
ib.position(0);
gl.glReadPixels(0, 0, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
for (int i = 0, k = 0; i < h; i++, k++) {
for (int j = 0; j < w; j++) {
int pix = b[i * w + j];
int pb = (pix >> 16) & 0xff;
int pr = (pix << 16) & 0xffff0000;
int pix1 = (pix & 0xff00ff00) | pr | pb;
bt[(h - k - 1) * w + j] = pix1;
}
}
return Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
}
这个方法会返回一个bitmap,然后直接创建流保存这个bitmap就好了
保存代码:
//本地保存图片
public static void screen(Bitmap bitmap) {
try {
if(!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){//内置存储卡不存在
Log.d("dog","存储卡不存在");
return;
}
String str= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath();
Log.d("dog",str);
final String dir=str+"/Camera"; //保存到相册
File directory = new File(dir);
if(!directory.exists()){ //是否存在目录
Log.d("dog","路径不存在");
directory = new File(str); //不存在就是用DCIM目录
}
directory.mkdirs();
// final File smdir=directory; //提供引用 发送广播 ,刷新图库
String name=System.currentTimeMillis()+".png"; //图片名
final File file = new File(directory,name);
FileOutputStream fos = new FileOutputStream(file);
final Boolean bol=bitmap.compress(Bitmap.CompressFormat.PNG, 50, fos);
fos.flush();
fos.close();
// MediaStore.Images.Media.insertImage(instance.getContentResolver(), file.getAbsolutePath(),
// "截图-20171018", "这是我的截图"); //刷新
Log.d("dog", file.toString());
instance.runOnUiThread(new Runnable() {
@Override
public void run() {
if(bol){
Toast.makeText(instance, "截图成功 路径:"+file.getAbsolutePath(), Toast.LENGTH_LONG).show();
Intent intent=new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Log.d("dog",file.getAbsolutePath());
Uri uri = Uri.fromFile(file);
intent.setData(uri);
instance.sendBroadcast(intent);//发送广播..通知相册刷新
}else{
Toast.makeText(instance, "保存失败", Toast.LENGTH_LONG).show();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
把代码移植到游戏里,运行,完美截图,完美刷新到相册…
除了这两种..网上的文章也有好多关于其他方式截图的,大家可以自己选择熟悉的方式,我这种就稍微嫌麻烦了,不过胜在能解决问题.
注意:
glSurfaceView.requestRender(); 调用这个api会重绘一个surfaceview,也就是调用一次onDrawFrame();
在这粘一下 游戏的h5版本,点开即玩:
http://m.qunhei.com/member/login.html?unid=qunhei&gid=3470&gourl=/member/index.html