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.github.dcendents:android-maven-gradle-plugin:2.1'
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 {

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

@ -8,16 +8,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.otaliastudios.cameraview.BaseEglTest;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.opengl.program.GlTextureProgram;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@ -91,11 +91,11 @@ public class BaseFilterTest extends BaseEglTest {
@Test
public void testOnProgramCreate() {
filter = new TestFilter();
int handle = GlUtils.createProgram(filter.getVertexShader(), filter.getFragmentShader());
int handle = GlTextureProgram.create(filter.getVertexShader(), filter.getFragmentShader());
filter.onCreate(handle);
assertTrue(filter.programHandle >= 0);
assertNotNull(filter.program);
filter.onDestroy();
assertTrue(filter.programHandle < 0);
assertNull(filter.program);
GLES20.glDeleteProgram(handle);
}
@ -117,7 +117,7 @@ public class BaseFilterTest extends BaseEglTest {
int texture = viewport.createTexture();
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)).onDraw(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.DuotoneFilter;
import com.otaliastudios.cameraview.filters.VignetteFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.opengl.program.GlProgram;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -127,7 +127,7 @@ public class MultiFilterTest extends BaseEglTest {
DuotoneFilter filter = spy(new DuotoneFilter());
MultiFilter multiFilter = new MultiFilter(filter);
int program = GlUtils.createProgram(multiFilter.getVertexShader(),
int program = GlProgram.create(multiFilter.getVertexShader(),
multiFilter.getFragmentShader());
multiFilter.onCreate(program);
verify(filter, never()).onCreate(anyInt());
@ -145,7 +145,7 @@ public class MultiFilterTest extends BaseEglTest {
EglViewport viewport = new EglViewport(multiFilter);
int texture = viewport.createTexture();
float[] matrix = new float[16];
viewport.drawFrame(0L, texture, matrix);
viewport.draw(0L, texture, matrix);
viewport.release();
// The child should have experienced the whole lifecycle.
@ -173,7 +173,7 @@ public class MultiFilterTest extends BaseEglTest {
public Object answer(InvocationOnMock invocation) {
MultiFilter.State state = multiFilter.states.get(filter1);
assertNotNull(state);
assertTrue(state.isCreated);
assertTrue(state.isProgramCreated);
assertTrue(state.isFramebufferCreated);
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0);
@ -189,7 +189,7 @@ public class MultiFilterTest extends BaseEglTest {
// The last filter has no FBO / texture.
MultiFilter.State state = multiFilter.states.get(filter2);
assertNotNull(state);
assertTrue(state.isCreated);
assertTrue(state.isProgramCreated);
assertFalse(state.isFramebufferCreated);
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, result, 0);
@ -201,7 +201,7 @@ public class MultiFilterTest extends BaseEglTest {
EglViewport viewport = new EglViewport(multiFilter);
int texture = viewport.createTexture();
viewport.drawFrame(0L, texture, matrix);
viewport.draw(0L, texture, matrix);
viewport.release();
// Verify that both are drawn.

@ -1,48 +1,29 @@
package com.otaliastudios.cameraview.overlay;
import android.content.res.XmlResourceParser;
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.filters.SmallTest;
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.size.Size;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
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.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.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(AndroidJUnit4.class)
@SmallTest
@ -82,7 +63,7 @@ public class OverlayDrawerTest extends BaseEglTest {
drawer.mViewport = spy(drawer.mViewport);
drawer.draw(Overlay.Target.PICTURE_SNAPSHOT);
drawer.render(0L);
verify(drawer.mViewport, times(1)).drawFrame(
verify(drawer.mViewport, times(1)).draw(
0L,
drawer.mTextureId,
drawer.getTransform()

@ -1,15 +1,14 @@
package com.otaliastudios.cameraview.filter;
import android.opengl.GLES20;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.cameraview.size.Size;
import java.nio.FloatBuffer;
import com.otaliastudios.opengl.draw.GlDrawable;
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.
@ -90,26 +89,8 @@ public abstract class BaseFilter implements Filter {
+ "}\n";
}
// When the model/view/projection matrix is identity, this will exactly cover the viewport.
private FloatBuffer vertexPosition = GlUtils.floatBuffer(new float[]{
-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 GlTextureProgram program = null;
private final GlDrawable programDrawable = new GlRect();
@VisibleForTesting Size size;
@SuppressWarnings("WeakerAccess")
@ -141,28 +122,22 @@ public abstract class BaseFilter implements Filter {
@Override
public void onCreate(int programHandle) {
this.programHandle = programHandle;
vertexPositionLocation = GLES20.glGetAttribLocation(programHandle, vertexPositionName);
GlUtils.checkLocation(vertexPositionLocation, vertexPositionName);
vertexTextureCoordinateLocation = GLES20.glGetAttribLocation(programHandle,
vertexTextureCoordinateName);
GlUtils.checkLocation(vertexTextureCoordinateLocation, vertexTextureCoordinateName);
vertexModelViewProjectionMatrixLocation = GLES20.glGetUniformLocation(programHandle,
vertexModelViewProjectionMatrixName);
GlUtils.checkLocation(vertexModelViewProjectionMatrixLocation,
vertexModelViewProjectionMatrixName);
vertexTransformMatrixLocation = GLES20.glGetUniformLocation(programHandle,
vertexTransformMatrixName);
GlUtils.checkLocation(vertexTransformMatrixLocation, vertexTransformMatrixName);
program = new GlTextureProgram(
programHandle,
vertexPositionName,
vertexModelViewProjectionMatrixName,
vertexTextureCoordinateName,
vertexTransformMatrixName
);
}
@Override
public void onDestroy() {
programHandle = -1;
vertexPositionLocation = -1;
vertexTextureCoordinateLocation = -1;
vertexModelViewProjectionMatrixLocation = -1;
vertexTransformMatrixLocation = -1;
// NOTE: this destroys the GL program, but Filter consumers (GlTextureDrawer, MultiFilter)
// will destroy it too (just like they create it). Hope this doesn't cause any issue;
// if it does, we could avoid calling release here.
program.release();
program = null;
}
@NonNull
@ -178,7 +153,7 @@ public abstract class BaseFilter implements Filter {
@Override
public void draw(long timestampUs, @NonNull float[] transformMatrix) {
if (programHandle == -1) {
if (program == null) {
LOG.w("Filter.draw() called after destroying the filter. " +
"This can happen rarely because of threading.");
} else {
@ -189,43 +164,18 @@ public abstract class BaseFilter implements Filter {
}
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
// Copy the model / view / projection matrix over.
GLES20.glUniformMatrix4fv(vertexModelViewProjectionMatrixLocation, 1,
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");
program.setTextureTransform(transformMatrix);
program.onPreDraw(programDrawable, programDrawable.getModelMatrix());
}
@SuppressWarnings("WeakerAccess")
protected void onDraw(long timestampUs) {
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GlUtils.checkError("glDrawArrays");
protected void onDraw(@SuppressWarnings("unused") long timestampUs) {
program.onDraw(programDrawable);
}
@SuppressWarnings("WeakerAccess")
protected void onPostDraw(long timestampUs) {
GLES20.glDisableVertexAttribArray(vertexPositionLocation);
GLES20.glDisableVertexAttribArray(vertexTextureCoordinateLocation);
protected void onPostDraw(@SuppressWarnings("unused") long timestampUs) {
program.onPostDraw(programDrawable);
}
@NonNull

@ -6,8 +6,10 @@ import android.opengl.GLES20;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.otaliastudios.cameraview.internal.GlUtils;
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.Arrays;
@ -42,13 +44,12 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
@VisibleForTesting
static class State {
@VisibleForTesting boolean isCreated = false;
@VisibleForTesting boolean isProgramCreated = false;
@VisibleForTesting boolean isFramebufferCreated = false;
@VisibleForTesting Size size = null;
private int programHandle = -1;
private int framebufferId = -1;
private int textureId = -1;
private int outputFramebufferId = -1;
private int outputTextureId = -1;
}
@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
// 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);
//noinspection ConstantConditions
if (!state.isCreated) {
state.isCreated = true;
String shader = filter.getFragmentShader();
if (!isFirst) {
if (state.isProgramCreated) return;
state.isProgramCreated = true;
// The first shader actually reads from a OES texture, but the others
// will read from the 2d framebuffer texture. This is a dirty hack.
shader = shader.replace("samplerExternalOES ", "sampler2D ");
}
state.programHandle = GlUtils.createProgram(filter.getVertexShader(), shader);
String fragmentShader = isFirst
? filter.getFragmentShader()
: filter.getFragmentShader().replace("samplerExternalOES ", "sampler2D ");
String vertexShader = filter.getVertexShader();
state.programHandle = GlProgram.create(vertexShader, fragmentShader);
filter.onCreate(state.programHandle);
}
}
private void maybeDestroy(@NonNull Filter filter) {
private void maybeDestroyProgram(@NonNull Filter filter) {
State state = states.get(filter);
//noinspection ConstantConditions
if (state.isCreated) {
state.isCreated = false;
if (!state.isProgramCreated) return;
state.isProgramCreated = false;
filter.onDestroy();
GLES20.glDeleteProgram(state.programHandle);
state.programHandle = -1;
}
}
private void maybeCreateFramebuffer(@NonNull Filter filter, boolean isLast) {
if (isLast) return;
private void maybeCreateFramebuffer(@NonNull Filter filter, boolean isFirst, boolean isLast) {
State state = states.get(filter);
//noinspection ConstantConditions
if (!state.isFramebufferCreated) {
if (state.isFramebufferCreated || isLast) return;
state.isFramebufferCreated = true;
int[] framebufferArray = new int[1];
int[] textureArray = new int[1];
GLES20.glGenFramebuffers(1, framebufferArray, 0);
GLES20.glGenTextures(1, textureArray, 0);
state.framebufferId = framebufferArray[0];
state.textureId = textureArray[0];
state.outputFramebufferId = framebufferArray[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,
state.size.getWidth(), state.size.getHeight(), 0, GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE, null);
@ -158,11 +157,11 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
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.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D,
state.textureId,
state.outputTextureId,
0);
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.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
}
private void maybeDestroyFramebuffer(@NonNull Filter filter) {
State state = states.get(filter);
//noinspection ConstantConditions
if (state.isFramebufferCreated) {
if (!state.isFramebufferCreated) return;
state.isFramebufferCreated = false;
GLES20.glDeleteFramebuffers(1, new int[]{state.framebufferId}, 0);
state.framebufferId = -1;
GLES20.glDeleteTextures(1, new int[]{state.textureId}, 0);
state.textureId = -1;
}
GLES20.glDeleteFramebuffers(1, new int[]{state.outputFramebufferId}, 0);
state.outputFramebufferId = -1;
GLES20.glDeleteTextures(1, new int[]{state.outputTextureId}, 0);
state.outputTextureId = -1;
}
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
@Override
public String getVertexShader() {
// Whatever, we won't be using this.
return new NoFilter().getVertexShader();
return GlTextureProgram.SIMPLE_VERTEX_SHADER;
}
@NonNull
@Override
public String getFragmentShader() {
// Whatever, we won't be using this.
return new NoFilter().getFragmentShader();
}
@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.
return GlTextureProgram.SIMPLE_FRAGMENT_SHADER;
}
@Override
@ -221,7 +218,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
synchronized (lock) {
for (Filter filter : filters) {
maybeDestroyFramebuffer(filter);
maybeDestroy(filter);
maybeDestroyProgram(filter);
}
}
}
@ -245,8 +242,8 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
Filter filter = filters.get(i);
State state = states.get(filter);
maybeSetSize(filter);
maybeCreate(filter, isFirst);
maybeCreateFramebuffer(filter, isLast);
maybeCreateProgram(filter, isFirst, isLast);
maybeCreateFramebuffer(filter, isFirst, isLast);
//noinspection ConstantConditions
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
// last filter, which outputs into the default framebuffer.
if (!isLast) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.framebufferId);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, state.outputFramebufferId);
GLES20.glClearColor(0, 0, 0, 0);
} else {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
@ -267,14 +264,14 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
if (isFirst) {
filter.draw(timestampUs, transformMatrix);
} else {
filter.draw(timestampUs, GlUtils.IDENTITY_MATRIX);
filter.draw(timestampUs, Egloo.IDENTITY_MATRIX);
}
// Set the input for the next cycle:
// It is the framebuffer texture from this cycle. If this is the last
// filter, reset this value just to cleanup.
if (!isLast) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.textureId);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.outputTextureId);
} else {
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* 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) {
super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale");
Egloo.checkGlProgramLocation(scaleLocation, "scale");
}
@Override
@ -120,6 +120,6 @@ public class AutoFixFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix);
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* Adjusts the brightness of the frames.
@ -76,7 +76,7 @@ public class BrightnessFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
brightnessLocation = GLES20.glGetUniformLocation(programHandle, "brightness");
GlUtils.checkLocation(brightnessLocation, "brightness");
Egloo.checkGlProgramLocation(brightnessLocation, "brightness");
}
@Override
@ -89,6 +89,6 @@ public class BrightnessFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix);
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* Adjusts the contrast.
@ -78,7 +78,7 @@ public class ContrastFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
contrastLocation = GLES20.glGetUniformLocation(programHandle, "contrast");
GlUtils.checkLocation(contrastLocation, "contrast");
Egloo.checkGlProgramLocation(contrastLocation, "contrast");
}
@Override
@ -91,6 +91,6 @@ public class ContrastFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(contrastLocation, contrast);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
}
}

@ -5,7 +5,7 @@ import android.opengl.GLES20;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
import java.util.Random;
@ -80,9 +80,9 @@ public class DocumentaryFilter extends BaseFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
mScaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(mScaleLocation, "scale");
Egloo.checkGlProgramLocation(mScaleLocation, "scale");
mMaxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist");
GlUtils.checkLocation(mMaxDistLocation, "inv_max_dist");
Egloo.checkGlProgramLocation(mMaxDistLocation, "inv_max_dist");
}
@Override
@ -104,12 +104,12 @@ public class DocumentaryFilter extends BaseFilter {
scale[1] = 1f;
}
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 invMaxDist = 1F / maxDist;
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.TwoParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* 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) {
super.onCreate(programHandle);
mFirstColorLocation = GLES20.glGetUniformLocation(programHandle, "first");
GlUtils.checkLocation(mFirstColorLocation, "first");
Egloo.checkGlProgramLocation(mFirstColorLocation, "first");
mSecondColorLocation = GLES20.glGetUniformLocation(programHandle, "second");
GlUtils.checkLocation(mSecondColorLocation, "second");
Egloo.checkGlProgramLocation(mSecondColorLocation, "second");
}
@Override
@ -150,9 +150,9 @@ public class DuotoneFilter extends BaseFilter implements TwoParameterFilter {
Color.blue(mSecondColor) / 255f
};
GLES20.glUniform3fv(mFirstColorLocation, 1, first, 0);
GlUtils.checkError("glUniform3fv");
Egloo.checkGlError("glUniform3fv");
GLES20.glUniform3fv(mSecondColorLocation, 1, second, 0);
GlUtils.checkError("glUniform3fv");
Egloo.checkGlError("glUniform3fv");
}
@Override

@ -6,7 +6,7 @@ import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter;
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.
@ -82,9 +82,9 @@ public class FillLightFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
multiplierLocation = GLES20.glGetUniformLocation(programHandle, "mult");
GlUtils.checkLocation(multiplierLocation, "mult");
Egloo.checkGlProgramLocation(multiplierLocation, "mult");
gammaLocation = GLES20.glGetUniformLocation(programHandle, "igamma");
GlUtils.checkLocation(gammaLocation, "igamma");
Egloo.checkGlProgramLocation(gammaLocation, "igamma");
}
@Override
@ -100,12 +100,12 @@ public class FillLightFilter extends BaseFilter implements OneParameterFilter {
float amount = 1.0f - strength;
float multiplier = 1.0f / (amount * 0.7f + 0.3f);
GLES20.glUniform1f(multiplierLocation, multiplier);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
float fadeGamma = 0.3f;
float faded = fadeGamma + (1.0f - fadeGamma) * multiplier;
float gamma = 1.0f / faded;
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* Applies gamma correction to the frames.
@ -73,7 +73,7 @@ public class GammaFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
gammaLocation = GLES20.glGetUniformLocation(programHandle, "gamma");
GlUtils.checkLocation(gammaLocation, "gamma");
Egloo.checkGlProgramLocation(gammaLocation, "gamma");
}
@Override
@ -86,6 +86,6 @@ public class GammaFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix);
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
import java.util.Random;
@ -122,11 +122,11 @@ public class GrainFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
strengthLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(strengthLocation, "scale");
Egloo.checkGlProgramLocation(strengthLocation, "scale");
stepXLocation = GLES20.glGetUniformLocation(programHandle, "stepX");
GlUtils.checkLocation(stepXLocation, "stepX");
Egloo.checkGlProgramLocation(stepXLocation, "stepX");
stepYLocation = GLES20.glGetUniformLocation(programHandle, "stepY");
GlUtils.checkLocation(stepYLocation, "stepY");
Egloo.checkGlProgramLocation(stepYLocation, "stepY");
}
@Override
@ -141,10 +141,10 @@ public class GrainFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(strengthLocation, strength);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepXLocation, 0.5f / width);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* Applies a hue effect on the input frames.
@ -86,7 +86,7 @@ public class HueFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
hueLocation = GLES20.glGetUniformLocation(programHandle, "hue");
GlUtils.checkLocation(hueLocation, "hue");
Egloo.checkGlProgramLocation(hueLocation, "hue");
}
@Override
@ -101,6 +101,6 @@ public class HueFilter extends BaseFilter implements OneParameterFilter {
// map it on 360 degree circle
float shaderHue = ((hue - 45) / 45f + 0.5f) * -1;
GLES20.glUniform1f(hueLocation, shaderHue);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
}
}

