support hdr text overlays

adds luminance multiplier to allow the luminance (i.e. brightness) over a text overlay to be scaled

PiperOrigin-RevId: 643047928
This commit is contained in:
tofunmi 2024-06-13 10:38:07 -07:00 committed by Copybara-Service
parent 81f15dbd37
commit 174c49313c
6 changed files with 250 additions and 51 deletions

View File

@ -25,6 +25,8 @@
* Effect:
* Remove unused `OverlaySettings.useHdr` since dynamic range of overlay
and frame must match.
* Add HDR support for `TextOverlay`. Luminance of the text overlay can be
adjusted with `OverlaySettings.setHdrLuminanceMultiplier`.
* Muxers:
* IMA extension:
* Session:

View File

@ -36,6 +36,7 @@ public final class OverlaySettings {
private Pair<Float, Float> overlayFrameAnchor;
private Pair<Float, Float> scale;
private float rotationDegrees;
private float hdrLuminanceMultiplier;
/** Creates a new {@link Builder}. */
public Builder() {
@ -44,6 +45,7 @@ public final class OverlaySettings {
overlayFrameAnchor = Pair.create(0f, 0f);
scale = Pair.create(1f, 1f);
rotationDegrees = 0f;
hdrLuminanceMultiplier = 1f;
}
private Builder(OverlaySettings overlaySettings) {
@ -140,10 +142,31 @@ public final class OverlaySettings {
return this;
}
/**
* Set the luminance multiplier of an SDR overlay when overlaid on a HDR frame.
*
* <p>Scales the luminance of the overlay to adjust the output brightness of the overlay on the
* frame. The default value is 1, which scales the overlay colors into the standard HDR
* luminance within the processing pipeline. Use 0.5 to scale the luminance of the overlay to
* SDR range, so that no extra luminance is added.
*
* <p>Currently only supported on text overlays
*/
@CanIgnoreReturnValue
public Builder setHdrLuminanceMultiplier(float hdrLuminanceMultiplier) {
this.hdrLuminanceMultiplier = hdrLuminanceMultiplier;
return this;
}
/** Creates an instance of {@link OverlaySettings}, using defaults if values are unset. */
public OverlaySettings build() {
return new OverlaySettings(
alphaScale, backgroundFrameAnchor, overlayFrameAnchor, scale, rotationDegrees);
alphaScale,
backgroundFrameAnchor,
overlayFrameAnchor,
scale,
rotationDegrees,
hdrLuminanceMultiplier);
}
}
@ -162,17 +185,22 @@ public final class OverlaySettings {
/** The rotation of the overlay, counter-clockwise. */
public final float rotationDegrees;
/** The luminance multiplier of an SDR overlay when overlaid on a HDR frame. */
public final float hdrLuminanceMultiplier;
private OverlaySettings(
float alphaScale,
Pair<Float, Float> backgroundFrameAnchor,
Pair<Float, Float> overlayFrameAnchor,
Pair<Float, Float> scale,
float rotationDegrees) {
float rotationDegrees,
float hdrLuminanceMultiplier) {
this.alphaScale = alphaScale;
this.backgroundFrameAnchor = backgroundFrameAnchor;
this.overlayFrameAnchor = overlayFrameAnchor;
this.scale = scale;
this.rotationDegrees = rotationDegrees;
this.hdrLuminanceMultiplier = hdrLuminanceMultiplier;
}
/** Returns a new {@link Builder} initialized with the values of this instance. */

View File

@ -17,6 +17,7 @@ package androidx.media3.effect;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Util.formatInvariant;
import static androidx.media3.common.util.Util.loadAsset;
@ -25,6 +26,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Gainmap;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.SparseArray;
import android.util.SparseIntArray;
import androidx.annotation.Nullable;
@ -40,6 +42,14 @@ import java.io.IOException;
/** Applies zero or more {@link TextureOverlay}s onto each frame. */
/* package */ final class OverlayShaderProgram extends BaseGlShaderProgram {
/** Types of HDR overlay. */
private static final int HDR_TYPE_ULTRA_HDR = 1;
private static final int HDR_TYPE_TEXT = 2;
// The maximum number of samplers allowed in a single GL program is 16.
// We use one for every overlay and one for the video.
private static final int MAX_OVERLAY_SAMPLERS = 15;
private static final String ULTRA_HDR_INSERT = "shaders/insert_ultra_hdr.glsl";
private static final String FRAGMENT_SHADER_METHODS_INSERT =
"shaders/insert_overlay_fragment_shader_methods.glsl";
@ -48,7 +58,8 @@ import java.io.IOException;
private final GlProgram glProgram;
private final SamplerOverlayMatrixProvider samplerOverlayMatrixProvider;
private final ImmutableList<TextureOverlay> overlays;
private final boolean useHdr;
@Nullable private final int[] hdrTypes;
private final SparseArray<Gainmap> lastGainmaps;
private final SparseIntArray gainmapTexIds;
@ -67,20 +78,14 @@ import java.io.IOException;
throws VideoFrameProcessingException {
super(/* useHighPrecisionColorComponents= */ useHdr, /* texturePoolCapacity= */ 1);
if (useHdr) {
// Each UltraHDR overlay uses an extra texture to apply the gainmap to the base in the shader.
checkArgument(
overlays.size() <= 7,
"OverlayShaderProgram does not support more than 7 HDR overlays in the same instance.");
checkArgument(Util.SDK_INT >= 34);
hdrTypes = findHdrTypes(overlays);
} else {
// The maximum number of samplers allowed in a single GL program is 16.
// We use one for every overlay and one for the video.
hdrTypes = null;
checkArgument(
overlays.size() <= 15,
overlays.size() <= MAX_OVERLAY_SAMPLERS,
"OverlayShaderProgram does not support more than 15 SDR overlays in the same instance.");
}
this.useHdr = useHdr;
this.overlays = overlays;
this.samplerOverlayMatrixProvider = new SamplerOverlayMatrixProvider();
lastGainmaps = new SparseArray<>();
@ -89,7 +94,7 @@ import java.io.IOException;
glProgram =
new GlProgram(
createVertexShader(overlays.size()),
createFragmentShader(context, overlays.size(), useHdr));
createFragmentShader(context, overlays.size(), hdrTypes));
} catch (GlUtil.GlException | IOException e) {
throw new VideoFrameProcessingException(e);
}
@ -119,23 +124,35 @@ import java.io.IOException;
for (int texUnitIndex = 1; texUnitIndex <= overlays.size(); texUnitIndex++) {
TextureOverlay overlay = overlays.get(texUnitIndex - 1);
if (useHdr) {
checkArgument(overlay instanceof BitmapOverlay);
Bitmap bitmap = ((BitmapOverlay) overlay).getBitmap(presentationTimeUs);
checkArgument(bitmap.hasGainmap());
Gainmap gainmap = checkNotNull(bitmap.getGainmap());
@Nullable Gainmap lastGainmap = lastGainmaps.get(texUnitIndex);
if (lastGainmap == null || !GainmapUtil.equals(lastGainmap, gainmap)) {
lastGainmaps.put(texUnitIndex, gainmap);
if (gainmapTexIds.get(texUnitIndex, /* valueIfKeyNotFound= */ C.INDEX_UNSET)
== C.INDEX_UNSET) {
gainmapTexIds.put(texUnitIndex, GlUtil.createTexture(gainmap.getGainmapContents()));
} else {
GlUtil.setTexture(gainmapTexIds.get(texUnitIndex), gainmap.getGainmapContents());
if (hdrTypes != null) {
if (hdrTypes[texUnitIndex - 1] == HDR_TYPE_ULTRA_HDR) {
checkArgument(overlay instanceof BitmapOverlay);
Bitmap bitmap = ((BitmapOverlay) overlay).getBitmap(presentationTimeUs);
checkArgument(bitmap.hasGainmap());
Gainmap gainmap = checkNotNull(bitmap.getGainmap());
@Nullable Gainmap lastGainmap = lastGainmaps.get(texUnitIndex);
if (lastGainmap == null || !GainmapUtil.equals(lastGainmap, gainmap)) {
lastGainmaps.put(texUnitIndex, gainmap);
if (gainmapTexIds.get(texUnitIndex, /* valueIfKeyNotFound= */ C.INDEX_UNSET)
== C.INDEX_UNSET) {
gainmapTexIds.put(texUnitIndex, GlUtil.createTexture(gainmap.getGainmapContents()));
} else {
GlUtil.setTexture(gainmapTexIds.get(texUnitIndex), gainmap.getGainmapContents());
}
glProgram.setSamplerTexIdUniform(
"uGainmapTexSampler" + texUnitIndex,
gainmapTexIds.get(texUnitIndex),
texUnitIndex);
GainmapUtil.setGainmapUniforms(
glProgram, lastGainmaps.get(texUnitIndex), texUnitIndex);
}
glProgram.setSamplerTexIdUniform(
"uGainmapTexSampler" + texUnitIndex, gainmapTexIds.get(texUnitIndex), texUnitIndex);
GainmapUtil.setGainmapUniforms(glProgram, lastGainmaps.get(texUnitIndex), texUnitIndex);
} else if (hdrTypes[texUnitIndex - 1] == HDR_TYPE_TEXT) {
float[] luminanceMatrix = GlUtil.create4x4IdentityMatrix();
float multiplier =
overlay.getOverlaySettings(presentationTimeUs).hdrLuminanceMultiplier;
Matrix.scaleM(luminanceMatrix, /* mOffset= */ 0, multiplier, multiplier, multiplier);
glProgram.setFloatsUniform(
formatInvariant("uLuminanceMatrix%d", texUnitIndex), luminanceMatrix);
}
}
@ -172,7 +189,7 @@ import java.io.IOException;
glProgram.delete();
for (int i = 0; i < overlays.size(); i++) {
overlays.get(i).release();
if (useHdr) {
if (hdrTypes != null && hdrTypes[i] == HDR_TYPE_ULTRA_HDR) {
int gainmapTexId = gainmapTexIds.get(i, /* valueIfKeyNotFound= */ C.INDEX_UNSET);
if (gainmapTexId != C.INDEX_UNSET) {
GlUtil.deleteTexture(gainmapTexId);
@ -184,6 +201,32 @@ import java.io.IOException;
}
}
private static int[] findHdrTypes(ImmutableList<TextureOverlay> overlays) {
int[] hdrTypes = new int[overlays.size()];
int overlaySamplersAvailable = MAX_OVERLAY_SAMPLERS;
for (int i = 0; i < overlays.size(); i++) {
TextureOverlay overlay = overlays.get(i);
if (overlay instanceof TextOverlay) {
// TextOverlay must be checked first since they extend BitmapOverlay.
hdrTypes[i] = HDR_TYPE_TEXT;
overlaySamplersAvailable -= 1;
} else if (overlay instanceof BitmapOverlay) {
checkState(Util.SDK_INT >= 34);
hdrTypes[i] = HDR_TYPE_ULTRA_HDR;
// Each UltraHDR overlay uses an extra texture to apply the gainmap to the base in the
// shader.
overlaySamplersAvailable -= 2;
} else {
throw new IllegalArgumentException(overlay + " is not supported on HDR content.");
}
if (overlaySamplersAvailable < 0) {
throw new IllegalArgumentException(
"Too many HDR overlays in the same OverlayShaderProgram instance.");
}
}
return hdrTypes;
}
private static String createVertexShader(int numOverlays) {
StringBuilder shader =
new StringBuilder()
@ -219,8 +262,8 @@ import java.io.IOException;
return shader.toString();
}
private static String createFragmentShader(Context context, int numOverlays, boolean useHdr)
throws IOException {
private static String createFragmentShader(
Context context, int numOverlays, @Nullable int[] hdrTypes) throws IOException {
StringBuilder shader =
new StringBuilder()
.append("#version 100\n")
@ -231,7 +274,7 @@ import java.io.IOException;
shader.append(loadAsset(context, FRAGMENT_SHADER_METHODS_INSERT));
if (useHdr) {
if (hdrTypes != null) {
shader.append(loadAsset(context, ULTRA_HDR_INSERT));
}
@ -241,21 +284,25 @@ import java.io.IOException;
.append(formatInvariant("uniform float uOverlayAlphaScale%d;\n", texUnitIndex))
.append(formatInvariant("varying vec2 vOverlayTexSamplingCoord%d;\n", texUnitIndex))
.append("\n");
if (useHdr) {
shader
.append("// Uniforms for applying the gainmap to the base.\n")
.append(formatInvariant("uniform sampler2D uGainmapTexSampler%d;\n", texUnitIndex))
.append(formatInvariant("uniform int uGainmapIsAlpha%d;\n", texUnitIndex))
.append(formatInvariant("uniform int uNoGamma%d;\n", texUnitIndex))
.append(formatInvariant("uniform int uSingleChannel%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uLogRatioMin%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uLogRatioMax%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uEpsilonSdr%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uEpsilonHdr%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uGainmapGamma%d;\n", texUnitIndex))
.append(formatInvariant("uniform float uDisplayRatioHdr%d;\n", texUnitIndex))
.append(formatInvariant("uniform float uDisplayRatioSdr%d;\n", texUnitIndex))
.append("\n");
if (hdrTypes != null) {
if (hdrTypes[texUnitIndex - 1] == HDR_TYPE_ULTRA_HDR) {
shader
.append("// Uniforms for applying the gainmap to the base.\n")
.append(formatInvariant("uniform sampler2D uGainmapTexSampler%d;\n", texUnitIndex))
.append(formatInvariant("uniform int uGainmapIsAlpha%d;\n", texUnitIndex))
.append(formatInvariant("uniform int uNoGamma%d;\n", texUnitIndex))
.append(formatInvariant("uniform int uSingleChannel%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uLogRatioMin%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uLogRatioMax%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uEpsilonSdr%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uEpsilonHdr%d;\n", texUnitIndex))
.append(formatInvariant("uniform vec4 uGainmapGamma%d;\n", texUnitIndex))
.append(formatInvariant("uniform float uDisplayRatioHdr%d;\n", texUnitIndex))
.append(formatInvariant("uniform float uDisplayRatioSdr%d;\n", texUnitIndex))
.append("\n");
} else if (hdrTypes[texUnitIndex - 1] == HDR_TYPE_TEXT) {
shader.append(formatInvariant("uniform mat4 uLuminanceMatrix%d;\n", texUnitIndex));
}
}
}
@ -276,12 +323,20 @@ import java.io.IOException;
+ " vec4 opticalBt2020OverlayColor% =\n"
+ " vec4(scaleHdrLuminance(bt709ToBt2020(opticalBt709Color%)),"
+ " electricalOverlayColor%.a);";
String luminanceApplicationTemplate =
"vec4 opticalOverlayColor% = uLuminanceMatrix% * srgbEotf(electricalOverlayColor%);\n";
for (int texUnitIndex = 1; texUnitIndex <= numOverlays; texUnitIndex++) {
shader.append(replaceFormatSpecifierWithIndex(eletricalColorTemplate, texUnitIndex));
String overlayMixColor = "electricalOverlayColor";
if (useHdr) {
shader.append(replaceFormatSpecifierWithIndex(gainmapApplicationTemplate, texUnitIndex));
overlayMixColor = "opticalBt2020OverlayColor";
if (hdrTypes != null) {
if (hdrTypes[texUnitIndex - 1] == HDR_TYPE_ULTRA_HDR) {
shader.append(replaceFormatSpecifierWithIndex(gainmapApplicationTemplate, texUnitIndex));
overlayMixColor = "opticalBt2020OverlayColor";
} else if (hdrTypes[texUnitIndex - 1] == HDR_TYPE_TEXT) {
shader.append(
replaceFormatSpecifierWithIndex(luminanceApplicationTemplate, texUnitIndex));
overlayMixColor = "opticalOverlayColor";
}
}
shader.append(
formatInvariant(

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 MiB

View File

@ -36,7 +36,11 @@ import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import androidx.media3.common.C;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.Effect;
@ -52,6 +56,8 @@ import androidx.media3.effect.DefaultVideoFrameProcessor;
import androidx.media3.effect.GaussianBlur;
import androidx.media3.effect.GlTextureProducer;
import androidx.media3.effect.OverlayEffect;
import androidx.media3.effect.OverlaySettings;
import androidx.media3.effect.TextOverlay;
import androidx.media3.test.utils.BitmapPixelTestUtil;
import androidx.media3.test.utils.TextureBitmapReader;
import androidx.media3.test.utils.VideoFrameProcessorTestRunner;
@ -64,6 +70,7 @@ import org.json.JSONException;
import org.junit.After;
import org.junit.AssumptionViolatedException;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@ -105,6 +112,11 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
"test-generated-goldens/hdr-goldens/ultrahdr_overlay_hlg.png";
private static final String ULTRA_HDR_OVERLAY_PQ_PNG_ASSET_PATH =
"test-generated-goldens/hdr-goldens/ultrahdr_overlay_pq.png";
private static final String ULTRA_HDR_AND_TEXT_OVERLAY_PNG_ASSET_PATH =
"test-generated-goldens/hdr-goldens/ultrahdr_and_text_overlay.png";
private static final String HDR_TEXT_OVERLAY_PNG_ASSET_PATH =
"test-generated-goldens/hdr-goldens/text_overlay.png";
/** Input SDR video of which we only use the first frame. */
private static final String INPUT_SDR_MP4_ASSET_STRING = "media/mp4/sample.mp4";
@ -115,6 +127,8 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
/** Input HLG video of which we only use the first frame. */
private static final String INPUT_HLG10_MP4_ASSET_STRING = "media/mp4/hlg-1080p.mp4";
public static final float HDR_PSNR_THRESHOLD = 43.5f;
@Rule public final TestName testName = new TestName();
private String testId;
@ -251,6 +265,60 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE);
}
@Test
@Ignore("TODO: b/344529901 - enable this test when fixed.")
public void ultraHdrBitmapAndTextOverlay_hlg10Input_matchesGoldenFile() throws Exception {
Context context = getApplicationContext();
Format format = MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT;
assumeDeviceSupportsUltraHdrEditing();
assumeDeviceSupportsHdrEditing(testId, format);
assumeFormatsSupported(context, testId, /* inputFormat= */ format, /* outputFormat= */ null);
ColorInfo colorInfo = checkNotNull(format.colorInfo);
Bitmap inputBitmap = readBitmap(ULTRA_HDR_ASSET_PATH);
inputBitmap =
Bitmap.createScaledBitmap(
inputBitmap,
inputBitmap.getWidth() / 8,
inputBitmap.getHeight() / 8,
/* filter= */ true);
BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(inputBitmap);
SpannableString overlayText = new SpannableString("W R G B");
overlayText.setSpan(
new ForegroundColorSpan(Color.WHITE),
/* start= */ 0,
/* end= */ 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
overlayText.setSpan(
new ForegroundColorSpan(Color.RED),
/* start= */ 2,
/* end= */ 3,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
overlayText.setSpan(
new ForegroundColorSpan(Color.GREEN),
/* start= */ 4,
/* end= */ 5,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
overlayText.setSpan(
new ForegroundColorSpan(Color.BLUE),
/* start= */ 6,
/* end= */ 7,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextOverlay textOverlay =
TextOverlay.createStaticTextOverlay(overlayText, new OverlaySettings.Builder().build());
videoFrameProcessorTestRunner =
getDefaultFrameProcessorTestRunnerBuilder(testId)
.setEffects(new OverlayEffect(ImmutableList.of(bitmapOverlay, textOverlay)))
.setOutputColorInfo(colorInfo)
.setVideoAssetPath(INPUT_HLG10_MP4_ASSET_STRING)
.build();
Bitmap expectedBitmap = readBitmap(ULTRA_HDR_AND_TEXT_OVERLAY_PNG_ASSET_PATH);
videoFrameProcessorTestRunner.processFirstFrameAndEnd();
Bitmap actualBitmap = videoFrameProcessorTestRunner.getOutputBitmap();
assertBitmapsAreSimilar(expectedBitmap, actualBitmap, HDR_PSNR_THRESHOLD);
}
@Test
public void ultraHdrBitmapOverlay_hlg10Input_matchesGoldenFile() throws Exception {
Context context = getApplicationContext();
@ -333,6 +401,52 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest {
.isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16);
}
@Test
public void textOverlay_hdr10Input_matchesGoldenFile() throws Exception {
Context context = getApplicationContext();
Format format = MP4_ASSET_720P_4_SECOND_HDR10_FORMAT;
assumeDeviceSupportsUltraHdrEditing();
assumeDeviceSupportsHdrEditing(testId, format);
assumeFormatsSupported(context, testId, /* inputFormat= */ format, /* outputFormat= */ null);
ColorInfo colorInfo = checkNotNull(format.colorInfo);
SpannableString overlayText = new SpannableString("W R G B");
overlayText.setSpan(
new ForegroundColorSpan(Color.WHITE),
/* start= */ 0,
/* end= */ 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
overlayText.setSpan(
new ForegroundColorSpan(Color.RED),
/* start= */ 2,
/* end= */ 3,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
overlayText.setSpan(
new ForegroundColorSpan(Color.GREEN),
/* start= */ 4,
/* end= */ 5,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
overlayText.setSpan(
new ForegroundColorSpan(Color.BLUE),
/* start= */ 6,
/* end= */ 7,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextOverlay textOverlay =
TextOverlay.createStaticTextOverlay(
overlayText, new OverlaySettings.Builder().setHdrLuminanceMultiplier(3f).build());
videoFrameProcessorTestRunner =
getDefaultFrameProcessorTestRunnerBuilder(testId)
.setEffects(new OverlayEffect(ImmutableList.of(textOverlay)))
.setOutputColorInfo(colorInfo)
.setVideoAssetPath(INPUT_PQ_MP4_ASSET_STRING)
.build();
Bitmap expectedBitmap = readBitmap(HDR_TEXT_OVERLAY_PNG_ASSET_PATH);
videoFrameProcessorTestRunner.processFirstFrameAndEnd();
Bitmap actualBitmap = videoFrameProcessorTestRunner.getOutputBitmap();
assertBitmapsAreSimilar(expectedBitmap, actualBitmap, HDR_PSNR_THRESHOLD);
}
@Test
public void noEffects_hlg10Input_matchesGoldenFile() throws Exception {
Context context = getApplicationContext();