2013-12-16 41 views
2

我想呈現一個gsurfaceview的輸出到一個PNG的SD卡上,我有一些問題。我花了幾天的時間嘗試對類似的SO查詢進行排序,這簡直超出了我的專業水平。有人可以幫助我通過下面的日誌排序,看看我可能會出錯的地方。GLSURFACEVIEW保存爲PNG - 錯誤glReadPixels

非常感謝。

登錄:

12-16 12:09:18.831: E/AndroidRuntime(29864): FATAL EXCEPTION: GLThread 2712 
12-16 12:09:18.831: E/AndroidRuntime(29864): java.nio.BufferUnderflowException 
12-16 12:09:18.831: E/AndroidRuntime(29864): at java.nio.Buffer.checkGetBounds(Buffer.java:177) 
12-16 12:09:18.831: E/AndroidRuntime(29864): at java.nio.DirectByteBuffer.get(DirectByteBuffer.java:66) 
12-16 12:09:18.831: E/AndroidRuntime(29864): at java.nio.IntToByteBufferAdapter.get(IntToByteBufferAdapter.java:105) 
12-16 12:09:18.831: E/AndroidRuntime(29864): at java.nio.IntBuffer.get(IntBuffer.java:234) 
12-16 12:09:18.831: E/AndroidRuntime(29864): at com.research.glgrade.GLLayer.grabPixels(GLLayer.java:865) 
12-16 12:09:18.831: E/AndroidRuntime(29864): at com.research.glgrade.GLLayer.saveScreenShot(GLLayer.java:810) 
12-16 12:09:18.831: E/AndroidRuntime(29864): at com.research.glgrade.GLLayer.onDrawFrame(GLLayer.java:794) 
12-16 12:09:18.831: E/AndroidRuntime(29864): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1527) 
12-16 12:09:18.831: E/AndroidRuntime(29864): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1240) 
12-16 12:09:26.849: I/Choreographer(29864): Skipped 478 frames! The application may be doing too much work on its main thread. 

這裏是我當前的代碼: 從主要活動:

GlobalVariables.setPrint("true"); 
mView.requestRender(); 

從呈現類

public void onDrawFrame(GL10 glUnused) { 
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 
    final String vertexShader = getVertexShader(); 
    final String fragmentShader = getFragmentShader(); 

    final int vertexShaderHandle = ShaderHelper.compileShader(
      GLES20.GL_VERTEX_SHADER, vertexShader); 
    final int fragmentShaderHandle = ShaderHelper.compileShader(
      GLES20.GL_FRAGMENT_SHADER, fragmentShader); 

    mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, 
      fragmentShaderHandle, new String[] { "a_Position", 
        "a_TexCoordinate" }); 

    // Set our per-vertex lighting program. 
    GLES20.glUseProgram(mProgramHandle); 
      // Set program handles for cube drawing. 
    mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, 
      "u_MVPMatrix"); 
    mTextureUniformHandle0 = GLES20.glGetUniformLocation(mProgramHandle, 
      "u_Texture0"); 
    mTextureUniformHandle1 = GLES20.glGetUniformLocation(mProgramHandle, 
      "u_Texture1"); 

      GLES20.glActiveTexture(GLES20.GL_TEXTURE4); 
      // Bind the texture to this unit. 
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle4); 
      // Tell the texture uniform sampler to use this texture in the shader by 
      // binding to texture unit 3. 
    GLES20.glUniform1i(mTextureUniformHandle4, 4); 


    // Draw some cubes. 
    Matrix.setIdentityM(mModelMatrix, 0); 


    Matrix.translateM(mModelMatrix, 0, mTransX, mTransY, mAngle*0.05f); 
    Matrix.rotateM(mModelMatrix, 0, 0.0f, 1.0f, 1.0f, 0.0f); 
    drawCube(); 


    int width_surfacea = width_surface ; 
     int height_surfacea = height_surface ; 

    if (GlobalVariables.getPrint() != "false") { 

     String mFrameCount = "1"; 
     saveScreenShot(0, 0, width_surfacea, height_surfacea, "/save/test.png"); 
     GlobalVariables.setPrint("false"); 


    } 

} 

    public void saveScreenShot(int x, int y, int w, int h, String filename) { 

    Bitmap bmp = grabPixels(x, y, w, h); 

    try { 
     String path = Environment.getExternalStorageDirectory() + "/" + filename; 

     File file = new File(path); 
     file.createNewFile(); 

     FileOutputStream fos = new FileOutputStream(file); 
     bmp.compress(CompressFormat.PNG, 100, fos); 

     fos.flush(); 

     fos.close(); 

    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

public Bitmap grabPixels(int x, int y, int w, int h) { 

    int screenshotSize = w * h; 

    ByteBuffer bb = ByteBuffer.allocateDirect(screenshotSize * 3); 
    bb.order(ByteOrder.nativeOrder()); 
    bb.position(0); 
    GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, bb); 
    int pixelsBuffer[] = new int[screenshotSize]; 

    bb.asIntBuffer().get(pixelsBuffer); 
    bb = null; 

    Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565); 

    bitmap.setPixels(pixelsBuffer, screenshotSize-w, -w, 0, 0, w, h); 

    pixelsBuffer = null; 

    short sBuffer[] = new short[screenshotSize]; 
    ShortBuffer sb = ShortBuffer.wrap(sBuffer); 
    bitmap.copyPixelsToBuffer(sb); 

    //Making created bitmap (from OpenGL points) compatible with Android bitmap 
    for (int i = 0; i < screenshotSize; ++i) {     
     short v = sBuffer[i]; 
     sBuffer[i] = (short) (((v&0x1f) << 11) | (v&0x7e0) | ((v&0xf800) >> 11)); 
    } 
    sb.rewind(); 
    bitmap.copyPixelsFromBuffer(sb); 


    return bitmap; 

} 
+0

