1. 程式人生 > >把Android原生的View渲染到OpenGL Texture

把Android原生的View渲染到OpenGL Texture

revert keyword attribute slist ast ava ews emp processes

http://blog.csdn.net/u010949962/article/details/41865777

最近要把Android 原生的View渲染到OpenGL GLSurfaceView中,起初想到的是截圖的方法,也就是把View截取成bitmap後,再把Bitmap渲染到OpenGL中;但是明顯這種方法是不可行的,面對一些高速動態更新的View,只有不停的對view 進行截圖才能渲染出原生View的效果。

通過大量的Google終於在國外的網站找到了一個做過類似的先例(鏈接:http://www.felixjones.co.uk/neo%20website/Android_View/)。不過經過測試該方法只能渲染直接父類為View的view,也就是只能渲染一層View(如progressbar,沒不能添加child的view),當該原生Android View包含很多子view時(也就是根View為FramLayout、或者linearLayout之類),無法實時的監聽到View動態改變,OpenGL中只能不停的渲染該view,才能渲染出原生View的效果。但是這樣一來不同的渲染會耗費大量的資源,降低應用程序的效率。理想中的話,是監聽到了該View的內容或者其子view 的內容發生了變化(如:View中的字幕發生滾動)才進行渲染。

經過接近兩周的努力我終於完美地實現了該效果,既然是站在別人的基礎上得來的成果,那麽該方法就應當被共享,所以產生了此文,不過只支持api 15以上的

步驟一:重寫根View

1.設置該View 繪制自己:

[java] view plain copy 技術分享技術分享
  1. setWillNotDraw(false);

2.監聽View的變化,重寫View,用ViewTreeObServer來監聽,方法如下:

[java] view plain copy 技術分享技術分享
  1. private void addOnPreDrawListener() {
  2. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
  3. final ViewTreeObserver mObserver = getViewTreeObserver();
  4. if (mObserver != null) {
  5. mObserver.addOnPreDrawListener(new OnPreDrawListener() {
  6. @Override
  7. public boolean onPreDraw() {
  8. if (isDirty()) {//View或者子view發生變化
  9. invalidate();
  10. }
  11. return true;
  12. }
  13. });
  14. }
  15. }
  16. }

3.重寫該View的onDraw方法:

[java] view plain copy 技術分享技術分享
  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. try {
  4. if (mSurface != null) {
  5. Canvas surfaceCanvas = mSurface.lockCanvas(null);
  6. super.dispatchDraw(surfaceCanvas);
  7. mSurface.unlockCanvasAndPost(surfaceCanvas);
  8. mSurface.release();
  9. mSurface = null;
  10. mSurface = new Surface(mSurfaceTexture);
  11. }
  12. } catch (OutOfResourcesException e) {
  13. e.printStackTrace();
  14. }
  15. }

步驟二:GLSurfaceView.Renderer

