游戏内截图,surfaceView截图

项目中需要在游戏中截图并保存到相册下,花了一点时间研究了下,查看了很多文章,走了不少弯路,终于把截图功能做出来了.
在此记一下实现过程.

刚开始查看了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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值