diff --git a/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/OverlayTextureProcessorPixelTest.java b/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/OverlayTextureProcessorPixelTest.java index 9d2618c22a..faff13af3c 100644 --- a/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/OverlayTextureProcessorPixelTest.java +++ b/library/effect/src/androidTest/java/com/google/android/exoplayer2/effect/OverlayTextureProcessorPixelTest.java @@ -64,6 +64,8 @@ public class OverlayTextureProcessorPixelTest { "media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_default.png"; public static final String OVERLAY_BITMAP_SCALED = "media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_scaled.png"; + public static final String OVERLAY_BITMAP_TRANSLUCENT = + "media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_translucent.png"; public static final String OVERLAY_TEXT_DEFAULT = "media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_text_default.png"; public static final String OVERLAY_TEXT_TRANSLATE = @@ -167,6 +169,59 @@ public class OverlayTextureProcessorPixelTest { assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); } + @Test + public void drawFrame_translucentBitmapOverlay_blendsBitmapIntoFrame() throws Exception { + String testId = "drawFrame_translucentBitmapOverlay"; + Bitmap bitmap = readBitmap(OVERLAY_PNG_ASSET_PATH); + OverlaySettings overlaySettings = new OverlaySettings.Builder().setAlpha(0.5f).build(); + BitmapOverlay translucentBitmapOverlay = + BitmapOverlay.createStaticBitmapOverlay(bitmap, overlaySettings); + overlayTextureProcessor = + new OverlayEffect(ImmutableList.of(translucentBitmapOverlay)) + .toGlTextureProcessor(context, false); + Pair outputSize = overlayTextureProcessor.configure(inputWidth, inputHeight); + setupOutputTexture(outputSize.first, outputSize.second); + Bitmap expectedBitmap = readBitmap(OVERLAY_BITMAP_TRANSLUCENT); + + overlayTextureProcessor.drawFrame(inputTexId, /* presentationTimeUs= */ 0); + Bitmap actualBitmap = + createArgb8888BitmapFromCurrentGlFramebuffer(outputSize.first, outputSize.second); + + maybeSaveTestBitmapToCacheDirectory(testId, /* bitmapLabel= */ "actual", actualBitmap); + float averagePixelAbsoluteDifference = + getBitmapAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, actualBitmap, testId); + assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); + } + + @Test + public void drawFrame_transparentTextOverlay_blendsBitmapIntoFrame() throws Exception { + String testId = "drawFrame_transparentTextOverlay"; + SpannableString overlayText = new SpannableString(/* source= */ "Text styling"); + OverlaySettings overlaySettings = new OverlaySettings.Builder().setAlpha(0f).build(); + overlayText.setSpan( + new ForegroundColorSpan(Color.GRAY), + /* start= */ 0, + /* end= */ 4, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + TextOverlay staticTextOverlay = + TextOverlay.createStaticTextOverlay(overlayText, overlaySettings); + overlayTextureProcessor = + new OverlayEffect(ImmutableList.of(staticTextOverlay)) + .toGlTextureProcessor(context, /* useHdr= */ false); + Pair outputSize = overlayTextureProcessor.configure(inputWidth, inputHeight); + setupOutputTexture(outputSize.first, outputSize.second); + Bitmap expectedBitmap = readBitmap(ORIGINAL_PNG_ASSET_PATH); + + overlayTextureProcessor.drawFrame(inputTexId, /* presentationTimeUs= */ 0); + Bitmap actualBitmap = + createArgb8888BitmapFromCurrentGlFramebuffer(outputSize.first, outputSize.second); + + maybeSaveTestBitmapToCacheDirectory(testId, /* bitmapLabel= */ "actual", actualBitmap); + float averagePixelAbsoluteDifference = + getBitmapAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, actualBitmap, testId); + assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE); + } + @Test public void drawFrame_textOverlay_blendsTextIntoFrame() throws Exception { String testId = "drawFrame_textOverlay"; diff --git a/library/effect/src/main/assets/shaders/fragment_shader_overlay_es2.glsl b/library/effect/src/main/assets/shaders/fragment_shader_overlay_es2.glsl index 436edb4555..9723f70e83 100644 --- a/library/effect/src/main/assets/shaders/fragment_shader_overlay_es2.glsl +++ b/library/effect/src/main/assets/shaders/fragment_shader_overlay_es2.glsl @@ -20,6 +20,9 @@ precision mediump float; uniform sampler2D uVideoTexSampler0; // Texture containing the overlay bitmap. uniform sampler2D uOverlayTexSampler1; +// The alpha values for the texture. +uniform float uOverlayAlpha1; + varying vec2 vVideoTexSamplingCoord; varying vec2 vOverlayTexSamplingCoord1; @@ -27,15 +30,18 @@ varying vec2 vOverlayTexSamplingCoord1; // (https://open.gl/textures) since it's not implemented until OpenGL ES 3.2. vec4 getClampToBorderOverlayColor() { if (vOverlayTexSamplingCoord1.x > 1.0 || vOverlayTexSamplingCoord1.x < 0.0 - || vOverlayTexSamplingCoord1.y > 1.0 || vOverlayTexSamplingCoord1.y < 0.0){ + || vOverlayTexSamplingCoord1.y > 1.0 || vOverlayTexSamplingCoord1.y < 0.0) { return vec4(0.0, 0.0, 0.0, 0.0); } else { - return vec4(texture2D(uOverlayTexSampler1, vOverlayTexSamplingCoord1)); + vec4 overlayColor = vec4( + texture2D(uOverlayTexSampler1, vOverlayTexSamplingCoord1)); + overlayColor.a = uOverlayAlpha1 * overlayColor.a; + return overlayColor; } } float getMixAlpha(float videoAlpha, float overlayAlpha) { - if (videoAlpha == 0.0){ + if (videoAlpha == 0.0) { return 1.0; } else { return clamp(overlayAlpha/videoAlpha, 0.0, 1.0); diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/OverlaySettings.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/OverlaySettings.java index bbc0bef93e..92aec3db8c 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/OverlaySettings.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/OverlaySettings.java @@ -14,22 +14,28 @@ package com.google.android.exoplayer2.effect; * See the License for the specific language governing permissions and * limitations under the License. */ +import static com.google.android.exoplayer2.util.Assertions.checkArgument; + +import androidx.annotation.FloatRange; import com.google.android.exoplayer2.util.GlUtil; import com.google.errorprone.annotations.CanIgnoreReturnValue; /** Contains information to control how an {@link TextureOverlay} is displayed on the screen. */ public final class OverlaySettings { public final boolean useHdr; + public final float alpha; public final float[] matrix; - private OverlaySettings(boolean useHdr, float[] matrix) { + private OverlaySettings(boolean useHdr, float alpha, float[] matrix) { this.useHdr = useHdr; + this.alpha = alpha; this.matrix = matrix; } /** A builder for {@link OverlaySettings} instances. */ public static final class Builder { private boolean useHdr; + private float alpha = 1; private float[] matrix; /** Creates a new {@link Builder}. */ @@ -61,9 +67,23 @@ public final class OverlaySettings { return this; } + /** + * Sets the alpha value of the overlay, altering its transparency. + * + *

Alpha values range from 0 (all transparent) to 1 (completely opaque). + * + *

Set to always return {@code 1} by default. + */ + @CanIgnoreReturnValue + public Builder setAlpha(@FloatRange(from = 0, to = 1) float alpha) { + checkArgument(0 <= alpha && alpha <= 1, "Alpha needs to be in the interval [0, 1]."); + this.alpha = alpha; + return this; + } + /** Creates an instance of {@link OverlaySettings}, using defaults if values are unset. */ public OverlaySettings build() { - return new OverlaySettings(useHdr, matrix); + return new OverlaySettings(useHdr, alpha, matrix); } } } diff --git a/library/effect/src/main/java/com/google/android/exoplayer2/effect/OverlayTextureProcessor.java b/library/effect/src/main/java/com/google/android/exoplayer2/effect/OverlayTextureProcessor.java index eaa38d4b6a..6bd31b0d82 100644 --- a/library/effect/src/main/java/com/google/android/exoplayer2/effect/OverlayTextureProcessor.java +++ b/library/effect/src/main/java/com/google/android/exoplayer2/effect/OverlayTextureProcessor.java @@ -99,13 +99,14 @@ import java.io.IOException; videoHeight / (float) overlayTextureSize.second, /* z= */ 1); glProgram.setFloatsUniform("uAspectRatioMatrix", aspectRatioMatrix); - Matrix.invertM( overlayMatrix, MATRIX_OFFSET, overlay.getOverlaySettings(presentationTimeUs).matrix, MATRIX_OFFSET); glProgram.setFloatsUniform("uOverlayMatrix", overlayMatrix); + glProgram.setFloatUniform( + "uOverlayAlpha1", overlay.getOverlaySettings(presentationTimeUs).alpha); } else { glProgram.setSamplerTexIdUniform( diff --git a/testdata/src/test/assets/media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_translucent.png b/testdata/src/test/assets/media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_translucent.png new file mode 100644 index 0000000000..0a8490d988 Binary files /dev/null and b/testdata/src/test/assets/media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_translucent.png differ