[java] view plain copy 技術分享技術分享
  1. class CustomRenderer implements GLSurfaceView.Renderer {
  2. int glSurfaceTex;
  3. private final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
  4. long currentTime;
  5. long previousTime;
  6. boolean b = false;
  7. int frameCount = 0;
  8. DirectDrawer mDirectDrawer;
  9. ActivityManager activityManager;
  10. MemoryInfo _memoryInfo;
  11. // Fixed values
  12. private int TEXTURE_WIDTH = 360;
  13. private int TEXTURE_HEIGHT = 360;
  14. Context context;
  15. private LauncherAppWidgetHostView addedWidgetView;
  16. private SurfaceTexture surfaceTexture = null;
  17. private Surface surface;
  18. float fps;
  19. public CustomRenderer(Context context, LauncherAppWidgetHostView addedWidgetView, Display mDisplay){
  20. this.context = context;
  21. this.addedWidgetView = addedWidgetView;
  22. TEXTURE_WIDTH = mDisplay.getWidth();
  23. TEXTURE_HEIGHT = mDisplay.getHeight();
  24. _memoryInfo = new MemoryInfo();
  25. activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
  26. }
  27. @Override
  28. public void onDrawFrame(GL10 gl) {
  29. synchronized (this) {
  30. surfaceTexture.updateTexImage();
  31. }
  32. activityManager.getMemoryInfo(_memoryInfo);
  33. GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
  34. GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
  35. GLES20.glEnable(GLES20.GL_BLEND);
  36. GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
  37. float[] mtx = new float[16];
  38. surfaceTexture.getTransformMatrix(mtx);
  39. mDirectDrawer.draw(mtx);
  40. calculateFps();
  41. //getAppMemorySize();
  42. //getRunningAppProcessInfo();
  43. //Log.v("onDrawFrame", "FPS: " + Math.round(fps) + ", availMem: " + Math.round(_memoryInfo.availMem / 1048576) + "MB");
  44. }
  45. private void getAppMemorySize(){
  46. ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  47. android.os.Debug.MemoryInfo[] memoryInfos = mActivityManager.getProcessMemoryInfo(new int[]{android.os.Process.myPid()});
  48. int size = memoryInfos[0].dalvikPrivateDirty;
  49. Log.w("getAppMemorySize", size / 1024 + " MB");
  50. }
  51. private void getRunningAppProcessInfo() {
  52. ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  53. //獲得系統裏正在運行的所有進程
  54. List<RunningAppProcessInfo> runningAppProcessesList = mActivityManager.getRunningAppProcesses();
  55. for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcessesList) {
  56. // 進程ID號
  57. int pid = runningAppProcessInfo.pid;
  58. // 用戶ID
  59. int uid = runningAppProcessInfo.uid;
  60. // 進程名
  61. String processName = runningAppProcessInfo.processName;
  62. // 占用的內存
  63. int[] pids = new int[] {pid};
  64. Debug.MemoryInfo[] memoryInfo = mActivityManager.getProcessMemoryInfo(pids);
  65. int memorySize = memoryInfo[0].dalvikPrivateDirty;
  66. System.out.println("processName="+processName+",currentPid: "+ "pid= " +android.os.Process.myPid()+"----------->"+pid+",uid="+uid+",memorySize="+memorySize+"kb");
  67. }
  68. }
  69. @Override
  70. public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  71. surface = null;
  72. surfaceTexture = null;
  73. glSurfaceTex = Engine_CreateSurfaceTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT);
  74. Log.d("GLES20Ext", "glSurfaceTex" + glSurfaceTex);
  75. if (glSurfaceTex > 0) {
  76. surfaceTexture = new SurfaceTexture(glSurfaceTex);
  77. surfaceTexture.setDefaultBufferSize(TEXTURE_WIDTH, TEXTURE_HEIGHT);
  78. surface = new Surface(surfaceTexture);
  79. addedWidgetView.setSurface(surface);
  80. addedWidgetView.setSurfaceTexture(surfaceTexture);
  81. //addedWidgetView.setSurfaceTexture(surfaceTexture);
  82. mDirectDrawer = new DirectDrawer(glSurfaceTex);
  83. }
  84. }
  85. float calculateFps() {
  86. frameCount++;
  87. if (!b) {
  88. b = true;
  89. previousTime = System.currentTimeMillis();
  90. }
  91. long intervalTime = System.currentTimeMillis() - previousTime;
  92. if (intervalTime >= 1000) {
  93. b = false;
  94. fps = frameCount / (intervalTime / 1000f);
  95. frameCount = 0;
  96. Log.w("calculateFps", "FPS: " + fps);
  97. }
  98. return fps;
  99. }
  100. int Engine_CreateSurfaceTexture(int width, int height) {
  101. /*
  102. * Create our texture. This has to be done each time the surface is
  103. * created.
  104. */
  105. int[] textures = new int[1];
  106. GLES20.glGenTextures(1, textures, 0);
  107. glSurfaceTex = textures[0];
  108. if (glSurfaceTex > 0) {
  109. GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, glSurfaceTex);
  110. // Notice the use of GL_TEXTURE_2D for texture creation
  111. GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, width, height, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null);
  112. GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
  113. GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
  114. GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
  115. GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
  116. }
  117. return glSurfaceTex;
  118. }
  119. @Override
  120. public void onSurfaceChanged(GL10 gl, int width, int height) {
  121. }
  122. }

