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:
hschlueter 2022-04-06 16:07:12 +01:00 committed by Ian Baker
parent 7c64b1915c
commit f5792dab92
10 changed files with 401 additions and 74 deletions

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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();
}
}
}

View File

@ -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));

View File

@ -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" />

View File

@ -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>

View File

@ -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>

View File

@ -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();
}
}
}

View File

@ -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];
}
/**