From dc9854cc5b1fdd73cd06a0732b7cc90ee79f897e Mon Sep 17 00:00:00 2001 From: dancho Date: Fri, 30 Aug 2024 01:11:10 -0700 Subject: [PATCH] Add GlRect helper class Adds a class that represents an image rectangle in OpenGL coordinate convention. android.graphics.Rect puts (0, 0) as the top-left corner: it enforces `Rect.top <= Rect.bottom` and this matches `android.graphics.Bitmap` coordinates: docs https://developer.android.com/reference/android/graphics/Rect This is different from OpenGL coordinates where (0, 0) is at the bottom-left corner. I.e. GlRect.bottom <= GlRect.top: docs https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glReadPixels.xhtml The reason for this change is to allow a public API GlRect getScaledRegion() which selects a region of pixels of a GL texture to be copied to CPU memory. PiperOrigin-RevId: 669231826 --- .../androidx/media3/common/util/GlRect.java | 48 +++++++++++++++++++ .../androidx/media3/common/util/GlUtil.java | 11 ++--- .../media3/effect/ByteBufferGlEffectTest.java | 10 ++-- .../effect/ByteBufferConcurrentEffect.java | 5 +- .../media3/effect/ByteBufferGlEffect.java | 6 +-- .../media3/effect/QueuingGlShaderProgram.java | 7 ++- 6 files changed, 63 insertions(+), 24 deletions(-) create mode 100644 libraries/common/src/main/java/androidx/media3/common/util/GlRect.java diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlRect.java b/libraries/common/src/main/java/androidx/media3/common/util/GlRect.java new file mode 100644 index 0000000000..4b2f881742 --- /dev/null +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlRect.java @@ -0,0 +1,48 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.common.util; + +import static androidx.media3.common.util.Assertions.checkArgument; + +/** + * Represents a rectangle by the coordinates of its 4 edges (left, bottom, right, top). + * + *

Note that the right and top coordinates are exclusive. + * + *