[java] view plain copy 技術分享技術分享
  1. public class DirectDrawer {
  2. private final String vertexShaderCode =
  3. "attribute vec4 vPosition;" +
  4. "attribute vec2 inputTextureCoordinate;" +
  5. "varying vec2 textureCoordinate;" +
  6. "void main()" +
  7. "{"+
  8. "gl_Position = vPosition;"+
  9. "textureCoordinate = inputTextureCoordinate;" +
  10. "}";
  11. private final String fragmentShaderCode =
  12. "#extension GL_OES_EGL_image_external : require\n"+
  13. "precision mediump float;" +
  14. "varying vec2 textureCoordinate;\n" +
  15. "uniform samplerExternalOES s_texture;\n" +
  16. "void main() {" +
  17. " gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
  18. "}";
  19. private FloatBuffer vertexBuffer, textureVerticesBuffer;
  20. private ShortBuffer drawListBuffer;
  21. private final int mProgram;
  22. private int mPositionHandle;
  23. private int mTextureCoordHandle;
  24. private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
  25. // number of coordinates per vertex in this array
  26. private static final int COORDS_PER_VERTEX = 2;
  27. private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
  28. static float squareCoords[] = {
  29. -1.0f, 0.0f,
  30. -1.0f, -2.2f,
  31. 1.0f, -2.2f,
  32. 1.0f, 0.0f,
  33. };
  34. static float textureVertices[] = {
  35. 0f, 0f,
  36. 0f, 1f,
  37. 1f, 1f,
  38. 1f, 0f,
  39. };
  40. private int texture;
  41. public DirectDrawer(int texture)
  42. {
  43. this.texture = texture;
  44. // initialize vertex byte buffer for shape coordinates
  45. ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
  46. bb.order(ByteOrder.nativeOrder());
  47. vertexBuffer = bb.asFloatBuffer();
  48. vertexBuffer.put(squareCoords);
  49. vertexBuffer.position(0);
  50. // initialize byte buffer for the draw list
  51. ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
  52. dlb.order(ByteOrder.nativeOrder());
  53. drawListBuffer = dlb.asShortBuffer();
  54. drawListBuffer.put(drawOrder);
  55. drawListBuffer.position(0);
  56. ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
  57. bb2.order(ByteOrder.nativeOrder());
  58. textureVerticesBuffer = bb2.asFloatBuffer();
  59. textureVerticesBuffer.put(textureVertices);
  60. textureVerticesBuffer.position(0);
  61. int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
  62. int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
  63. mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
  64. GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
  65. GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
  66. GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables
  67. }
  68. public void draw(float[] mtx)
  69. {
  70. GLES20.glUseProgram(mProgram);
  71. GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  72. GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);
  73. // get handle to vertex shader‘s vPosition member
  74. mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
  75. // Enable a handle to the triangle vertices
  76. GLES20.glEnableVertexAttribArray(mPositionHandle);
  77. // Prepare the <insert shape here> coordinate data
  78. GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
  79. mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
  80. GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
  81. // textureVerticesBuffer.clear();
  82. // textureVerticesBuffer.put( transformTextureCoordinates(
  83. // textureVertices, mtx ));
  84. // textureVerticesBuffer.position(0);
  85. GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureVerticesBuffer);
  86. GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
  87. // Disable vertex array
  88. GLES20.glDisableVertexAttribArray(mPositionHandle);
  89. GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
  90. }
  91. private int loadShader(int type, String shaderCode){
  92. // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
  93. // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
  94. int shader = GLES20.glCreateShader(type);
  95. // add the source code to the shader and compile it
  96. GLES20.glShaderSource(shader, shaderCode);
  97. GLES20.glCompileShader(shader);
  98. return shader;
  99. }
  100. private float[] transformTextureCoordinates( float[] coords, float[] matrix)
  101. {
  102. float[] result = new float[ coords.length ];
  103. float[] vt = new float[4];
  104. for ( int i = 0 ; i < coords.length ; i += 2 ) {
  105. float[] v = { coords[i], coords[i+1], 0 , 1 };
  106. Matrix.multiplyMV(vt, 0, matrix, 0, v, 0);
  107. result[i] = vt[0];
  108. result[i+1] = vt[1];
  109. }
  110. return result;
  111. }
  112. }

步驟三:配置GLSurfaceView:

[java] view plain copy 技術分享技術分享
  1. GLSurfaceView glSurfaceView = new GLSurfaceView(getApplicationContext());
  2. // Setup the surface view for drawing to
  3. glSurfaceView.setEGLContextClientVersion(2);
  4. glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
  5. glSurfaceView.setRenderer(renderer);
  6. //glSurfaceView.setZOrderOnTop(true);
  7. // Add our WebView to the Android View hierarchy
  8. glSurfaceView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));

效果如圖所示,上面是獵豹清理大師的widget,下面是GLSurfaceView的Texture,原生的Widget和OpenGL中渲染的一模一樣。並且點擊widget進行清理時也能達到實時渲染的Veiw動畫清理效果,源碼鏈接:https://github.com/MrHuangXin/RenderViewToOpenGL/tree/master

技術分享

把Android原生的View渲染到OpenGL Texture