Egloo: create GlTextureDrawer

egloo2
Mattia Iavarone 5 years ago
parent 24b02caa28
commit 8bad417cf2
  1. 4
      build.gradle
  2. 2
      cameraview/build.gradle
  3. 12
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/BaseFilterTest.java
  4. 12
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/filter/MultiFilterTest.java
  5. 21
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/overlay/OverlayDrawerTest.java
  6. 100
      cameraview/src/main/java/com/otaliastudios/cameraview/filter/BaseFilter.java
  7. 91
      cameraview/src/main/java/com/otaliastudios/cameraview/filter/MultiFilter.java
  8. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/AutoFixFilter.java
  9. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/BrightnessFilter.java
  10. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/ContrastFilter.java
  11. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/DocumentaryFilter.java
  12. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/DuotoneFilter.java
  13. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/FillLightFilter.java
  14. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/GammaFilter.java
  15. 14
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrainFilter.java
  16. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/HueFilter.java
  17. 18
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/LomoishFilter.java
  18. 14
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/SaturationFilter.java
  19. 14
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/SharpnessFilter.java
  20. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/TemperatureFilter.java
  21. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/TintFilter.java
  22. 18
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/VignetteFilter.java
  23. 96
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/GlTextureDrawer.java
  24. 96
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/GlUtils.java
  25. 5
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglBaseSurface.java
  26. 16
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglViewport.java
  27. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java
  28. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java
  29. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java
  30. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/video/encoding/TextureMediaEncoder.java
  31. 2
      settings.gradle