當您選擇GL上下文時,您選擇了哪種像素格式?我主要活動中的 –

+0

onCreate將mView定義爲mView.setZOrderOnTop(false); mView.setEGLContextClientVersion(2); mView.setRenderer(new GLLayer(this)); mView.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR); //mView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); mView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); – user1540142

+0

GLSurfaceview沒有特定的顏色定義。 – user1540142

回答

2

您有陣列長度不匹配。 代碼行:

bb.asIntBuffer().get(pixelsBuffer); 

導致異常因爲bb.asIntBuffer()的長度僅爲(W * H * 3),同時pixelsBuffer的長度爲W * H * 4(當然,我談到字節)。 所以get()方法,因爲它應該(根據文檔)拋出一個異常 http://developer.android.com/reference/java/nio/IntBuffer.html#get%28int[]%29

更新:

試試這個代碼(警告:我還沒有嘗試編譯):

public Bitmap grabPixels(int x, int y, int w, int h) { 

    // Allocate byte array of H*W size assuming that we retrieve only R,G and B values 
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(screenshotSize * 3); 
    byteBuffer.order(ByteOrder.nativeOrder()); 
    byteBuffer.position(0); 
    // Read RGB values to our byte array 
    GLES20.glReadPixels(x, y, w, h, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, byteBuffer); 

    //Now we need to convert the 24bit RGB data to ARGB8888 format 
    // I like arrays, they are honest :) 
    byte[] pixelBuffer = byteBuffer.array(); 
    // This is an array that we going to use as backend for the bitmap 
    int[] finalPixels = new int[w*h]; 
    // Let the magic flow 
    int j = 0; 
    for (int i = 0; i < pixelBuffer.length; i += 3) { 
     byte red = pixelBuffer[i]; 
     byte green = pixelBuffer[i+1]; 
     byte blue = pixelBuffer[i+2]; 

     finalPixels[j++] = 0xFF000000 | ((int)blue << 16) | ((int)green << 8) | red; 
    } 

    // Create a bitmap of ARGB_8888 
    return Bitmap.createBitmap(finalPixels, w, h, Bitmap.Config.ARGB_8888); 
} 
+0

謝謝亞歷克斯。我將bb更改爲BYteBuffer.allocateDirect(screenshotSize * 4),它現在運行該進程而不會崩潰,但生成的PNG爲BLACK。 – user1540142

+0

嗨亞歷克斯我添加了您的更改和代碼運行順利,但生成的圖像仍然是黑色。是否因爲glReadPixels的前綴是GLES20?如何檢查ReadPixels是否具有我的屏幕圖像? – user1540142

+0

這個工作後,我相匹配的EGLConfigChooser到8888 – user1540142

1

我覺得ByteBuffer bb = ByteBuffer.allocateDirect(screenshotSize * 3);是錯誤的。 它應該是ByteBuffer bb = ByteBuffer.allocateDirect(screenshotSize * 4);

+0

THanks Shashika。將其更改爲* 4現在可以保存PNG而不會崩潰,但它是黑色的。 – user1540142

+0

我會建議兩個更改。 GLES20.glReadPixels(0,0,w,h,GLES20.GL_RGBA,GLES20.GL_UNSIGNED_BYTE,bb);和位圖位圖=位圖。createBitmap(w,h,Bitmap.Config.ARGB_8888); – Shashika

+0

這裏一定是錯的。如果我做了這兩個更改,我會得到「堆棧損壞檢測中止」。謝謝你的幫助。 – user1540142

2

從MPEG文件中提取幀的示例代碼可以在on bigflake找到。視頻幀以GL呈現,saveFrame()功能使用glReadPixels()Bitmap將其作爲PNG保存到磁盤。

有一些相當重要的性能缺陷,你需要知道;請參閱saveFrame()中有關如何最好地使用NIO的說明。您還需要確保您的EGLConfig符合您的像素讀取格式(如果源格式和目標格式不相同,則glReadPixels()可能會變得非常慢)。

更新:現在在Grafika中有一個稍微更普遍的實現 - 請參見EglSurfaceBase#saveFrame()