GlSurfaceViewRenderer: Capture graph output texture

Captures the original graph output texture, not what has been copied to the screen. This will be important for zooming to prevent top/bottom letterboxes, and it preserves the original quality, so that high resolution images can be used.

PiperOrigin-RevId: 570604422
This commit is contained in:
MediaPipe Team 2023-10-04 00:03:42 -07:00 committed by Copybara-Service
parent da8fcb6bb2
commit 9bb042cc86

View File

@ -21,6 +21,7 @@ import android.graphics.Bitmap;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext; import android.opengl.GLES11Ext;
import android.opengl.GLES20; import android.opengl.GLES20;
import android.opengl.GLES31;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
import android.opengl.Matrix; import android.opengl.Matrix;
import android.util.Log; import android.util.Log;
@ -204,11 +205,36 @@ public class GlSurfaceViewRenderer implements GLSurfaceView.Renderer {
// Capture Bitmap if requested. // Capture Bitmap if requested.
BitmapCaptureListener bitmapCaptureListener = this.bitmapCaptureListener; BitmapCaptureListener bitmapCaptureListener = this.bitmapCaptureListener;
if (captureNextFrameBitmap.getAndSet(false) && bitmapCaptureListener != null) { if (captureNextFrameBitmap.getAndSet(false) && bitmapCaptureListener != null) {
int bitmapSize = surfaceWidth * surfaceHeight; // Find the name of the bound texture.
int[] texName = new int[1];
if (surfaceTexture != null) {
GLES20.glGetIntegerv(GLES11Ext.GL_TEXTURE_BINDING_EXTERNAL_OES, texName, 0);
} else {
texName[0] = frame.getTextureName();
}
// Find texture size and create byte buffer large enough to hold an RGBA image.
int[] texDims = new int[2];
GLES31.glGetTexLevelParameteriv(textureTarget, 0, GLES31.GL_TEXTURE_WIDTH, texDims, 0);
GLES31.glGetTexLevelParameteriv(textureTarget, 0, GLES31.GL_TEXTURE_HEIGHT, texDims, 1);
int texWidth = texDims[0];
int texHeight = texDims[1];
int bitmapSize = texWidth * texHeight;
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bitmapSize * 4); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bitmapSize * 4);
byteBuffer.order(ByteOrder.nativeOrder()); byteBuffer.order(ByteOrder.nativeOrder());
// Read pixels from texture.
int[] fbo = new int[1];
GLES20.glGenFramebuffers(1, fbo, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbo[0]);
GLES20.glFramebufferTexture2D(
GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, texName[0], 0);
GLES20.glReadPixels( GLES20.glReadPixels(
0, 0, surfaceWidth, surfaceHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuffer); 0, 0, texWidth, texHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuffer);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glDeleteFramebuffers(1, fbo, 0);
ShaderUtil.checkGlError("capture frame");
int[] pixelBuffer = new int[bitmapSize]; int[] pixelBuffer = new int[bitmapSize];
byteBuffer.asIntBuffer().get(pixelBuffer); byteBuffer.asIntBuffer().get(pixelBuffer);
for (int i = 0; i < bitmapSize; i++) { for (int i = 0; i < bitmapSize; i++) {
@ -218,10 +244,17 @@ public class GlSurfaceViewRenderer implements GLSurfaceView.Renderer {
| ((pixelBuffer[i] & 0x000000ff) << 16) | ((pixelBuffer[i] & 0x000000ff) << 16)
| ((pixelBuffer[i] & 0x00ff0000) >> 16); | ((pixelBuffer[i] & 0x00ff0000) >> 16);
} }
Bitmap bitmap = Bitmap.createBitmap(surfaceWidth, surfaceHeight, Bitmap.Config.ARGB_8888);
// Send bitmap.
Bitmap bitmap = Bitmap.createBitmap(texWidth, texHeight, Bitmap.Config.ARGB_8888);
bitmap.setPixels( bitmap.setPixels(
pixelBuffer, /* offset= */bitmapSize - surfaceWidth, /* stride= */-surfaceWidth, pixelBuffer,
/* x= */0, /* y= */0, surfaceWidth, surfaceHeight); /* offset= */ bitmapSize - texWidth,
/* stride= */ -texWidth,
/* x= */ 0,
/* y= */ 0,
texWidth,
texHeight);
bitmapCaptureListener.onBitmapCaptured(bitmap); bitmapCaptureListener.onBitmapCaptured(bitmap);
} }