对GLSurfaceView截图

很多时候我们想从GLSurfaceView里直接获取图像,也就是截图,但是用普通的方法是没法获取到图像的,具体的方法看这里。

问题出现

我在实验一个相机功能的时候,我想“如果我把GLSurfaceView的尺寸设置好,把图像预览到GLSurfaceView里,我直接从GLSurfaceView把预览图扣出来这样拍照是不是也有拍照的效果呢?”
然后就出现问题了。。。

方法尝试

按照一般的View截图的方式,我尝试了使用

1
2
3
4
5
view.setDrawingCacheEnabled(true);
screenshot = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
Log.v(TAG, "Screenshot height: " + screenshot.getHeight());
image.setImageBitmap(screenshot);

结果很悲剧,失败了,我截取到的图是纯黑的。

原因分析

SurfaceView绘制图像的方式和普通的View是不一样的,SurfaceView绘制的时候是分前景曾和背景层的……具体的原理呢,还是得阅读源码

处理方法

我们需要自定义一个GLSurfaceView的子类,自定义实现GLSurfaceView的Renderer接口,在GLSurfaceView把图像显示出来的时候抓取并保持下来。

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
* 可以截图的GLSurfaceView
* 使用的时候 setShouldTakePic(true)
* Created by James.H.Zhang on 2015/5/13.
*/
public class MySurfaceView extends GLSurfaceView {
boolean shouldTakePic = false;
File tempDir;
private MyRenderer renderer;
public void setShouldTakePic(boolean shouldTakePic) {
this.shouldTakePic = shouldTakePic;
}
public MySurfaceView(Context context) {
super(context);
renderer = new MyRenderer();
setRenderer(renderer);
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
renderer = new MyRenderer();
setRenderer(renderer);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
class MyRenderer implements GLSurfaceView.Renderer {
int surfaceWidth;
int surfaceHeight;
public MyRenderer() {
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
surfaceWidth = width;
surfaceHeight = height;
}
@Override
public void onDrawFrame(GL10 gl) {
try {
if (shouldTakePic) {
shouldTakePic = false;
int w = surfaceWidth;
int h = surfaceHeight;
int b[] = new int[(int) (w * h)];
int bt[] = new int[(int) (w * h)];
IntBuffer buffer = IntBuffer.wrap(b);
buffer.position(0);
GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
int pix = b[i * w + j];
int pb = (pix >> 16) & 0xff;
int pr = (pix << 16) & 0x00ff0000;
int pix1 = (pix & 0xff00ff00) | pr | pb;
bt[(h - i - 1) * w + j] = pix1;
}
}
Bitmap inBitmap = null;
if (inBitmap == null || !inBitmap.isMutable()
|| inBitmap.getWidth() != w || inBitmap.getHeight() != h) {
inBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
//为了图像能小一点,使用了RGB_565而不是ARGB_8888
}
inBitmap.copyPixelsFromBuffer(buffer);
inBitmap = Bitmap.createBitmap(bt, w, h, Bitmap.Config.RGB_565);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
inBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bos);
byte[] bitmapData = bos.toByteArray();
ByteArrayInputStream fis = new ByteArrayInputStream(bitmapData);
String tempPicFile = "temp_" + System.currentTimeMillis() + ".jpeg";
tempDir = new File(Environment.getExternalStorageDirectory() + File.separator +
"SurfaceScreenShot" + File.separator + "Images");
tempDir.mkdirs();
try {
File tmpFile = new File(tempDir, tempPicFile);
FileOutputStream fos = new FileOutputStream(tmpFile);
byte[] buf = new byte[1024];
int len;
while ((len = fis.read(buf)) > 0) {
fos.write(buf, 0, len);
}
fis.close();
fos.close();
inBitmap.recycle();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

在想要想要截图的时候,也就是onClick的时候setShouldTakePic(true)就可以截取到一张图了

资料

http://blog.csdn.net/luoshengyang/article/details/8661317

http://stackoverflow.com/questions/5514149/capture-screen-of-glsurfaceview-to-bitmap

http://www.oschina.net/question/1027071_106109