Add sRGB eotf to overlay fragment shader.

The eotf is needed so that overlay (image) colors are correctly interpreted and mixed the linear video colors.

Also replaces the 100winners.png with "homemade" image file.

Added GlEffectsFrameProcessor test to justify that the color looks correct at the end of frame processing.

PiperOrigin-RevId: 506290309
This commit is contained in:
tofunmi 2023-02-01 13:40:35 +00:00 committed by christosts
parent eb8fffba15
commit eea1868d54
14 changed files with 52 additions and 5 deletions

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.effect;
import static com.google.android.exoplayer2.effect.OverlayTextureProcessorPixelTest.OVERLAY_PNG_ASSET_PATH;
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE;
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceArgb8888;
import static com.google.android.exoplayer2.testutil.BitmapPixelTestUtil.readBitmap;
@ -34,6 +35,7 @@ import com.google.android.exoplayer2.util.Size;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -52,6 +54,9 @@ import org.junit.runner.RunWith;
public final class GlEffectsFrameProcessorPixelTest {
public static final String ORIGINAL_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/electrical_colors/original.png";
// This file is generated on a Pixel 7, because the emulator isn't able to generate this file.
public static final String BITMAP_OVERLAY_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/electrical_colors/overlay_bitmap_FrameProcessor.png";
public static final String SCALE_WIDE_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/electrical_colors/scale_wide.png";
public static final String TRANSLATE_RIGHT_PNG_ASSET_PATH =
@ -169,6 +174,26 @@ public final class GlEffectsFrameProcessorPixelTest {
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
}
@Test
@Ignore("b/267031388 Test can only run on physical devices because emulator cannot produce image")
public void bitmapOverlay_matchesGoldenFile() throws Exception {
String testId = "bitmapOverlay_matchesGoldenFile";
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(overlayBitmap);
frameProcessorTestRunner =
getDefaultFrameProcessorTestRunnerBuilder(testId)
.setEffects(new OverlayEffect(ImmutableList.of(bitmapOverlay)))
.build();
Bitmap expectedBitmap = readBitmap(BITMAP_OVERLAY_PNG_ASSET_PATH);
Bitmap actualBitmap = frameProcessorTestRunner.processFirstFrameAndEnd();
// TODO(b/207848601): switch to using proper tooling for testing against golden data.
float averagePixelAbsoluteDifference =
getBitmapAveragePixelAbsoluteDifferenceArgb8888(expectedBitmap, actualBitmap, testId);
assertThat(averagePixelAbsoluteDifference).isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE);
}
@Test
public void scaleToFitAndMatrixTransformation_matchesGoldenFile() throws Exception {
String testId = "scaleToFitAndMatrixTransformation_matchesGoldenFile";

View File

@ -58,7 +58,7 @@ import org.junit.runner.RunWith;
*/
@RunWith(AndroidJUnit4.class)
public class OverlayTextureProcessorPixelTest {
public static final String OVERLAY_PNG_ASSET_PATH = "media/bitmap/overlay/100winners.png";
public static final String OVERLAY_PNG_ASSET_PATH = "media/bitmap/overlay/media3test.png";
public static final String ORIGINAL_PNG_ASSET_PATH =
"media/bitmap/sample_mp4_first_frame/electrical_colors/original.png";
public static final String OVERLAY_BITMAP_DEFAULT =
@ -132,9 +132,9 @@ public class OverlayTextureProcessorPixelTest {
public void drawFrame_bitmapOverlay_blendsBitmapIntoFrame() throws Exception {
String testId = "drawFrame_bitmapOverlay";
Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH);
BitmapOverlay scaledBitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(overlayBitmap);
BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(overlayBitmap);
overlayTextureProcessor =
new OverlayEffect(ImmutableList.of(scaledBitmapOverlay))
new OverlayEffect(ImmutableList.of(bitmapOverlay))
.toGlTextureProcessor(context, /* useHdr= */ false);
Size outputSize = overlayTextureProcessor.configure(inputWidth, inputHeight);
setupOutputTexture(outputSize.getWidth(), outputSize.getHeight());

View File

@ -234,6 +234,21 @@ import com.google.common.collect.ImmutableList;
.append(" } else {\n")
.append(" return clamp(overlayAlpha/videoAlpha, 0.0, 1.0);\n")
.append(" }\n")
.append("}\n")
.append("")
.append("float srgbEotfSingleChannel(float srgb) {\n")
.append(" return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);\n")
.append("}\n")
.append("// sRGB EOTF.\n")
.append("vec3 applyEotf(const vec3 srgb) {\n")
.append("// Reference implementation:\n")
.append(
"// https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa;l=235\n")
.append(" return vec3(\n")
.append(" srgbEotfSingleChannel(srgb.r),\n")
.append(" srgbEotfSingleChannel(srgb.g),\n")
.append(" srgbEotfSingleChannel(srgb.b)\n")
.append(" );\n")
.append("}\n");
for (int texUnitIndex = 1; texUnitIndex <= numOverlays; texUnitIndex++) {
@ -253,15 +268,22 @@ import com.google.common.collect.ImmutableList;
shader
.append(
Util.formatInvariant(
" vec4 overlayColor%d = getClampToBorderOverlayColor(\n", texUnitIndex))
" vec4 electricalOverlayColor%d = getClampToBorderOverlayColor(\n",
texUnitIndex))
.append(
Util.formatInvariant(
" uOverlayTexSampler%d, vOverlayTexSamplingCoord%d, uOverlayAlpha%d);\n",
texUnitIndex, texUnitIndex, texUnitIndex))
.append(Util.formatInvariant(" vec4 opticalOverlayColor%d = vec4(\n", texUnitIndex))
.append(
Util.formatInvariant(
" applyEotf(electricalOverlayColor%d.rgb), electricalOverlayColor%d.a);\n",
texUnitIndex, texUnitIndex))
.append(" fragColor = mix(\n")
.append(
Util.formatInvariant(
" fragColor, overlayColor%d, getMixAlpha(videoColor.a, overlayColor%d.a));\n",
" fragColor, opticalOverlayColor%d, getMixAlpha(videoColor.a,"
+ " opticalOverlayColor%d.a));\n",
texUnitIndex, texUnitIndex));
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 545 KiB

After

Width:  |  Height:  |  Size: 516 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 KiB

After

Width:  |  Height:  |  Size: 500 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 KiB

After

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 533 KiB

After

Width:  |  Height:  |  Size: 529 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 KiB

After

Width:  |  Height:  |  Size: 542 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 KiB

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 KiB

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 KiB

After

Width:  |  Height:  |  Size: 535 KiB