Add periodic dimming effect to transformer demo.
PeriodicDimmingFrameProcessor is an example of how a custom fragment shader can be used to apply color changes that change over time. PiperOrigin-RevId: 439840609
This commit is contained in:
parent
7c64b1915c
commit
f5792dab92
@ -0,0 +1,31 @@
|
||||
#version 100
|
||||
// Copyright 2022 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
|
||||
//
|
||||
// http://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.
|
||||
|
||||
// ES 2 fragment shader that samples from a (non-external) texture with uTexSampler,
|
||||
// copying from this texture to the current output while applying a vignette effect
|
||||
// by linearly darkening the pixels between uInnerRadius and uOuterRadius.
|
||||
|
||||
precision mediump float;
|
||||
uniform sampler2D uTexSampler;
|
||||
uniform vec2 uCenter;
|
||||
uniform float uInnerRadius;
|
||||
uniform float uOuterRadius;
|
||||
varying vec2 vTexSamplingCoord;
|
||||
void main() {
|
||||
vec3 src = texture2D(uTexSampler, vTexSamplingCoord).xyz;
|
||||
float dist = distance(vTexSamplingCoord, uCenter);
|
||||
float scale = clamp(1.0 - (dist - uInnerRadius) / (uOuterRadius - uInnerRadius), 0.0, 1.0);
|
||||
gl_FragColor = vec4(src.r * scale, src.g * scale, src.b * scale, 1.0);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
#version 100
|
||||
// Copyright 2022 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
|
||||
//
|
||||
// http://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.
|
||||
|
||||
// ES 2 vertex shader that leaves the coordinates unchanged.
|
||||
|
||||
attribute vec4 aFramePosition;
|
||||
attribute vec4 aTexSamplingCoord;
|
||||
varying vec2 vTexSamplingCoord;
|
||||
void main() {
|
||||
gl_Position = aFramePosition;
|
||||
vTexSamplingCoord = aTexSamplingCoord.xy;
|
||||
}
|
@ -34,6 +34,8 @@ import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.Util;
|
||||
import com.google.android.material.slider.RangeSlider;
|
||||
import com.google.android.material.slider.Slider;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
@ -56,7 +58,11 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
public static final String ENABLE_FALLBACK = "enable_fallback";
|
||||
public static final String ENABLE_REQUEST_SDR_TONE_MAPPING = "enable_request_sdr_tone_mapping";
|
||||
public static final String ENABLE_HDR_EDITING = "enable_hdr_editing";
|
||||
public static final String FRAME_PROCESSOR_SELECTION = "frame_processor_selection";
|
||||
public static final String DEMO_FRAME_PROCESSORS_SELECTIONS = "demo_frame_processors_selections";
|
||||
public static final String PERIODIC_VIGNETTE_CENTER_X = "periodic_vignette_center_x";
|
||||
public static final String PERIODIC_VIGNETTE_CENTER_Y = "periodic_vignette_center_y";
|
||||
public static final String PERIODIC_VIGNETTE_INNER_RADIUS = "periodic_vignette_inner_radius";
|
||||
public static final String PERIODIC_VIGNETTE_OUTER_RADIUS = "periodic_vignette_outer_radius";
|
||||
private static final String[] INPUT_URIS = {
|
||||
"https://html5demos.com/assets/dizzy.mp4",
|
||||
"https://storage.googleapis.com/exoplayer-test-media-0/android-block-1080-hevc.mp4",
|
||||
@ -81,8 +87,12 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"SEF slow motion with 240 fps",
|
||||
"MP4 with HDR (HDR10) H265 video (encoding may fail)",
|
||||
};
|
||||
private static final String[] FRAME_PROCESSORS = {"Dizzy crop", "3D spin", "Zoom in start"};
|
||||
private static final String[] DEMO_FRAME_PROCESSORS = {
|
||||
"Dizzy crop", "Periodic vignette", "3D spin", "Zoom in start"
|
||||
};
|
||||
private static final int PERIODIC_VIGNETTE_INDEX = 1;
|
||||
private static final String SAME_AS_INPUT_OPTION = "same as input";
|
||||
private static final float HALF_DIAGONAL = 1f / (float) Math.sqrt(2);
|
||||
|
||||
private @MonotonicNonNull Button selectFileButton;
|
||||
private @MonotonicNonNull TextView selectedFileTextView;
|
||||
@ -97,9 +107,13 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
|
||||
private @MonotonicNonNull CheckBox enableRequestSdrToneMappingCheckBox;
|
||||
private @MonotonicNonNull CheckBox enableHdrEditingCheckBox;
|
||||
private @MonotonicNonNull Button selectFrameProcessorsButton;
|
||||
private boolean @MonotonicNonNull [] selectedFrameProcessors;
|
||||
private @MonotonicNonNull Button selectDemoFrameProcessorsButton;
|
||||
private boolean @MonotonicNonNull [] demoFrameProcessorsSelections;
|
||||
private int inputUriPosition;
|
||||
private float periodicVignetteCenterX;
|
||||
private float periodicVignetteCenterY;
|
||||
private float periodicVignetteInnerRadius;
|
||||
private float periodicVignetteOuterRadius;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@ -169,9 +183,9 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
findViewById(R.id.request_sdr_tone_mapping).setEnabled(isRequestSdrToneMappingSupported());
|
||||
enableHdrEditingCheckBox = findViewById(R.id.hdr_editing_checkbox);
|
||||
|
||||
selectedFrameProcessors = new boolean[FRAME_PROCESSORS.length];
|
||||
selectFrameProcessorsButton = findViewById(R.id.select_frameprocessors_button);
|
||||
selectFrameProcessorsButton.setOnClickListener(this::selectFrameProcessors);
|
||||
demoFrameProcessorsSelections = new boolean[DEMO_FRAME_PROCESSORS.length];
|
||||
selectDemoFrameProcessorsButton = findViewById(R.id.select_demo_frameprocessors_button);
|
||||
selectDemoFrameProcessorsButton.setOnClickListener(this::selectFrameProcessors);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -202,7 +216,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"enableFallbackCheckBox",
|
||||
"enableRequestSdrToneMappingCheckBox",
|
||||
"enableHdrEditingCheckBox",
|
||||
"selectedFrameProcessors"
|
||||
"demoFrameProcessorsSelections"
|
||||
})
|
||||
private void startTransformation(View view) {
|
||||
Intent transformerIntent = new Intent(this, TransformerActivity.class);
|
||||
@ -237,7 +251,11 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
bundle.putBoolean(
|
||||
ENABLE_REQUEST_SDR_TONE_MAPPING, enableRequestSdrToneMappingCheckBox.isChecked());
|
||||
bundle.putBoolean(ENABLE_HDR_EDITING, enableHdrEditingCheckBox.isChecked());
|
||||
bundle.putBooleanArray(FRAME_PROCESSOR_SELECTION, selectedFrameProcessors);
|
||||
bundle.putBooleanArray(DEMO_FRAME_PROCESSORS_SELECTIONS, demoFrameProcessorsSelections);
|
||||
bundle.putFloat(PERIODIC_VIGNETTE_CENTER_X, periodicVignetteCenterX);
|
||||
bundle.putFloat(PERIODIC_VIGNETTE_CENTER_Y, periodicVignetteCenterY);
|
||||
bundle.putFloat(PERIODIC_VIGNETTE_INNER_RADIUS, periodicVignetteInnerRadius);
|
||||
bundle.putFloat(PERIODIC_VIGNETTE_OUTER_RADIUS, periodicVignetteOuterRadius);
|
||||
transformerIntent.putExtras(bundle);
|
||||
|
||||
@Nullable Uri intentUri = getIntent().getData();
|
||||
@ -258,9 +276,11 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
|
||||
private void selectFrameProcessors(View view) {
|
||||
new AlertDialog.Builder(/* context= */ this)
|
||||
.setTitle(R.string.select_frameprocessors)
|
||||
.setTitle(R.string.select_demo_frameprocessors)
|
||||
.setMultiChoiceItems(
|
||||
FRAME_PROCESSORS, checkNotNull(selectedFrameProcessors), this::selectFrameProcessor)
|
||||
DEMO_FRAME_PROCESSORS,
|
||||
checkNotNull(demoFrameProcessorsSelections),
|
||||
this::selectFrameProcessor)
|
||||
.setPositiveButton(android.R.string.ok, /* listener= */ null)
|
||||
.create()
|
||||
.show();
|
||||
@ -272,9 +292,36 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
selectedFileTextView.setText(URI_DESCRIPTIONS[inputUriPosition]);
|
||||
}
|
||||
|
||||
@RequiresNonNull("selectedFrameProcessors")
|
||||
@RequiresNonNull("demoFrameProcessorsSelections")
|
||||
private void selectFrameProcessor(DialogInterface dialog, int which, boolean isChecked) {
|
||||
selectedFrameProcessors[which] = isChecked;
|
||||
demoFrameProcessorsSelections[which] = isChecked;
|
||||
if (!isChecked || which != PERIODIC_VIGNETTE_INDEX) {
|
||||
return;
|
||||
}
|
||||
|
||||
View dialogView =
|
||||
getLayoutInflater().inflate(R.layout.periodic_vignette_options, /* root= */ null);
|
||||
Slider centerXSlider =
|
||||
checkNotNull(dialogView.findViewById(R.id.periodic_vignette_center_x_slider));
|
||||
Slider centerYSlider =
|
||||
checkNotNull(dialogView.findViewById(R.id.periodic_vignette_center_y_slider));
|
||||
RangeSlider radiusRangeSlider =
|
||||
checkNotNull(dialogView.findViewById(R.id.periodic_vignette_radius_range_slider));
|
||||
radiusRangeSlider.setValues(0f, HALF_DIAGONAL);
|
||||
new AlertDialog.Builder(/* context= */ this)
|
||||
.setTitle(R.string.periodic_vignette_options)
|
||||
.setView(dialogView)
|
||||
.setPositiveButton(
|
||||
android.R.string.ok,
|
||||
(DialogInterface dialogInterface, int i) -> {
|
||||
periodicVignetteCenterX = centerXSlider.getValue();
|
||||
periodicVignetteCenterY = centerYSlider.getValue();
|
||||
List<Float> radiusRange = radiusRangeSlider.getValues();
|
||||
periodicVignetteInnerRadius = radiusRange.get(0);
|
||||
periodicVignetteOuterRadius = radiusRange.get(1);
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
@RequiresNonNull({
|
||||
@ -286,7 +333,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"rotateSpinner",
|
||||
"enableRequestSdrToneMappingCheckBox",
|
||||
"enableHdrEditingCheckBox",
|
||||
"selectFrameProcessorsButton"
|
||||
"selectDemoFrameProcessorsButton"
|
||||
})
|
||||
private void onRemoveAudio(View view) {
|
||||
if (((CheckBox) view).isChecked()) {
|
||||
@ -306,7 +353,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"rotateSpinner",
|
||||
"enableRequestSdrToneMappingCheckBox",
|
||||
"enableHdrEditingCheckBox",
|
||||
"selectFrameProcessorsButton"
|
||||
"selectDemoFrameProcessorsButton"
|
||||
})
|
||||
private void onRemoveVideo(View view) {
|
||||
if (((CheckBox) view).isChecked()) {
|
||||
@ -325,7 +372,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
"rotateSpinner",
|
||||
"enableRequestSdrToneMappingCheckBox",
|
||||
"enableHdrEditingCheckBox",
|
||||
"selectFrameProcessorsButton"
|
||||
"selectDemoFrameProcessorsButton"
|
||||
})
|
||||
private void enableTrackSpecificOptions(boolean isAudioEnabled, boolean isVideoEnabled) {
|
||||
audioMimeSpinner.setEnabled(isAudioEnabled);
|
||||
@ -336,7 +383,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
||||
enableRequestSdrToneMappingCheckBox.setEnabled(
|
||||
isRequestSdrToneMappingSupported() && isVideoEnabled);
|
||||
enableHdrEditingCheckBox.setEnabled(isVideoEnabled);
|
||||
selectFrameProcessorsButton.setEnabled(isVideoEnabled);
|
||||
selectDemoFrameProcessorsButton.setEnabled(isVideoEnabled);
|
||||
|
||||
findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled);
|
||||
findViewById(R.id.video_mime_text_view).setEnabled(isVideoEnabled);
|
||||
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2022 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
|
||||
*
|
||||
* http://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.demo.transformer;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
|
||||
import android.content.Context;
|
||||
import android.opengl.GLES20;
|
||||
import android.util.Size;
|
||||
import androidx.media3.common.util.GlProgram;
|
||||
import androidx.media3.common.util.GlUtil;
|
||||
import androidx.media3.transformer.GlFrameProcessor;
|
||||
import java.io.IOException;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* A {@link GlFrameProcessor} that periodically dims the frames such that pixels are darker the
|
||||
* further they are away from the frame center.
|
||||
*/
|
||||
/* package */ final class PeriodicVignetteFrameProcessor implements GlFrameProcessor {
|
||||
static {
|
||||
GlUtil.glAssertionsEnabled = true;
|
||||
}
|
||||
|
||||
private static final String VERTEX_SHADER_PATH = "vertex_shader_copy_es2.glsl";
|
||||
private static final String FRAGMENT_SHADER_PATH = "fragment_shader_vignette_es2.glsl";
|
||||
private static final float DIMMING_PERIOD_US = 5_600_000f;
|
||||
|
||||
private final Context context;
|
||||
private float centerX;
|
||||
private float centerY;
|
||||
private float minInnerRadius;
|
||||
private float deltaInnerRadius;
|
||||
private float outerRadius;
|
||||
|
||||
private @MonotonicNonNull Size outputSize;
|
||||
private @MonotonicNonNull GlProgram glProgram;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* <p>The inner radius of the vignette effect oscillates smoothly between {@code minInnerRadius}
|
||||
* and {@code maxInnerRadius}.
|
||||
*
|
||||
* <p>The pixels between the inner radius and the {@code outerRadius} are darkened linearly based
|
||||
* on their distance from {@code innerRadius}. All pixels outside {@code outerRadius} are black.
|
||||
*
|
||||
* <p>The parameters are given in normalized texture coordinates from 0 to 1.
|
||||
*
|
||||
* @param context The {@link Context}.
|
||||
* @param centerX The x-coordinate of the center of the effect.
|
||||
* @param centerY The y-coordinate of the center of the effect.
|
||||
* @param minInnerRadius The lower bound of the radius that is unaffected by the effect.
|
||||
* @param maxInnerRadius The upper bound of the radius that is unaffected by the effect.
|
||||
* @param outerRadius The radius after which all pixels are black.
|
||||
*/
|
||||
public PeriodicVignetteFrameProcessor(
|
||||
Context context,
|
||||
float centerX,
|
||||
float centerY,
|
||||
float minInnerRadius,
|
||||
float maxInnerRadius,
|
||||
float outerRadius) {
|
||||
checkArgument(minInnerRadius <= maxInnerRadius);
|
||||
checkArgument(maxInnerRadius <= outerRadius);
|
||||
this.context = context;
|
||||
this.centerX = centerX;
|
||||
this.centerY = centerY;
|
||||
this.minInnerRadius = minInnerRadius;
|
||||
this.deltaInnerRadius = maxInnerRadius - minInnerRadius;
|
||||
this.outerRadius = outerRadius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(int inputTexId, int inputWidth, int inputHeight) throws IOException {
|
||||
outputSize = new Size(inputWidth, inputHeight);
|
||||
glProgram = new GlProgram(context, VERTEX_SHADER_PATH, FRAGMENT_SHADER_PATH);
|
||||
glProgram.setSamplerTexIdUniform("uTexSampler", inputTexId, /* texUnitIndex= */ 0);
|
||||
glProgram.setFloatsUniform("uCenter", new float[] {centerX, centerY});
|
||||
glProgram.setFloatsUniform("uOuterRadius", new float[] {outerRadius});
|
||||
// Draw the frame on the entire normalized device coordinate space, from -1 to 1, for x and y.
|
||||
glProgram.setBufferAttribute(
|
||||
"aFramePosition", GlUtil.getNormalizedCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT);
|
||||
glProgram.setBufferAttribute(
|
||||
"aTexSamplingCoord", GlUtil.getTextureCoordinateBounds(), GlUtil.RECTANGLE_VERTICES_COUNT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size getOutputSize() {
|
||||
return checkStateNotNull(outputSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgramAndDraw(long presentationTimeUs) {
|
||||
checkStateNotNull(glProgram).use();
|
||||
double theta = presentationTimeUs * 2 * Math.PI / DIMMING_PERIOD_US;
|
||||
float innerRadius = minInnerRadius + deltaInnerRadius * (0.5f - 0.5f * (float) Math.cos(theta));
|
||||
glProgram.setFloatsUniform("uInnerRadius", new float[] {innerRadius});
|
||||
glProgram.bindAttributesAndUniforms();
|
||||
// The four-vertex triangle strip forms a quad.
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, /* first= */ 0, /* count= */ 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
if (glProgram != null) {
|
||||
glProgram.delete();
|
||||
}
|
||||
}
|
||||
}
|
@ -243,7 +243,7 @@ public final class TransformerActivity extends AppCompatActivity {
|
||||
ImmutableList.Builder<GlFrameProcessor> frameProcessors = new ImmutableList.Builder<>();
|
||||
@Nullable
|
||||
boolean[] selectedFrameProcessors =
|
||||
bundle.getBooleanArray(ConfigurationActivity.FRAME_PROCESSOR_SELECTION);
|
||||
bundle.getBooleanArray(ConfigurationActivity.DEMO_FRAME_PROCESSORS_SELECTIONS);
|
||||
if (selectedFrameProcessors != null) {
|
||||
if (selectedFrameProcessors[0]) {
|
||||
frameProcessors.add(
|
||||
@ -251,9 +251,21 @@ public final class TransformerActivity extends AppCompatActivity {
|
||||
}
|
||||
if (selectedFrameProcessors[1]) {
|
||||
frameProcessors.add(
|
||||
AdvancedFrameProcessorFactory.createSpin3dFrameProcessor(/* context= */ this));
|
||||
new PeriodicVignetteFrameProcessor(
|
||||
/* context= */ this,
|
||||
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_CENTER_X),
|
||||
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_CENTER_Y),
|
||||
/* minInnerRadius= */ bundle.getFloat(
|
||||
ConfigurationActivity.PERIODIC_VIGNETTE_INNER_RADIUS),
|
||||
/* maxInnerRadius= */ bundle.getFloat(
|
||||
ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS),
|
||||
bundle.getFloat(ConfigurationActivity.PERIODIC_VIGNETTE_OUTER_RADIUS)));
|
||||
}
|
||||
if (selectedFrameProcessors[2]) {
|
||||
frameProcessors.add(
|
||||
AdvancedFrameProcessorFactory.createSpin3dFrameProcessor(/* context= */ this));
|
||||
}
|
||||
if (selectedFrameProcessors[3]) {
|
||||
frameProcessors.add(
|
||||
AdvancedFrameProcessorFactory.createZoomInTransitionFrameProcessor(
|
||||
/* context= */ this));
|
||||
|
@ -64,7 +64,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/selected_file_text_view"
|
||||
app:layout_constraintBottom_toTopOf="@+id/select_frameprocessors_button">
|
||||
app:layout_constraintBottom_toTopOf="@+id/select_demo_frameprocessors_button">
|
||||
<TableLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -192,13 +192,13 @@
|
||||
</TableLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
<Button
|
||||
android:id="@+id/select_frameprocessors_button"
|
||||
android:id="@+id/select_demo_frameprocessors_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:text="@string/select_frameprocessors"
|
||||
android:text="@string/select_demo_frameprocessors"
|
||||
app:layout_constraintBottom_toTopOf="@+id/transform_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright 2022 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
|
||||
~
|
||||
~ http://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.
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
tools:context=".ConfigurationActivity">
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="1"
|
||||
android:layout_marginTop="32dp"
|
||||
android:measureWithLargestChild="true"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="12dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical" >
|
||||
<TextView
|
||||
android:text="@string/center_x" />
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/periodic_vignette_center_x_slider"
|
||||
android:valueFrom="0.0"
|
||||
android:value="0.5"
|
||||
android:valueTo="1.0"
|
||||
android:layout_gravity="right"/>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical" >
|
||||
<TextView
|
||||
android:text="@string/center_y" />
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/periodic_vignette_center_y_slider"
|
||||
android:valueFrom="0.0"
|
||||
android:value="0.5"
|
||||
android:valueTo="1.0"
|
||||
android:layout_gravity="right"/>
|
||||
</TableRow>
|
||||
<TableRow
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical" >
|
||||
<TextView
|
||||
android:text="@string/radius_range" />
|
||||
<com.google.android.material.slider.RangeSlider
|
||||
android:id="@+id/periodic_vignette_radius_range_slider"
|
||||
android:valueFrom="0.0"
|
||||
android:valueTo="1.414"
|
||||
android:layout_gravity="right"/>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -29,7 +29,8 @@
|
||||
<string name="enable_fallback" translatable="false">Enable fallback</string>
|
||||
<string name="request_sdr_tone_mapping" translatable="false">Request SDR tone-mapping (API 31+)</string>
|
||||
<string name="hdr_editing" translatable="false">[Experimental] HDR editing</string>
|
||||
<string name="select_frameprocessors" translatable="false">Add effects</string>
|
||||
<string name="select_demo_frameprocessors" translatable="false">Add demo effects</string>
|
||||
<string name="periodic_vignette_options" translatable="false">Periodic vignette options</string>
|
||||
<string name="transform" translatable="false">Transform</string>
|
||||
<string name="debug_preview" translatable="false">Debug preview:</string>
|
||||
<string name="debug_preview_not_available" translatable="false">No debug preview available.</string>
|
||||
@ -37,4 +38,7 @@
|
||||
<string name="transformation_timer" translatable="false">Transformation started %d seconds ago.</string>
|
||||
<string name="transformation_completed" translatable="false">Transformation completed in %d seconds.</string>
|
||||
<string name="transformation_error" translatable="false">Transformation error</string>
|
||||
<string name="center_x">Center X</string>
|
||||
<string name="center_y">Center Y</string>
|
||||
<string name="radius_range">Radius range</string>
|
||||
</resources>
|
||||
|
@ -364,45 +364,48 @@ public final class GlProgram {
|
||||
* <p>Should be called before each drawing call.
|
||||
*/
|
||||
public void bind() {
|
||||
if (type == GLES20.GL_FLOAT) {
|
||||
GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
return;
|
||||
switch (type) {
|
||||
case GLES20.GL_FLOAT:
|
||||
GLES20.glUniform1fv(location, /* count= */ 1, value, /* offset= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
break;
|
||||
case GLES20.GL_FLOAT_VEC2:
|
||||
GLES20.glUniform2fv(location, /* count= */ 1, value, /* offset= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
break;
|
||||
case GLES20.GL_FLOAT_VEC3:
|
||||
GLES20.glUniform3fv(location, /* count= */ 1, value, /* offset= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
break;
|
||||
case GLES20.GL_FLOAT_MAT3:
|
||||
GLES20.glUniformMatrix3fv(
|
||||
location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
break;
|
||||
case GLES20.GL_FLOAT_MAT4:
|
||||
GLES20.glUniformMatrix4fv(
|
||||
location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
break;
|
||||
case GLES20.GL_SAMPLER_2D:
|
||||
case GLES11Ext.GL_SAMPLER_EXTERNAL_OES:
|
||||
case GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT:
|
||||
if (texId == 0) {
|
||||
throw new IllegalStateException("No call to setSamplerTexId() before bind.");
|
||||
}
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + texUnitIndex);
|
||||
GlUtil.checkGlError();
|
||||
GlUtil.bindTexture(
|
||||
type == GLES20.GL_SAMPLER_2D
|
||||
? GLES20.GL_TEXTURE_2D
|
||||
: GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
|
||||
texId);
|
||||
GLES20.glUniform1i(location, texUnitIndex);
|
||||
GlUtil.checkGlError();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected uniform type: " + type);
|
||||
}
|
||||
|
||||
if (type == GLES20.GL_FLOAT_MAT3) {
|
||||
GLES20.glUniformMatrix3fv(
|
||||
location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == GLES20.GL_FLOAT_MAT4) {
|
||||
GLES20.glUniformMatrix4fv(
|
||||
location, /* count= */ 1, /* transpose= */ false, value, /* offset= */ 0);
|
||||
GlUtil.checkGlError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (texId == 0) {
|
||||
throw new IllegalStateException("No call to setSamplerTexId() before bind.");
|
||||
}
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + texUnitIndex);
|
||||
if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT) {
|
||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
|
||||
} else if (type == GLES20.GL_SAMPLER_2D) {
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected uniform type: " + type);
|
||||
}
|
||||
GLES20.glUniform1i(location, texUnitIndex);
|
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameteri(
|
||||
GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameteri(
|
||||
GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GlUtil.checkGlError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -385,7 +385,9 @@ public final class GlUtil {
|
||||
* GL_CLAMP_TO_EDGE wrapping.
|
||||
*/
|
||||
public static int createExternalTexture() {
|
||||
return generateAndBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
|
||||
int texId = generateTexture();
|
||||
bindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
|
||||
return texId;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -396,7 +398,8 @@ public final class GlUtil {
|
||||
*/
|
||||
public static int createTexture(int width, int height) {
|
||||
assertValidTextureSize(width, height);
|
||||
int texId = generateAndBindTexture(GLES20.GL_TEXTURE_2D);
|
||||
int texId = generateTexture();
|
||||
bindTexture(GLES20.GL_TEXTURE_2D, texId);
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
|
||||
GLES20.glTexImage2D(
|
||||
GLES20.GL_TEXTURE_2D,
|
||||
@ -412,30 +415,37 @@ public final class GlUtil {
|
||||
return texId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a GL texture identifier of a newly generated and bound texture of the requested type
|
||||
* with default configuration of GL_LINEAR filtering and GL_CLAMP_TO_EDGE wrapping.
|
||||
*
|
||||
* @param textureTarget The target to which the texture is bound, e.g. {@link
|
||||
* GLES20#GL_TEXTURE_2D} for a two-dimensional texture or {@link
|
||||
* GLES11Ext#GL_TEXTURE_EXTERNAL_OES} for an external texture.
|
||||
*/
|
||||
private static int generateAndBindTexture(int textureTarget) {
|
||||
/** Returns a new GL texture identifier. */
|
||||
private static int generateTexture() {
|
||||
checkEglException(
|
||||
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
||||
|
||||
int[] texId = new int[1];
|
||||
GLES20.glGenTextures(/* n= */ 1, texId, /* offset= */ 0);
|
||||
checkGlError();
|
||||
GLES20.glBindTexture(textureTarget, texId[0]);
|
||||
return texId[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the texture of the given type with default configuration of GL_LINEAR filtering and
|
||||
* GL_CLAMP_TO_EDGE wrapping.
|
||||
*
|
||||
* @param texId The texture identifier.
|
||||
* @param textureTarget The target to which the texture is bound, e.g. {@link
|
||||
* GLES20#GL_TEXTURE_2D} for a two-dimensional texture or {@link
|
||||
* GLES11Ext#GL_TEXTURE_EXTERNAL_OES} for an external texture.
|
||||
*/
|
||||
/* package */ static void bindTexture(int textureTarget, int texId) {
|
||||
GLES20.glBindTexture(textureTarget, texId);
|
||||
checkGlError();
|
||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
||||
checkGlError();
|
||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
||||
checkGlError();
|
||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
|
||||
checkGlError();
|
||||
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
|
||||
checkGlError();
|
||||
return texId[0];
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user