首页 > 代码库 > 把Android原生的View渲染到OpenGL Texture

把Android原生的View渲染到OpenGL Texture

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.   
  3.     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {  
  4.   
  5.         final ViewTreeObserver mObserver = getViewTreeObserver();  
  6.         if (mObserver != null) {  
  7.             mObserver.addOnPreDrawListener(new OnPreDrawListener() {  
  8.   
  9.                 @Override  
  10.                 public boolean onPreDraw() {  
  11.                     if (isDirty()) {//View或者子view发生变化  
  12.                         invalidate();  
  13.                     }  
  14.                     return true;  
  15.                 }  
  16.             });  
  17.         }  
  18.     }  
  19. }  

 

 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.               
  9.             mSurface.release();  
  10.             mSurface = null;  
  11.             mSurface = new Surface(mSurfaceTexture);  
  12.         }  
  13.     } catch (OutOfResourcesException e) {  
  14.         e.printStackTrace();  
  15.     }  
  16. }  

步骤二: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.       
  12.   
  13.     // Fixed values  
  14.     private int TEXTURE_WIDTH = 360;  
  15.     private int TEXTURE_HEIGHT = 360;  
  16.   
  17.     Context context;  
  18.       
  19.     private LauncherAppWidgetHostView addedWidgetView;  
  20.       
  21.     private SurfaceTexture surfaceTexture = null;  
  22.   
  23.     private Surface surface;  
  24.   
  25.     float fps;  
  26.       
  27.     public CustomRenderer(Context context, LauncherAppWidgetHostView addedWidgetView, Display mDisplay){  
  28.         this.context = context;  
  29.         this.addedWidgetView = addedWidgetView;  
  30.         TEXTURE_WIDTH = mDisplay.getWidth();  
  31.         TEXTURE_HEIGHT = mDisplay.getHeight();  
  32.         _memoryInfo = new MemoryInfo();  
  33.         activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);  
  34.     }  
  35.       
  36.     @Override  
  37.     public void onDrawFrame(GL10 gl) {  
  38.   
  39.         synchronized (this) {  
  40.             surfaceTexture.updateTexImage();  
  41.         }  
  42.   
  43.         activityManager.getMemoryInfo(_memoryInfo);  
  44.   
  45.         GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);  
  46.         GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);  
  47.         GLES20.glEnable(GLES20.GL_BLEND);  
  48.         GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);  
  49.   
  50.         float[] mtx = new float[16];  
  51.         surfaceTexture.getTransformMatrix(mtx);  
  52.         mDirectDrawer.draw(mtx);  
  53.   
  54.         calculateFps();  
  55.         //getAppMemorySize();  
  56.         //getRunningAppProcessInfo();  
  57.         //Log.v("onDrawFrame", "FPS: " + Math.round(fps) + ", availMem: " + Math.round(_memoryInfo.availMem / 1048576) + "MB");  
  58.     }  
  59.       
  60.     private void getAppMemorySize(){  
  61.         ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);  
  62.         android.os.Debug.MemoryInfo[] memoryInfos = mActivityManager.getProcessMemoryInfo(new int[]{android.os.Process.myPid()});  
  63.         int size = memoryInfos[0].dalvikPrivateDirty;  
  64.         Log.w("getAppMemorySize", size / 1024 + " MB");  
  65.     }  
  66.   
  67.       
  68.     private void getRunningAppProcessInfo() {    
  69.         ActivityManager  mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);    
  70.         
  71.         //获得系统里正在运行的所有进程    
  72.         List<RunningAppProcessInfo> runningAppProcessesList = mActivityManager.getRunningAppProcesses();    
  73.         
  74.         for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcessesList) {    
  75.             // 进程ID号    
  76.             int pid = runningAppProcessInfo.pid;    
  77.             // 用户ID    
  78.             int uid = runningAppProcessInfo.uid;    
  79.             // 进程名    
  80.             String processName = runningAppProcessInfo.processName;    
  81.             // 占用的内存    
  82.             int[] pids = new int[] {pid};    
  83.             Debug.MemoryInfo[] memoryInfo = mActivityManager.getProcessMemoryInfo(pids);    
  84.             int memorySize = memoryInfo[0].dalvikPrivateDirty;    
  85.         
  86.             System.out.println("processName="+processName+",currentPid: "+  "pid= " +android.os.Process.myPid()+"----------->"+pid+",uid="+uid+",memorySize="+memorySize+"kb");    
  87.         }    
  88.     }  
  89.       
  90.     @Override  
  91.     public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
  92.   
  93.         surface = null;  
  94.         surfaceTexture = null;  
  95.   
  96.         glSurfaceTex = Engine_CreateSurfaceTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT);  
  97.         Log.d("GLES20Ext", "glSurfaceTex" + glSurfaceTex);  
  98.         if (glSurfaceTex > 0) {  
  99.             surfaceTexture = new SurfaceTexture(glSurfaceTex);  
  100.             surfaceTexture.setDefaultBufferSize(TEXTURE_WIDTH, TEXTURE_HEIGHT);  
  101.             surface = new Surface(surfaceTexture);  
  102.             addedWidgetView.setSurface(surface);  
  103.             addedWidgetView.setSurfaceTexture(surfaceTexture);  
  104.             //addedWidgetView.setSurfaceTexture(surfaceTexture);  
  105.             mDirectDrawer = new DirectDrawer(glSurfaceTex);  
  106.         }  
  107.     }  
  108.   
  109.     float calculateFps() {  
  110.   
  111.         frameCount++;  
  112.         if (!b) {  
  113.             b = true;  
  114.             previousTime = System.currentTimeMillis();  
  115.         }  
  116.         long intervalTime = System.currentTimeMillis() - previousTime;  
  117.   
  118.         if (intervalTime >= 1000) {  
  119.             b = false;  
  120.             fps = frameCount / (intervalTime / 1000f);  
  121.             frameCount = 0;  
  122.             Log.w("calculateFps", "FPS: " + fps);  
  123.         }  
  124.   
  125.         return fps;  
  126.     }  
  127.   
  128.     int Engine_CreateSurfaceTexture(int width, int height) {  
  129.         /* 
  130.          * Create our texture. This has to be done each time the surface is 
  131.          * created. 
  132.          */  
  133.   
  134.         int[] textures = new int[1];  
  135.         GLES20.glGenTextures(1, textures, 0);  
  136.   
  137.         glSurfaceTex = textures[0];  
  138.   
  139.         if (glSurfaceTex > 0) {  
  140.             GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, glSurfaceTex);  
  141.   
  142.             // Notice the use of GL_TEXTURE_2D for texture creation  
  143.             GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, width, height, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null);  
  144.   
  145.             GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);  
  146.             GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);  
  147.   
  148.             GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);  
  149.             GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);  
  150.         }  
  151.         return glSurfaceTex;  
  152.     }  
  153.   
  154.     @Override  
  155.     public void onSurfaceChanged(GL10 gl, int width, int height) {  
  156.           
  157.     }  
  158. }  

 

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

步骤三:配置GLSurfaceView:

[java] view plain copy
 
 技术分享技术分享
  1. GLSurfaceView glSurfaceView = new GLSurfaceView(getApplicationContext());  
  2. // Setup the surface view for drawing to  
  3.   
  4. glSurfaceView.setEGLContextClientVersion(2);  
  5. glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);         
  6. glSurfaceView.setRenderer(renderer);  
  7. //glSurfaceView.setZOrderOnTop(true);  
  8.   
  9. // Add our WebView to the Android View hierarchy  
  10. 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