This class represents coordinates in the OpenGL coordinate convention: {@code left <= right} + * and {@code bottom <= top}. + */ +@UnstableApi +public final class GlRect { + public int left; + public int bottom; + public int right; + public int top; + + /** Creates an instance from (0, 0) to the specified width and height. */ + public GlRect(int width, int height) { + this(/* left= */ 0, /* bottom= */ 0, width, height); + } + + /** Creates an instance. */ + public GlRect(int left, int bottom, int right, int top) { + checkArgument(left <= right && bottom <= top); + this.left = left; + this.bottom = bottom; + this.right = right; + this.top = top; + } +} diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java index f9f43c52b6..948f3342a8 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java @@ -23,7 +23,6 @@ import static androidx.media3.common.util.Assertions.checkState; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Rect; import android.opengl.EGL14; import android.opengl.EGLConfig; import android.opengl.EGLContext; @@ -837,7 +836,7 @@ public final class GlUtil { * @param drawFboId The framebuffer object to draw into. * @param drawRect The rectangular region of {@code drawFboId} to draw into. */ - public static void blitFrameBuffer(int readFboId, Rect readRect, int drawFboId, Rect drawRect) + public static void blitFrameBuffer(int readFboId, GlRect readRect, int drawFboId, GlRect drawRect) throws GlException { int[] boundFramebuffer = new int[1]; GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, boundFramebuffer, /* offset= */ 0); @@ -848,13 +847,13 @@ public final class GlUtil { checkGlError(); GLES30.glBlitFramebuffer( readRect.left, - readRect.top, - readRect.right, readRect.bottom, + readRect.right, + readRect.top, drawRect.left, - drawRect.top, - drawRect.right, drawRect.bottom, + drawRect.right, + drawRect.top, GLES30.GL_COLOR_BUFFER_BIT, GLES30.GL_LINEAR); checkGlError(); diff --git a/libraries/effect/src/androidTest/java/androidx/media3/effect/ByteBufferGlEffectTest.java b/libraries/effect/src/androidTest/java/androidx/media3/effect/ByteBufferGlEffectTest.java index 40d58c593b..d8857e09a3 100644 --- a/libraries/effect/src/androidTest/java/androidx/media3/effect/ByteBufferGlEffectTest.java +++ b/libraries/effect/src/androidTest/java/androidx/media3/effect/ByteBufferGlEffectTest.java @@ -26,7 +26,6 @@ import static com.google.common.truth.Truth.assertThat; import android.graphics.Bitmap; import android.graphics.Color; -import android.graphics.Rect; import android.text.Spannable; import android.text.SpannableString; import android.text.style.AbsoluteSizeSpan; @@ -34,6 +33,7 @@ import android.text.style.ForegroundColorSpan; import android.text.style.TypefaceSpan; import androidx.media3.common.GlTextureInfo; import androidx.media3.common.util.Consumer; +import androidx.media3.common.util.GlRect; import androidx.media3.common.util.Size; import androidx.media3.common.util.Util; import androidx.media3.test.utils.TextureBitmapReader; @@ -140,12 +140,8 @@ public class ByteBufferGlEffectTest { } @Override - public Rect getScaledRegion(long presentationTimeUs) { - return new Rect( - /* left= */ 0, - /* top= */ 0, - /* right= */ INPUT_FRAME_WIDTH, - /* bottom= */ INPUT_FRAME_HEIGHT); + public GlRect getScaledRegion(long presentationTimeUs) { + return new GlRect(INPUT_FRAME_WIDTH, INPUT_FRAME_HEIGHT); } @Override diff --git a/libraries/effect/src/main/java/androidx/media3/effect/ByteBufferConcurrentEffect.java b/libraries/effect/src/main/java/androidx/media3/effect/ByteBufferConcurrentEffect.java index b3cc18ddb5..d3b6f8e0bb 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/ByteBufferConcurrentEffect.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/ByteBufferConcurrentEffect.java @@ -19,11 +19,11 @@ import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static com.google.common.util.concurrent.Futures.immediateFailedFuture; -import android.graphics.Rect; import androidx.media3.common.C; import androidx.media3.common.GlObjectsProvider; import androidx.media3.common.GlTextureInfo; import androidx.media3.common.VideoFrameProcessingException; +import androidx.media3.common.util.GlRect; import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.Size; import androidx.media3.common.util.Util; @@ -107,8 +107,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; textureInfo.fboId, processor.getScaledRegion(presentationTimeUs), effectInputTexture.fboId, - new Rect( - /* left= */ 0, /* top= */ 0, effectInputTexture.width, effectInputTexture.height)); + new GlRect(effectInputTexture.width, effectInputTexture.height)); TexturePixelBuffer texturePixelBuffer = new TexturePixelBuffer(effectInputTexture); unmappedPixelBuffers.add(texturePixelBuffer); diff --git a/libraries/effect/src/main/java/androidx/media3/effect/ByteBufferGlEffect.java b/libraries/effect/src/main/java/androidx/media3/effect/ByteBufferGlEffect.java index 00d50349fc..e8e7d411ba 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/ByteBufferGlEffect.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/ByteBufferGlEffect.java @@ -20,10 +20,10 @@ import static androidx.media3.common.util.Assertions.checkArgument; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Matrix; -import android.graphics.Rect; import android.opengl.GLES20; import androidx.media3.common.GlTextureInfo; import androidx.media3.common.VideoFrameProcessingException; +import androidx.media3.common.util.GlRect; import androidx.media3.common.util.Size; import androidx.media3.common.util.UnstableApi; import com.google.common.util.concurrent.ListenableFuture; @@ -143,9 +143,7 @@ import java.util.concurrent.Future; * @return The rectangular region of the input image that will be scaled to fill the effect * input image. */ - // TODO: b/b/361286064 - This method misuses android.graphics.Rect for OpenGL coordinates. - // Implement a custom GlUtils.Rect to correctly label lower left corner as (0, 0). - Rect getScaledRegion(long presentationTimeUs); + GlRect getScaledRegion(long presentationTimeUs); /** * Processing the image data in the {@code image}. diff --git a/libraries/effect/src/main/java/androidx/media3/effect/QueuingGlShaderProgram.java b/libraries/effect/src/main/java/androidx/media3/effect/QueuingGlShaderProgram.java index 242ad1a3ac..e01bcde03f 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/QueuingGlShaderProgram.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/QueuingGlShaderProgram.java @@ -18,13 +18,13 @@ package androidx.media3.effect; import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkState; -import android.graphics.Rect; import androidx.annotation.CallSuper; import androidx.annotation.IntRange; import androidx.media3.common.C; import androidx.media3.common.GlObjectsProvider; import androidx.media3.common.GlTextureInfo; import androidx.media3.common.VideoFrameProcessingException; +import androidx.media3.common.util.GlRect; import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.UnstableApi; import com.google.common.util.concurrent.Futures; @@ -213,10 +213,9 @@ import java.util.concurrent.TimeUnit; checkState(inputTexture.fboId != C.INDEX_UNSET); GlUtil.blitFrameBuffer( inputTexture.fboId, - new Rect(/* left= */ 0, /* top= */ 0, /* right= */ inputWidth, /* bottom= */ inputHeight), + new GlRect(inputWidth, inputHeight), outputTexture.fboId, - new Rect( - /* left= */ 0, /* top= */ 0, /* right= */ inputWidth, /* bottom= */ inputHeight)); + new GlRect(inputWidth, inputHeight)); Future task = concurrentEffect.queueInputFrame(glObjectsProvider, outputTexture, presentationTimeUs);