@ -5,7 +5,7 @@ import android.opengl.GLES20;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
import java.util.Random;
@ -122,13 +122,13 @@ public class LomoishFilter extends BaseFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale");
Egloo.checkGlProgramLocation(scaleLocation, "scale");
maxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist");
GlUtils.checkLocation(maxDistLocation, "inv_max_dist");
Egloo.checkGlProgramLocation(maxDistLocation, "inv_max_dist");
stepSizeXLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeX");
GlUtils.checkLocation(stepSizeXLocation, "stepsizeX");
Egloo.checkGlProgramLocation(stepSizeXLocation, "stepsizeX");
stepSizeYLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeY");
GlUtils.checkLocation(stepSizeYLocation, "stepsizeY");
Egloo.checkGlProgramLocation(stepSizeYLocation, "stepsizeY");
}
@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;
GLES20.glUniform2fv(scaleLocation, 1, scale, 0);
GlUtils.checkError("glUniform2fv");
Egloo.checkGlError("glUniform2fv");
GLES20.glUniform1f(maxDistLocation, 1.0F / maxDist);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepSizeXLocation, 1.0F / width);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* Adjusts color saturation.
@ -93,9 +93,9 @@ public class SaturationFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale");
Egloo.checkGlProgramLocation(scaleLocation, "scale");
exponentsLocation = GLES20.glGetUniformLocation(programHandle, "exponents");
GlUtils.checkLocation(exponentsLocation, "exponents");
Egloo.checkGlProgramLocation(exponentsLocation, "exponents");
}
@Override
@ -110,18 +110,18 @@ public class SaturationFilter extends BaseFilter implements OneParameterFilter {
super.onPreDraw(timestampUs, transformMatrix);
if (scale > 0.0f) {
GLES20.glUniform1f(scaleLocation, 0F);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
GLES20.glUniform3f(exponentsLocation,
(0.9f * scale) + 1.0f,
(2.1f * scale) + 1.0f,
(2.7f * scale) + 1.0f
);
GlUtils.checkError("glUniform3f");
Egloo.checkGlError("glUniform3f");
} else {
GLES20.glUniform1f(scaleLocation, 1.0F + scale);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* Sharpens the input frames.
@ -100,11 +100,11 @@ public class SharpnessFilter extends BaseFilter implements OneParameterFilter {
public void onCreate(int programHandle) {
super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale");
Egloo.checkGlProgramLocation(scaleLocation, "scale");
stepSizeXLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeX");
GlUtils.checkLocation(stepSizeXLocation, "stepsizeX");
Egloo.checkGlProgramLocation(stepSizeXLocation, "stepsizeX");
stepSizeYLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeY");
GlUtils.checkLocation(stepSizeYLocation, "stepsizeY");
Egloo.checkGlProgramLocation(stepSizeYLocation, "stepsizeY");
}
@Override
@ -119,10 +119,10 @@ public class SharpnessFilter extends BaseFilter implements OneParameterFilter {
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix);
GLES20.glUniform1f(scaleLocation, scale);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
GLES20.glUniform1f(stepSizeXLocation, 1.0F / width);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
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.OneParameterFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import com.otaliastudios.opengl.core.Egloo;
/**
* Adjusts color temperature.
@ -84,7 +84,7 @@ public class TemperatureFilter extends BaseFilter implements OneParameterFilter
public void onCreate(int programHandle) {
super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale");
Egloo.checkGlProgramLocation(scaleLocation, "scale");
}
@Override
@ -97,6 +97,6 @@ public class TemperatureFilter extends BaseFilter implements OneParameterFilter
protected void onPreDraw(long timestampUs, @NonNull float[] transformMatrix) {
super.onPreDraw(timestampUs, transformMatrix);
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.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) {
super.onCreate(programHandle);
tintLocation = GLES20.glGetUniformLocation(programHandle, "tint");
GlUtils.checkLocation(tintLocation, "tint");
Egloo.checkGlProgramLocation(tintLocation, "tint");
}
@Override
@ -99,6 +99,6 @@ public class TintFilter extends BaseFilter implements OneParameterFilter {
Color.blue(tint) / 255f
};
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.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) {
super.onCreate(programHandle);
mRangeLocation = GLES20.glGetUniformLocation(programHandle, "range");
GlUtils.checkLocation(mRangeLocation, "range");
Egloo.checkGlProgramLocation(mRangeLocation, "range");
mMaxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist");
GlUtils.checkLocation(mMaxDistLocation, "inv_max_dist");
Egloo.checkGlProgramLocation(mMaxDistLocation, "inv_max_dist");
mShadeLocation = GLES20.glGetUniformLocation(programHandle, "shade");
GlUtils.checkLocation(mShadeLocation, "shade");
Egloo.checkGlProgramLocation(mShadeLocation, "shade");
mScaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(mScaleLocation, "scale");
Egloo.checkGlProgramLocation(mScaleLocation, "scale");
}
@Override
@ -156,20 +156,20 @@ public class VignetteFilter extends BaseFilter implements TwoParameterFilter {
scale[1] = 1f;
}
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;
GLES20.glUniform1f(mMaxDistLocation, 1F / maxDist);
GlUtils.checkError("glUniform1f");
Egloo.checkGlError("glUniform1f");
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
// which means no vignette at all because the luminousity difference is
// less than 1/256 and will cause nothing.
float range = (1.30f - (float) Math.sqrt(mScale) * 0.7f);
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 com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.internal.GlUtils;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
@ -188,7 +187,7 @@ public class EglBaseSurface {
buf.order(ByteOrder.LITTLE_ENDIAN);
GLES20.glReadPixels(0, 0, width, height,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
GlUtils.checkError("glReadPixels");
Egloo.checkGlError("glReadPixels");
buf.rewind();
BufferedOutputStream bos = null;
@ -231,7 +230,7 @@ public class EglBaseSurface {
buf.order(ByteOrder.LITTLE_ENDIAN);
GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
buf);
GlUtils.checkError("glReadPixels");
Egloo.checkGlError("glReadPixels");
buf.rewind();
ByteArrayOutputStream bos = new ByteArrayOutputStream(buf.array().length);

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

@ -105,7 +105,7 @@ public class OverlayDrawer {
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
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
long timestampUs = surfaceTexture.getTimestamp() / 1000L;
LOG.i("takeFrame:", "timestampUs:", timestampUs);
mViewport.drawFrame(timestampUs, mTextureId, mTransform);
mViewport.draw(timestampUs, mTextureId, mTransform);
if (mHasOverlay) mOverlayDrawer.render(timestampUs);
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.scaleM(mTransformMatrix, 0, mCropScaleX, mCropScaleY, 1);
}
mOutputViewport.drawFrame(mInputSurfaceTexture.getTimestamp() / 1000L,
mOutputViewport.draw(mInputSurfaceTexture.getTimestamp() / 1000L,
mOutputTextureId, mTransformMatrix);
for (RendererFrameCallback callback : mRendererFrameCallbacks) {
callback.onRendererFrame(mInputSurfaceTexture, mCropScaleX, mCropScaleY);

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

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

Loading…
Cancel
Save