@ -8,7 +8,11 @@ buildscript {
classpath 'com.android.tools.build:gradle:3.5.3' classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31"
classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.17"
} }
ext.kotlinVersion = '1.3.31'
} }
allprojects { allprojects {

@ -48,6 +48,8 @@ dependencies {
api 'androidx.lifecycle:lifecycle-common:2.1.0' api 'androidx.lifecycle:lifecycle-common:2.1.0'
api 'com.google.android.gms:play-services-tasks:17.0.0' api 'com.google.android.gms:play-services-tasks:17.0.0'
implementation 'androidx.annotation:annotation:1.1.0' implementation 'androidx.annotation:annotation:1.1.0'
implementation project(':egloo')
} }
//endregion //endregion

@ -8,16 +8,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.BaseEglTest; import com.otaliastudios.cameraview.BaseEglTest;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.cameraview.internal.egl.EglViewport; import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.opengl.program.GlTextureProgram;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@ -91,11 +91,11 @@ public class BaseFilterTest extends BaseEglTest {
@Test @Test
public void testOnProgramCreate() { public void testOnProgramCreate() {
filter = new TestFilter(); filter = new TestFilter();
int handle = GlUtils.createProgram(filter.getVertexShader(), filter.getFragmentShader()); int handle = GlTextureProgram.create(filter.getVertexShader(), filter.getFragmentShader());
filter.onCreate(handle); filter.onCreate(handle);
assertTrue(filter.programHandle >= 0); assertNotNull(filter.program);
filter.onDestroy(); filter.onDestroy();
assertTrue(filter.programHandle < 0); assertNull(filter.program);
GLES20.glDeleteProgram(handle); GLES20.glDeleteProgram(handle);
} }
@ -117,7 +117,7 @@ public class BaseFilterTest extends BaseEglTest {
int texture = viewport.createTexture(); int texture = viewport.createTexture();
float[] matrix = new float[16]; float[] matrix = new float[16];
viewport.drawFrame(0L, texture, matrix); viewport.draw(0L, texture, matrix);
verify(filter, times(1)).onPreDraw(0L, matrix); verify(filter, times(1)).onPreDraw(0L, matrix);
verify(filter, times(1)).onDraw(0L); verify(filter, times(1)).onDraw(0L);
verify(filter, times(1)).onPostDraw(0L); verify(filter, times(1)).onPostDraw(0L);

@ -11,8 +11,8 @@ import com.otaliastudios.cameraview.filters.AutoFixFilter;
import com.otaliastudios.cameraview.filters.BrightnessFilter; import com.otaliastudios.cameraview.filters.BrightnessFilter;
import com.otaliastudios.cameraview.filters.DuotoneFilter; import com.otaliastudios.cameraview.filters.DuotoneFilter;
import com.otaliastudios.cameraview.filters.VignetteFilter; import com.otaliastudios.cameraview.filters.VignetteFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.cameraview.internal.egl.EglViewport; import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.opengl.program.GlProgram;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -127,7 +127,7 @@ public class MultiFilterTest extends BaseEglTest {
DuotoneFilter filter = spy(new DuotoneFilter()); DuotoneFilter filter = spy(new DuotoneFilter());
MultiFilter multiFilter = new MultiFilter(filter); MultiFilter multiFilter = new MultiFilter(filter);
int program = GlUtils.createProgram(multiFilter.getVertexShader(), int program = GlProgram.create(multiFilter.getVertexShader(),
multiFilter.getFragmentShader()); multiFilter.getFragmentShader());
multiFilter.onCreate(program); multiFilter.onCreate(program);
verify(filter, never()).onCreate(anyInt()); verify(filter, never()).onCreate(anyInt());
@ -145,7 +145,7 @@ public class MultiFilterTest extends BaseEglTest {
EglViewport viewport = new EglViewport(multiFilter); EglViewport viewport = new EglViewport(multiFilter);
int texture = viewport.createTexture(); int texture = viewport.createTexture();
float[] matrix = new float[16]; float[] matrix = new float[16];
viewport.drawFrame(0L, texture, matrix); viewport.draw(0L, texture, matrix);
viewport.release(); viewport.release();
// The child should have experienced the whole lifecycle. // The child should have experienced the whole lifecycle.
@ -173,7 +173,7 @@ public class MultiFilterTest extends BaseEglTest {
public Object answer(InvocationOnMock invocation) { public Object answer(InvocationOnMock invocation) {
MultiFilter.State state = multiFilter.states.get(filter1); MultiFilter.State state = multiFilter.states.get(filter1);
assertNotNull(state); assertNotNull(state);
assertTrue(state.isCreated); assertTrue(state.isProgramCreated);
assertTrue(state.isFramebufferCreated); assertTrue(state.isFramebufferCreated);
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0); GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0);
@ -189,7 +189,7 @@ public class MultiFilterTest extends BaseEglTest {
// The last filter has no FBO / texture. // The last filter has no FBO / texture.
MultiFilter.State state = multiFilter.states.get(filter2); MultiFilter.State state = multiFilter.states.get(filter2);
assertNotNull(state); assertNotNull(state);
assertTrue(state.isCreated); assertTrue(state.isProgramCreated);
assertFalse(state.isFramebufferCreated); assertFalse(state.isFramebufferCreated);
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0); GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0);
@ -201,7 +201,7 @@ public class MultiFilterTest extends BaseEglTest {
EglViewport viewport = new EglViewport(multiFilter); EglViewport viewport = new EglViewport(multiFilter);
int texture = viewport.createTexture(); int texture = viewport.createTexture();
viewport.drawFrame(0L, texture, matrix); viewport.draw(0L, texture, matrix);
viewport.release(); viewport.release();
// Verify that both are drawn. // Verify that both are drawn.

@ -1,48 +1,29 @@
package com.otaliastudios.cameraview.overlay; package com.otaliastudios.cameraview.overlay;
import android.content.res.XmlResourceParser;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.BaseEglTest; import com.otaliastudios.cameraview.BaseEglTest;
import com.otaliastudios.cameraview.BaseTest;
import com.otaliastudios.cameraview.internal.egl.EglBaseSurface;
import com.otaliastudios.cameraview.internal.egl.EglCore;
import com.otaliastudios.cameraview.internal.egl.EglViewport; import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import org.hamcrest.BaseMatcher; import org.hamcrest.BaseMatcher;
import org.hamcrest.Description; import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mockito;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
@ -82,7 +63,7 @@ public class OverlayDrawerTest extends BaseEglTest {
drawer.mViewport = spy(drawer.mViewport); drawer.mViewport = spy(drawer.mViewport);
drawer.draw(Overlay.Target.PICTURE_SNAPSHOT); drawer.draw(Overlay.Target.PICTURE_SNAPSHOT);
drawer.render(0L); drawer.render(0L);
verify(drawer.mViewport, times(1)).drawFrame( verify(drawer.mViewport, times(1)).draw(
0L, 0L,
drawer.mTextureId, drawer.mTextureId,
drawer.getTransform() drawer.getTransform()

@ -1,15 +1,14 @@
package com.otaliastudios.cameraview.filter; package com.otaliastudios.cameraview.filter;
import android.opengl.GLES20;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import com.otaliastudios.opengl.draw.GlDrawable;
import java.nio.FloatBuffer; import com.otaliastudios.opengl.draw.GlRect;
import com.otaliastudios.opengl.program.GlTextureProgram;
import com.otaliastudios.opengl.texture.GlTexture;
/** /**
* A base implementation of {@link Filter} that just leaves the fragment shader to subclasses. * A base implementation of {@link Filter} that just leaves the fragment shader to subclasses.
@ -90,26 +89,8 @@ public abstract class BaseFilter implements Filter {
+ "}\n"; + "}\n";
} }
// When the model/view/projection matrix is identity, this will exactly cover the viewport. @VisibleForTesting GlTextureProgram program = null;
private FloatBuffer vertexPosition = GlUtils.floatBuffer(new float[]{ private final GlDrawable programDrawable = new GlRect();
-1.0f, -1.0f, // 0 bottom left
1.0f, -1.0f, // 1 bottom right
-1.0f, 1.0f, // 2 top left
1.0f, 1.0f, // 3 top right
});
private FloatBuffer textureCoordinates = GlUtils.floatBuffer(new float[]{
0.0f, 0.0f, // 0 bottom left
1.0f, 0.0f, // 1 bottom right
0.0f, 1.0f, // 2 top left
1.0f, 1.0f // 3 top right
});
private int vertexModelViewProjectionMatrixLocation = -1;
private int vertexTransformMatrixLocation = -1;
private int vertexPositionLocation = -1;
private int vertexTextureCoordinateLocation = -1;
@VisibleForTesting int programHandle = -1;
@VisibleForTesting Size size; @VisibleForTesting Size size;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@ -141,28 +122,22 @@ public abstract class BaseFilter implements Filter {
@Override @Override
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
this.programHandle = programHandle; program = new GlTextureProgram(
vertexPositionLocation = GLES20.glGetAttribLocation(programHandle, vertexPositionName); programHandle,
GlUtils.checkLocation(vertexPositionLocation, vertexPositionName); vertexPositionName,
vertexTextureCoordinateLocation = GLES20.glGetAttribLocation(programHandle, vertexModelViewProjectionMatrixName,
vertexTextureCoordinateName); vertexTextureCoordinateName,
GlUtils.checkLocation(vertexTextureCoordinateLocation, vertexTextureCoordinateName); vertexTransformMatrixName
vertexModelViewProjectionMatrixLocation = GLES20.glGetUniformLocation(programHandle, );
vertexModelViewProjectionMatrixName);
GlUtils.checkLocation(vertexModelViewProjectionMatrixLocation,
vertexModelViewProjectionMatrixName);
vertexTransformMatrixLocation = GLES20.glGetUniformLocation(programHandle,
vertexTransformMatrixName);
GlUtils.checkLocation(vertexTransformMatrixLocation, vertexTransformMatrixName);
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
programHandle = -1; // NOTE: this destroys the GL program, but Filter consumers (GlTextureDrawer, MultiFilter)
vertexPositionLocation = -1; // will destroy it too (just like they create it). Hope this doesn't cause any issue;
vertexTextureCoordinateLocation = -1; // if it does, we could avoid calling release here.
vertexModelViewProjectionMatrixLocation = -1; program.release();
vertexTransformMatrixLocation = -1; program = null;
} }
@NonNull @NonNull
@ -178,7 +153,7 @@ public abstract class BaseFilter implements Filter {
@Override @Override
public void draw(long timestampUs, @NonNull float[] transformMatrix) { public void draw(long timestampUs, @NonNull float[] transformMatrix) {
if (programHandle == -1) { if (program == null) {
LOG.w("Filter.draw() called after destroying the filter. " + LOG.w("Filter.draw() called after destroying the filter. " +
"This can happen rarely because of threading."); "This can happen rarely because of threading.");
} else { } else {
@ -189,43 +164,18 @@ public abstract class BaseFilter implements Filter {
} }
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) { protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
// Copy the model / view / projection matrix over. program.setTextureTransform(transformMatrix);
GLES20.glUniformMatrix4fv(vertexModelViewProjectionMatrixLocation, 1, program.onPreDraw(programDrawable, programDrawable.getModelMatrix());
false, GlUtils.IDENTITY_MATRIX, 0);
GlUtils.checkError("glUniformMatrix4fv");
// Copy the texture transformation matrix over.
GLES20.glUniformMatrix4fv(vertexTransformMatrixLocation, 1,
false, transformMatrix, 0);
GlUtils.checkError("glUniformMatrix4fv");
// Enable the "aPosition" vertex attribute.
// Connect vertexBuffer to "aPosition".
GLES20.glEnableVertexAttribArray(vertexPositionLocation);
GlUtils.checkError("glEnableVertexAttribArray: " + vertexPositionLocation);
GLES20.glVertexAttribPointer(vertexPositionLocation, 2, GLES20.GL_FLOAT,
false, 8, vertexPosition);
GlUtils.checkError("glVertexAttribPointer");
// Enable the "aTextureCoord" vertex attribute.
// Connect texBuffer to "aTextureCoord".
GLES20.glEnableVertexAttribArray(vertexTextureCoordinateLocation);
GlUtils.checkError("glEnableVertexAttribArray");
GLES20.glVertexAttribPointer(vertexTextureCoordinateLocation, 2, GLES20.GL_FLOAT,
false, 8, textureCoordinates);
GlUtils.checkError("glVertexAttribPointer");
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected void onDraw(long timestampUs) { protected void onDraw(@SuppressWarnings("unused") long timestampUs) {
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); program.onDraw(programDrawable);
GlUtils.checkError("glDrawArrays");
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected void onPostDraw(long timestampUs) { protected void onPostDraw(@SuppressWarnings("unused") long timestampUs) {
GLES20.glDisableVertexAttribArray(vertexPositionLocation); program.onPostDraw(programDrawable);
GLES20.glDisableVertexAttribArray(vertexTextureCoordinateLocation);
} }
@NonNull @NonNull

@ -6,8 +6,10 @@ import android.opengl.GLES20;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import com.otaliastudios.opengl.core.Egloo;
import com.otaliastudios.opengl.program.GlProgram;
import com.otaliastudios.opengl.program.GlTextureProgram;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -42,13 +44,12 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
@VisibleForTesting @VisibleForTesting
static class State { static class State {
@VisibleForTesting boolean isCreated = false; @VisibleForTesting boolean isProgramCreated = false;
@VisibleForTesting boolean isFramebufferCreated = false; @VisibleForTesting boolean isFramebufferCreated = false;
@VisibleForTesting Size size = null; @VisibleForTesting Size size = null;
private int programHandle = -1; private int programHandle = -1;
private int framebufferId = -1; private int outputFramebufferId = -1;
private int textureId = -1; private int outputTextureId = -1;
} }
@VisibleForTesting final List<Filter> filters = new ArrayList<>(); @VisibleForTesting final List<Filter> filters = new ArrayList<>();
@ -105,48 +106,46 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
// with cleanup. Cleanup must happen on the GL thread so we'd have to wait // with cleanup. Cleanup must happen on the GL thread so we'd have to wait
// for new rendering call (which might not even happen). // for new rendering call (which might not even happen).
private void maybeCreate(@NonNull Filter filter, boolean isFirst) { private void maybeCreateProgram(@NonNull Filter filter, boolean isFirst, boolean isLast) {
State state = states.get(filter); State state = states.get(filter);
//noinspection ConstantConditions //noinspection ConstantConditions
if (!state.isCreated) { if (state.isProgramCreated) return;
state.isCreated = true; state.isProgramCreated = true;
String shader = filter.getFragmentShader();
if (!isFirst) {
// The first shader actually reads from a OES texture, but the others // The first shader actually reads from a OES texture, but the others
// will read from the 2d framebuffer texture. This is a dirty hack. // will read from the 2d framebuffer texture. This is a dirty hack.
shader = shader.replace("samplerExternalOES ", "sampler2D "); String fragmentShader = isFirst
} ? filter.getFragmentShader()
state.programHandle = GlUtils.createProgram(filter.getVertexShader(), shader); : filter.getFragmentShader().replace("samplerExternalOES ", "sampler2D ");
String vertexShader = filter.getVertexShader();
state.programHandle = GlProgram.create(vertexShader, fragmentShader);
filter.onCreate(state.programHandle); filter.onCreate(state.programHandle);
} }
}
private void maybeDestroy(@NonNull Filter filter) { private void maybeDestroyProgram(@NonNull Filter filter) {
State state = states.get(filter); State state = states.get(filter);
//noinspection ConstantConditions //noinspection ConstantConditions
if (state.isCreated) { if (!state.isProgramCreated) return;
state.isCreated = false; state.isProgramCreated = false;
filter.onDestroy(); filter.onDestroy();
GLES20.glDeleteProgram(state.programHandle); GLES20.glDeleteProgram(state.programHandle);
state.programHandle = -1; state.programHandle = -1;
} }
}
private void maybeCreateFramebuffer(@NonNull Filter filter, boolean isLast) { private void maybeCreateFramebuffer(@NonNull Filter filter, boolean isFirst, boolean isLast) {
if (isLast) return;
State state = states.get(filter); State state = states.get(filter);
//noinspection ConstantConditions //noinspection ConstantConditions
if (!state.isFramebufferCreated) { if (state.isFramebufferCreated || isLast) return;
state.isFramebufferCreated = true; state.isFramebufferCreated = true;
int[] framebufferArray = new int[1]; int[] framebufferArray = new int[1];
int[] textureArray = new int[1]; int[] textureArray = new int[1];
GLES20.glGenFramebuffers(1, framebufferArray, 0); GLES20.glGenFramebuffers(1, framebufferArray, 0);
GLES20.glGenTextures(1, textureArray, 0); GLES20.glGenTextures(1, textureArray, 0);
state.framebufferId = framebufferArray[0]; state.outputFramebufferId = framebufferArray[0];
state.textureId = textureArray[0]; state.outputTextureId = textureArray[0];
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.textureId); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.outputTextureId);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
state.size.getWidth(), state.size.getHeight(), 0, GLES20.GL_RGBA, state.size.getWidth(), state.size.getHeight(), 0, GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE, null); GLES20.GL_UNSIGNED_BYTE, null);
@ -158,11 +157,11 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
GLES20.GL_CLAMP_TO_EDGE); GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE); GLES20.GL_CLAMP_TO_EDGE);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.framebufferId); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.outputTextureId);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_2D,
state.textureId, state.outputTextureId,
0); 0);
int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
@ -173,18 +172,16 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
} }
}
private void maybeDestroyFramebuffer(@NonNull Filter filter) { private void maybeDestroyFramebuffer(@NonNull Filter filter) {
State state = states.get(filter); State state = states.get(filter);
//noinspection ConstantConditions //noinspection ConstantConditions
if (state.isFramebufferCreated) { if (!state.isFramebufferCreated) return;
state.isFramebufferCreated = false; state.isFramebufferCreated = false;
GLES20.glDeleteFramebuffers(1, new int[]{state.framebufferId}, 0); GLES20.glDeleteFramebuffers(1, new int[]{state.outputFramebufferId}, 0);
state.framebufferId = -1; state.outputFramebufferId = -1;
GLES20.glDeleteTextures(1, new int[]{state.textureId}, 0); GLES20.glDeleteTextures(1, new int[]{state.outputTextureId}, 0);
state.textureId = -1; state.outputTextureId = -1;
}
} }
private void maybeSetSize(@NonNull Filter filter) { private void maybeSetSize(@NonNull Filter filter) {
@ -196,24 +193,24 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
} }
} }
@Override
public void onCreate(int programHandle) {
// We'll create children during the draw() op, since some of them
// might have been added after this onCreate() is called.
}
@NonNull @NonNull
@Override @Override
public String getVertexShader() { public String getVertexShader() {
// Whatever, we won't be using this. // Whatever, we won't be using this.
return new NoFilter().getVertexShader(); return GlTextureProgram.SIMPLE_VERTEX_SHADER;
} }
@NonNull @NonNull
@Override @Override
public String getFragmentShader() { public String getFragmentShader() {
// Whatever, we won't be using this. // Whatever, we won't be using this.
return new NoFilter().getFragmentShader(); return GlTextureProgram.SIMPLE_FRAGMENT_SHADER;
}
@Override
public void onCreate(int programHandle) {
// We'll create children during the draw() op, since some of them
// might have been added after this onCreate() is called.
} }
@Override @Override
@ -221,7 +218,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
synchronized (lock) { synchronized (lock) {
for (Filter filter : filters) { for (Filter filter : filters) {
maybeDestroyFramebuffer(filter); maybeDestroyFramebuffer(filter);
maybeDestroy(filter); maybeDestroyProgram(filter);
} }
} }
} }
@ -245,8 +242,8 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
Filter filter = filters.get(i); Filter filter = filters.get(i);
State state = states.get(filter); State state = states.get(filter);
maybeSetSize(filter); maybeSetSize(filter);
maybeCreate(filter, isFirst); maybeCreateProgram(filter, isFirst, isLast);
maybeCreateFramebuffer(filter, isLast); maybeCreateFramebuffer(filter, isFirst, isLast);
//noinspection ConstantConditions //noinspection ConstantConditions
GLES20.glUseProgram(state.programHandle); GLES20.glUseProgram(state.programHandle);
@ -255,7 +252,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
// Each filter outputs into its own framebuffer object, except the // Each filter outputs into its own framebuffer object, except the
// last filter, which outputs into the default framebuffer. // last filter, which outputs into the default framebuffer.
if (!isLast) { if (!isLast) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.framebufferId); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.outputFramebufferId);
GLES20.glClearColor(0, 0, 0, 0); GLES20.glClearColor(0, 0, 0, 0);
} else { } else {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
@ -267,14 +264,14 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
if (isFirst) { if (isFirst) {
filter.draw(timestampUs, transformMatrix); filter.draw(timestampUs, transformMatrix);
} else { } else {
filter.draw(timestampUs, GlUtils.IDENTITY_MATRIX); filter.draw(timestampUs, Egloo.IDENTITY_MATRIX);
} }
// Set the input for the next cycle: // Set the input for the next cycle:
// It is the framebuffer texture from this cycle. If this is the last // It is the framebuffer texture from this cycle. If this is the last
// filter, reset this value just to cleanup. // filter, reset this value just to cleanup.
if (!isLast) { if (!isLast) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.textureId); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.outputTextureId);
} else { } else {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Attempts to auto-fix the frames based on histogram equalization. * Attempts to auto-fix the frames based on histogram equalization.
@ -107,7 +107,7 @@ public class AutoFixFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale"); Egloo.checkGlProgramLocation(scaleLocation, "scale");
} }
@Override @Override
@ -120,6 +120,6 @@ public class AutoFixFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) { protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix); super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(scaleLocation, scale); GLES20.glUniform1f(scaleLocation, scale);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Adjusts the brightness of the frames. * Adjusts the brightness of the frames.
@ -76,7 +76,7 @@ public class BrightnessFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
brightnessLocation = GLES20.glGetUniformLocation(programHandle, "brightness"); brightnessLocation = GLES20.glGetUniformLocation(programHandle, "brightness");
GlUtils.checkLocation(brightnessLocation, "brightness"); Egloo.checkGlProgramLocation(brightnessLocation, "brightness");
} }
@Override @Override
@ -89,6 +89,6 @@ public class BrightnessFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) { protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix); super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(brightnessLocation, brightness); GLES20.glUniform1f(brightnessLocation, brightness);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Adjusts the contrast. * Adjusts the contrast.
@ -78,7 +78,7 @@ public class ContrastFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
contrastLocation = GLES20.glGetUniformLocation(programHandle, "contrast"); contrastLocation = GLES20.glGetUniformLocation(programHandle, "contrast");
GlUtils.checkLocation(contrastLocation, "contrast"); Egloo.checkGlProgramLocation(contrastLocation, "contrast");
} }
@Override @Override
@ -91,6 +91,6 @@ public class ContrastFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) { protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix); super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(contrastLocation, contrast); GLES20.glUniform1f(contrastLocation, contrast);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -5,7 +5,7 @@ import android.opengl.GLES20;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
import java.util.Random; import java.util.Random;
@ -80,9 +80,9 @@ public class DocumentaryFilter extends BaseFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
mScaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); mScaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(mScaleLocation, "scale"); Egloo.checkGlProgramLocation(mScaleLocation, "scale");
mMaxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist"); mMaxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist");
GlUtils.checkLocation(mMaxDistLocation, "inv_max_dist"); Egloo.checkGlProgramLocation(mMaxDistLocation, "inv_max_dist");
} }
@Override @Override
@ -104,12 +104,12 @@ public class DocumentaryFilter extends BaseFilter {
scale[1] = 1f; scale[1] = 1f;
} }
GLES20.glUniform2fv(mScaleLocation, 1, scale, 0); GLES20.glUniform2fv(mScaleLocation, 1, scale, 0);
GlUtils.checkError("glUniform2fv"); Egloo.checkGlError("glUniform2fv");
float maxDist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] * scale[1])) * 0.5f; float maxDist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] * scale[1])) * 0.5f;
float invMaxDist = 1F / maxDist; float invMaxDist = 1F / maxDist;
GLES20.glUniform1f(mMaxDistLocation, invMaxDist); GLES20.glUniform1f(mMaxDistLocation, invMaxDist);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.TwoParameterFilter; import com.otaliastudios.cameraview.filter.TwoParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Representation of input frames using only two color tones. * Representation of input frames using only two color tones.
@ -131,9 +131,9 @@ public class DuotoneFilter extends BaseFilter implements TwoParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
mFirstColorLocation = GLES20.glGetUniformLocation(programHandle, "first"); mFirstColorLocation = GLES20.glGetUniformLocation(programHandle, "first");
GlUtils.checkLocation(mFirstColorLocation, "first"); Egloo.checkGlProgramLocation(mFirstColorLocation, "first");
mSecondColorLocation = GLES20.glGetUniformLocation(programHandle, "second"); mSecondColorLocation = GLES20.glGetUniformLocation(programHandle, "second");
GlUtils.checkLocation(mSecondColorLocation, "second"); Egloo.checkGlProgramLocation(mSecondColorLocation, "second");
} }
@Override @Override
@ -150,9 +150,9 @@ public class DuotoneFilter extends BaseFilter implements TwoParameterFilter {
Color.blue(mSecondColor) / 255f Color.blue(mSecondColor) / 255f
}; };
GLES20.glUniform3fv(mFirstColorLocation, 1, first, 0); GLES20.glUniform3fv(mFirstColorLocation, 1, first, 0);
GlUtils.checkError("glUniform3fv"); Egloo.checkGlError("glUniform3fv");
GLES20.glUniform3fv(mSecondColorLocation, 1, second, 0); GLES20.glUniform3fv(mSecondColorLocation, 1, second, 0);
GlUtils.checkError("glUniform3fv"); Egloo.checkGlError("glUniform3fv");
} }
@Override @Override

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Applies back-light filling to the frames. * Applies back-light filling to the frames.
@ -82,9 +82,9 @@ public class FillLightFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
multiplierLocation = GLES20.glGetUniformLocation(programHandle, "mult"); multiplierLocation = GLES20.glGetUniformLocation(programHandle, "mult");
GlUtils.checkLocation(multiplierLocation, "mult"); Egloo.checkGlProgramLocation(multiplierLocation, "mult");
gammaLocation = GLES20.glGetUniformLocation(programHandle, "igamma"); gammaLocation = GLES20.glGetUniformLocation(programHandle, "igamma");
GlUtils.checkLocation(gammaLocation, "igamma"); Egloo.checkGlProgramLocation(gammaLocation, "igamma");
} }
@Override @Override
@ -100,12 +100,12 @@ public class FillLightFilter extends BaseFilter implements OneParameterFilter {
float amount = 1.0f - strength; float amount = 1.0f - strength;
float multiplier = 1.0f / (amount * 0.7f + 0.3f); float multiplier = 1.0f / (amount * 0.7f + 0.3f);
GLES20.glUniform1f(multiplierLocation, multiplier); GLES20.glUniform1f(multiplierLocation, multiplier);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
float fadeGamma = 0.3f; float fadeGamma = 0.3f;
float faded = fadeGamma + (1.0f - fadeGamma) * multiplier; float faded = fadeGamma + (1.0f - fadeGamma) * multiplier;
float gamma = 1.0f / faded; float gamma = 1.0f / faded;
GLES20.glUniform1f(gammaLocation, gamma); GLES20.glUniform1f(gammaLocation, gamma);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Applies gamma correction to the frames. * Applies gamma correction to the frames.
@ -73,7 +73,7 @@ public class GammaFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
gammaLocation = GLES20.glGetUniformLocation(programHandle, "gamma"); gammaLocation = GLES20.glGetUniformLocation(programHandle, "gamma");
GlUtils.checkLocation(gammaLocation, "gamma"); Egloo.checkGlProgramLocation(gammaLocation, "gamma");
} }
@Override @Override
@ -86,6 +86,6 @@ public class GammaFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) { protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix); super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(gammaLocation, gamma); GLES20.glUniform1f(gammaLocation, gamma);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
import java.util.Random; import java.util.Random;
@ -122,11 +122,11 @@ public class GrainFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
strengthLocation = GLES20.glGetUniformLocation(programHandle, "scale"); strengthLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(strengthLocation, "scale"); Egloo.checkGlProgramLocation(strengthLocation, "scale");
stepXLocation = GLES20.glGetUniformLocation(programHandle, "stepX"); stepXLocation = GLES20.glGetUniformLocation(programHandle, "stepX");
GlUtils.checkLocation(stepXLocation, "stepX"); Egloo.checkGlProgramLocation(stepXLocation, "stepX");
stepYLocation = GLES20.glGetUniformLocation(programHandle, "stepY"); stepYLocation = GLES20.glGetUniformLocation(programHandle, "stepY");
GlUtils.checkLocation(stepYLocation, "stepY"); Egloo.checkGlProgramLocation(stepYLocation, "stepY");
} }
@Override @Override
@ -141,10 +141,10 @@ public class GrainFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) { protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix); super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(strengthLocation, strength); GLES20.glUniform1f(strengthLocation, strength);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepXLocation, 0.5f / width); GLES20.glUniform1f(stepXLocation, 0.5f / width);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepYLocation, 0.5f / height); GLES20.glUniform1f(stepYLocation, 0.5f / height);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Applies a hue effect on the input frames. * Applies a hue effect on the input frames.
@ -86,7 +86,7 @@ public class HueFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
hueLocation = GLES20.glGetUniformLocation(programHandle, "hue"); hueLocation = GLES20.glGetUniformLocation(programHandle, "hue");
GlUtils.checkLocation(hueLocation, "hue"); Egloo.checkGlProgramLocation(hueLocation, "hue");
} }
@Override @Override
@ -101,6 +101,6 @@ public class HueFilter extends BaseFilter implements OneParameterFilter {
// map it on 360 degree circle // map it on 360 degree circle
float shaderHue = ((hue - 45) / 45f + 0.5f) * -1; float shaderHue = ((hue - 45) / 45f + 0.5f) * -1;
GLES20.glUniform1f(hueLocation, shaderHue); GLES20.glUniform1f(hueLocation, shaderHue);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -5,7 +5,7 @@ import android.opengl.GLES20;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
import java.util.Random; import java.util.Random;
@ -122,13 +122,13 @@ public class LomoishFilter extends BaseFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale"); Egloo.checkGlProgramLocation(scaleLocation, "scale");
maxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist"); maxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist");
GlUtils.checkLocation(maxDistLocation, "inv_max_dist"); Egloo.checkGlProgramLocation(maxDistLocation, "inv_max_dist");
stepSizeXLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeX"); stepSizeXLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeX");
GlUtils.checkLocation(stepSizeXLocation, "stepsizeX"); Egloo.checkGlProgramLocation(stepSizeXLocation, "stepsizeX");
stepSizeYLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeY"); stepSizeYLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeY");
GlUtils.checkLocation(stepSizeYLocation, "stepsizeY"); Egloo.checkGlProgramLocation(stepSizeYLocation, "stepsizeY");
} }
@Override @Override
@ -153,12 +153,12 @@ public class LomoishFilter extends BaseFilter {
} }
float maxDist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] * scale[1])) * 0.5f; float maxDist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] * scale[1])) * 0.5f;
GLES20.glUniform2fv(scaleLocation, 1, scale, 0); GLES20.glUniform2fv(scaleLocation, 1, scale, 0);
GlUtils.checkError("glUniform2fv"); Egloo.checkGlError("glUniform2fv");
GLES20.glUniform1f(maxDistLocation, 1.0F / maxDist); GLES20.glUniform1f(maxDistLocation, 1.0F / maxDist);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepSizeXLocation, 1.0F / width); GLES20.glUniform1f(stepSizeXLocation, 1.0F / width);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepSizeYLocation, 1.0F / height); GLES20.glUniform1f(stepSizeYLocation, 1.0F / height);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Adjusts color saturation. * Adjusts color saturation.
@ -93,9 +93,9 @@ public class SaturationFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale"); Egloo.checkGlProgramLocation(scaleLocation, "scale");
exponentsLocation = GLES20.glGetUniformLocation(programHandle, "exponents"); exponentsLocation = GLES20.glGetUniformLocation(programHandle, "exponents");
GlUtils.checkLocation(exponentsLocation, "exponents"); Egloo.checkGlProgramLocation(exponentsLocation, "exponents");
} }
@Override @Override
@ -110,18 +110,18 @@ public class SaturationFilter extends BaseFilter implements OneParameterFilter {
super.onPreDraw(timestampUs, transformMatrix); super.onPreDraw(timestampUs, transformMatrix);
if (scale > 0.0f) { if (scale > 0.0f) {
GLES20.glUniform1f(scaleLocation, 0F); GLES20.glUniform1f(scaleLocation, 0F);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform3f(exponentsLocation, GLES20.glUniform3f(exponentsLocation,
(0.9f * scale) + 1.0f, (0.9f * scale) + 1.0f,
(2.1f * scale) + 1.0f, (2.1f * scale) + 1.0f,
(2.7f * scale) + 1.0f (2.7f * scale) + 1.0f
); );
GlUtils.checkError("glUniform3f"); Egloo.checkGlError("glUniform3f");
} else { } else {
GLES20.glUniform1f(scaleLocation, 1.0F + scale); GLES20.glUniform1f(scaleLocation, 1.0F + scale);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform3f(exponentsLocation, 0F, 0F, 0F); GLES20.glUniform3f(exponentsLocation, 0F, 0F, 0F);
GlUtils.checkError("glUniform3f"); Egloo.checkGlError("glUniform3f");
} }
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Sharpens the input frames. * Sharpens the input frames.
@ -100,11 +100,11 @@ public class SharpnessFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale"); Egloo.checkGlProgramLocation(scaleLocation, "scale");
stepSizeXLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeX"); stepSizeXLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeX");
GlUtils.checkLocation(stepSizeXLocation, "stepsizeX"); Egloo.checkGlProgramLocation(stepSizeXLocation, "stepsizeX");
stepSizeYLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeY"); stepSizeYLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeY");
GlUtils.checkLocation(stepSizeYLocation, "stepsizeY"); Egloo.checkGlProgramLocation(stepSizeYLocation, "stepsizeY");
} }
@Override @Override
@ -119,10 +119,10 @@ public class SharpnessFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) { protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix); super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(scaleLocation, scale); GLES20.glUniform1f(scaleLocation, scale);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepSizeXLocation, 1.0F / width); GLES20.glUniform1f(stepSizeXLocation, 1.0F / width);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepSizeYLocation, 1.0F / height); GLES20.glUniform1f(stepSizeYLocation, 1.0F / height);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
* Adjusts color temperature. * Adjusts color temperature.
@ -84,7 +84,7 @@ public class TemperatureFilter extends BaseFilter implements OneParameterFilter
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale"); Egloo.checkGlProgramLocation(scaleLocation, "scale");
} }
@Override @Override
@ -97,6 +97,6 @@ public class TemperatureFilter extends BaseFilter implements OneParameterFilter
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) { protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix); super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(scaleLocation, scale); GLES20.glUniform1f(scaleLocation, scale);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.OneParameterFilter; import com.otaliastudios.cameraview.filter.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
@ -81,7 +81,7 @@ public class TintFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
tintLocation = GLES20.glGetUniformLocation(programHandle, "tint"); tintLocation = GLES20.glGetUniformLocation(programHandle, "tint");
GlUtils.checkLocation(tintLocation, "tint"); Egloo.checkGlProgramLocation(tintLocation, "tint");
} }
@Override @Override
@ -99,6 +99,6 @@ public class TintFilter extends BaseFilter implements OneParameterFilter {
Color.blue(tint) / 255f Color.blue(tint) / 255f
}; };
GLES20.glUniform3fv(tintLocation, 1, channels, 0); GLES20.glUniform3fv(tintLocation, 1, channels, 0);
GlUtils.checkError("glUniform3fv"); Egloo.checkGlError("glUniform3fv");
} }
} }

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter; import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.filter.TwoParameterFilter; import com.otaliastudios.cameraview.filter.TwoParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.core.Egloo;
/** /**
@ -126,13 +126,13 @@ public class VignetteFilter extends BaseFilter implements TwoParameterFilter {
public void onCreate(int programHandle) { public void onCreate(int programHandle) {
super.onCreate(programHandle); super.onCreate(programHandle);
mRangeLocation = GLES20.glGetUniformLocation(programHandle, "range"); mRangeLocation = GLES20.glGetUniformLocation(programHandle, "range");
GlUtils.checkLocation(mRangeLocation, "range"); Egloo.checkGlProgramLocation(mRangeLocation, "range");
mMaxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist"); mMaxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist");
GlUtils.checkLocation(mMaxDistLocation, "inv_max_dist"); Egloo.checkGlProgramLocation(mMaxDistLocation, "inv_max_dist");
mShadeLocation = GLES20.glGetUniformLocation(programHandle, "shade"); mShadeLocation = GLES20.glGetUniformLocation(programHandle, "shade");
GlUtils.checkLocation(mShadeLocation, "shade"); Egloo.checkGlProgramLocation(mShadeLocation, "shade");
mScaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); mScaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(mScaleLocation, "scale"); Egloo.checkGlProgramLocation(mScaleLocation, "scale");
} }
@Override @Override
@ -156,20 +156,20 @@ public class VignetteFilter extends BaseFilter implements TwoParameterFilter {
scale[1] = 1f; scale[1] = 1f;
} }
GLES20.glUniform2fv(mScaleLocation, 1, scale, 0); GLES20.glUniform2fv(mScaleLocation, 1, scale, 0);
GlUtils.checkError("glUniform2fv"); Egloo.checkGlError("glUniform2fv");
float maxDist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] * scale[1])) * 0.5f; float maxDist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] * scale[1])) * 0.5f;
GLES20.glUniform1f(mMaxDistLocation, 1F / maxDist); GLES20.glUniform1f(mMaxDistLocation, 1F / maxDist);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(mShadeLocation, mShade); GLES20.glUniform1f(mShadeLocation, mShade);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
// The 'range' is between 1.3 to 0.6. When scale is zero then range is 1.3 // The 'range' is between 1.3 to 0.6. When scale is zero then range is 1.3
// which means no vignette at all because the luminousity difference is // which means no vignette at all because the luminousity difference is
// less than 1/256 and will cause nothing. // less than 1/256 and will cause nothing.
float range = (1.30f - (float) Math.sqrt(mScale) * 0.7f); float range = (1.30f - (float) Math.sqrt(mScale) * 0.7f);
GLES20.glUniform1f(mRangeLocation, range); GLES20.glUniform1f(mRangeLocation, range);
GlUtils.checkError("glUniform1f"); Egloo.checkGlError("glUniform1f");
} }
} }

@ -0,0 +1,96 @@
package com.otaliastudios.cameraview.internal;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.NoFilter;
import com.otaliastudios.opengl.core.Egloo;
import com.otaliastudios.opengl.program.GlProgram;
import com.otaliastudios.opengl.texture.GlTexture;
public class GlTextureDrawer {
private final static String TAG = GlTextureDrawer.class.getSimpleName();
private final static CameraLogger LOG = CameraLogger.create(TAG);
private final static int TEXTURE_TARGET = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
private final static int TEXTURE_UNIT = GLES20.GL_TEXTURE0;
private final GlTexture mTexture;
private final float[] mTextureTransform = Egloo.IDENTITY_MATRIX.clone();
@NonNull
private Filter mFilter = new NoFilter();
private Filter mPendingFilter = null;
private int mProgramHandle = -1;
@SuppressWarnings("unused")
public GlTextureDrawer() {
this(new GlTexture(TEXTURE_UNIT, TEXTURE_TARGET));
}
@SuppressWarnings("unused")
public GlTextureDrawer(int textureId) {
this(new GlTexture(TEXTURE_UNIT, TEXTURE_TARGET, textureId));
}
@SuppressWarnings("WeakerAccess")
public GlTextureDrawer(@NonNull GlTexture texture) {
mTexture = texture;
}
public void setFilter(@NonNull Filter filter) {
mPendingFilter = filter;
}
@NonNull
public GlTexture getTexture() {
return mTexture;
}
@NonNull
public float[] getTextureTransform() {
return mTextureTransform;
}
public void draw(long timestampUs) {
if (mPendingFilter != null) {
release();
mFilter = mPendingFilter;
mPendingFilter = null;
}
if (mProgramHandle == -1) {
mProgramHandle = GlProgram.create(
mFilter.getVertexShader(),
mFilter.getFragmentShader());
mFilter.onCreate(mProgramHandle);
}
Egloo.checkGlError("draw start");
// Select the program and the active texture.
GLES20.glUseProgram(mProgramHandle);
Egloo.checkGlError("glUseProgram");
GLES20.glActiveTexture(mTexture.getUnit());
GLES20.glBindTexture(mTexture.getTarget(), mTexture.getId());
// Draw and release.
mFilter.draw(timestampUs, mTextureTransform);
GLES20.glBindTexture(mTexture.getTarget(), 0);
GLES20.glUseProgram(0);
Egloo.checkGlError("draw end");
}
public void release() {
if (mProgramHandle == -1) return;
mFilter.onDestroy();
GLES20.glDeleteProgram(mProgramHandle);
mProgramHandle = -1;
}
}

@ -1,96 +0,0 @@
package com.otaliastudios.cameraview.internal;
import android.opengl.GLES20;
import android.opengl.Matrix;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.CameraLogger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class GlUtils {
private final static String TAG = GlUtils.class.getSimpleName();
private final static CameraLogger LOG = CameraLogger.create(TAG);
// Identity matrix for general use.
public static final float[] IDENTITY_MATRIX = new float[16];
static {
Matrix.setIdentityM(IDENTITY_MATRIX, 0);
}
public static void checkError(@NonNull String opName) {
int error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
String message = LOG.e("Error during", opName, "glError 0x",
Integer.toHexString(error));
throw new RuntimeException(message);
}
}
public static void checkLocation(int location, @NonNull String name) {
if (location < 0) {
String message = LOG.e("Unable to locate", name, "in program");
throw new RuntimeException(message);
}
}
// Compiles the given shader, returns a handle.
@SuppressWarnings("WeakerAccess")
public static int loadShader(int shaderType, @NonNull String source) {
int shader = GLES20.glCreateShader(shaderType);
checkError("glCreateShader type=" + shaderType);
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
LOG.e("Could not compile shader", shaderType, ":",
GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
return shader;
}
// Creates a program with given vertex shader and pixel shader.
public static int createProgram(@NonNull String vertexSource, @NonNull String fragmentSource) {
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) return 0;
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) return 0;
int program = GLES20.glCreateProgram();
checkError("glCreateProgram");
if (program == 0) {
LOG.e("Could not create program");
}
GLES20.glAttachShader(program, vertexShader);
checkError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE) {
LOG.e("Could not link program:", GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
return program;
}
// Allocates a direct float buffer, and populates it with the float array data.
public static FloatBuffer floatBuffer(@NonNull float[] coords) {
// Allocate a direct ByteBuffer, using 4 bytes per float, and copy coords into it.
ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * 4);
bb.order(ByteOrder.nativeOrder());
FloatBuffer fb = bb.asFloatBuffer();
fb.put(coords);
fb.position(0);
return fb;
}
}

@ -25,7 +25,6 @@ import androidx.annotation.RequiresApi;
import android.util.Log; import android.util.Log;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.internal.GlUtils;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -188,7 +187,7 @@ public class EglBaseSurface {
buf.order(ByteOrder.LITTLE_ENDIAN); buf.order(ByteOrder.LITTLE_ENDIAN);
GLES20.glReadPixels(0, 0, width, height, GLES20.glReadPixels(0, 0, width, height,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf); GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
GlUtils.checkError("glReadPixels"); Egloo.checkGlError("glReadPixels");
buf.rewind(); buf.rewind();
BufferedOutputStream bos = null; BufferedOutputStream bos = null;
@ -231,7 +230,7 @@ public class EglBaseSurface {
buf.order(ByteOrder.LITTLE_ENDIAN); buf.order(ByteOrder.LITTLE_ENDIAN);
GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
buf); buf);
GlUtils.checkError("glReadPixels"); Egloo.checkGlError("glReadPixels");
buf.rewind(); buf.rewind();
ByteArrayOutputStream bos = new ByteArrayOutputStream(buf.array().length); ByteArrayOutputStream bos = new ByteArrayOutputStream(buf.array().length);

@ -9,7 +9,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.filter.Filter; import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.NoFilter; import com.otaliastudios.cameraview.filter.NoFilter;
import com.otaliastudios.cameraview.internal.GlUtils; import com.otaliastudios.opengl.program.GlProgram;
public class EglViewport { public class EglViewport {
@ -35,7 +35,7 @@ public class EglViewport {
} }
private void createProgram() { private void createProgram() {
mProgramHandle = GlUtils.createProgram(mFilter.getVertexShader(), mProgramHandle = GlProgram.create(mFilter.getVertexShader(),
mFilter.getFragmentShader()); mFilter.getFragmentShader());
mFilter.onCreate(mProgramHandle); mFilter.onCreate(mProgramHandle);
} }
@ -51,12 +51,12 @@ public class EglViewport {
public int createTexture() { public int createTexture() {
int[] textures = new int[1]; int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0); GLES20.glGenTextures(1, textures, 0);
GlUtils.checkError("glGenTextures"); Egloo.checkGlError("glGenTextures");
int texId = textures[0]; int texId = textures[0];
GLES20.glActiveTexture(mTextureUnit); GLES20.glActiveTexture(mTextureUnit);
GLES20.glBindTexture(mTextureTarget, texId); GLES20.glBindTexture(mTextureTarget, texId);
GlUtils.checkError("glBindTexture " + texId); Egloo.checkGlError("glBindTexture " + texId);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST); GLES20.GL_NEAREST);
@ -66,7 +66,7 @@ public class EglViewport {
GLES20.GL_CLAMP_TO_EDGE); GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE); GLES20.GL_CLAMP_TO_EDGE);
GlUtils.checkError("glTexParameter"); Egloo.checkGlError("glTexParameter");
return texId; return texId;
} }
@ -77,7 +77,7 @@ public class EglViewport {
mPendingFilter = filter; mPendingFilter = filter;
} }
public void drawFrame(long timestampUs, int textureId, float[] textureMatrix) { public void draw(long timestampUs, int textureId, float[] textureMatrix) {
if (mPendingFilter != null) { if (mPendingFilter != null) {
release(); release();
mFilter = mPendingFilter; mFilter = mPendingFilter;
@ -85,11 +85,11 @@ public class EglViewport {
createProgram(); createProgram();
} }
GlUtils.checkError("draw start"); Egloo.checkGlError("draw start");
// Select the program and the active texture. // Select the program and the active texture.
GLES20.glUseProgram(mProgramHandle); GLES20.glUseProgram(mProgramHandle);
GlUtils.checkError("glUseProgram"); Egloo.checkGlError("glUseProgram");
GLES20.glActiveTexture(mTextureUnit); GLES20.glActiveTexture(mTextureUnit);
GLES20.glBindTexture(mTextureTarget, textureId); GLES20.glBindTexture(mTextureTarget, textureId);

@ -105,7 +105,7 @@ public class OverlayDrawer {
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
synchronized (mIssue514WorkaroundLock) { synchronized (mIssue514WorkaroundLock) {
mViewport.drawFrame(timestampUs, mTextureId, mTransform); mViewport.draw(timestampUs, mTextureId, mTransform);
} }
} }

@ -232,7 +232,7 @@ public class SnapshotGlPictureRecorder extends SnapshotPictureRecorder {
// 5. Draw and save // 5. Draw and save
long timestampUs = surfaceTexture.getTimestamp() / 1000L; long timestampUs = surfaceTexture.getTimestamp() / 1000L;
LOG.i("takeFrame:", "timestampUs:", timestampUs); LOG.i("takeFrame:", "timestampUs:", timestampUs);
mViewport.drawFrame(timestampUs, mTextureId, mTransform); mViewport.draw(timestampUs, mTextureId, mTransform);
if (mHasOverlay) mOverlayDrawer.render(timestampUs); if (mHasOverlay) mOverlayDrawer.render(timestampUs);
mResult.data = eglSurface.saveFrameTo(Bitmap.CompressFormat.JPEG); mResult.data = eglSurface.saveFrameTo(Bitmap.CompressFormat.JPEG);

@ -231,7 +231,7 @@ public class GlCameraPreview extends FilterCameraPreview<GLSurfaceView, SurfaceT
Matrix.translateM(mTransformMatrix, 0, translX, translY, 0); Matrix.translateM(mTransformMatrix, 0, translX, translY, 0);
Matrix.scaleM(mTransformMatrix, 0, mCropScaleX, mCropScaleY, 1); Matrix.scaleM(mTransformMatrix, 0, mCropScaleX, mCropScaleY, 1);
} }
mOutputViewport.drawFrame(mInputSurfaceTexture.getTimestamp() / 1000L, mOutputViewport.draw(mInputSurfaceTexture.getTimestamp() / 1000L,
mOutputTextureId, mTransformMatrix); mOutputTextureId, mTransformMatrix);
for (RendererFrameCallback callback : mRendererFrameCallbacks) { for (RendererFrameCallback callback : mRendererFrameCallbacks) {
callback.onRendererFrame(mInputSurfaceTexture, mCropScaleX, mCropScaleY); callback.onRendererFrame(mInputSurfaceTexture, mCropScaleX, mCropScaleY);

@ -229,7 +229,7 @@ public class TextureMediaEncoder extends VideoMediaEncoder<TextureConfig> {
"hasReachedMaxLength:", hasReachedMaxLength(), "hasReachedMaxLength:", hasReachedMaxLength(),
"thread:", Thread.currentThread(), "thread:", Thread.currentThread(),
"- gl rendering."); "- gl rendering.");
mViewport.drawFrame(frame.timestampUs(), mConfig.textureId, transform); mViewport.draw(frame.timestampUs(), mConfig.textureId, transform);
if (mConfig.hasOverlay()) { if (mConfig.hasOverlay()) {
mConfig.overlayDrawer.render(frame.timestampUs()); mConfig.overlayDrawer.render(frame.timestampUs());
} }

@ -1 +1,3 @@
include ':demo', ':cameraview' include ':demo', ':cameraview'
include ':egloo'
project(':egloo').projectDir = file('../Egloo/library')

Loading…
Cancel
Save