filters = new ArrayList<>();
@@ -62,7 +65,6 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
* Creates a new group with the given filters.
* @param filters children
*/
- @SuppressWarnings("WeakerAccess")
public MultiFilter(@NonNull Filter... filters) {
this(Arrays.asList(filters));
}
@@ -105,86 +107,54 @@ 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) {
- // 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);
- filter.onCreate(state.programHandle);
- }
+ 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.
+ 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;
- filter.onDestroy();
- GLES20.glDeleteProgram(state.programHandle);
- state.programHandle = -1;
- }
+ 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) {
- 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];
-
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, state.textureId);
- 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);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
- GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
- GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
- 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.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
- GLES20.GL_COLOR_ATTACHMENT0,
- GLES20.GL_TEXTURE_2D,
- state.textureId,
- 0);
-
- int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
- if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
- throw new RuntimeException("Invalid framebuffer generation. Error:" + status);
- }
-
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
- }
+ if (state.isFramebufferCreated || isLast) return;
+ state.isFramebufferCreated = true;
+ state.outputTexture = new GlTexture(GLES20.GL_TEXTURE0,
+ GLES20.GL_TEXTURE_2D,
+ state.size.getWidth(),
+ state.size.getHeight());
+ state.outputFramebuffer = new GlFramebuffer();
+ state.outputFramebuffer.attach(state.outputTexture);
}
private void maybeDestroyFramebuffer(@NonNull Filter filter) {
State state = states.get(filter);
//noinspection ConstantConditions
- if (state.isFramebufferCreated) {
- 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;
- }
+ if (!state.isFramebufferCreated) return;
+ state.isFramebufferCreated = false;
+ state.outputFramebuffer.release();
+ state.outputFramebuffer = null;
+ state.outputTexture.release();
+ state.outputTexture = null;
}
private void maybeSetSize(@NonNull Filter filter) {
@@ -196,24 +166,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 +191,7 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
synchronized (lock) {
for (Filter filter : filters) {
maybeDestroyFramebuffer(filter);
- maybeDestroy(filter);
+ maybeDestroyProgram(filter);
}
}
}
@@ -244,9 +214,10 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
boolean isLast = i == filters.size() - 1;
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 +226,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);
+ state.outputFramebuffer.bind();
GLES20.glClearColor(0, 0, 0, 0);
} else {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
@@ -267,16 +238,17 @@ 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);
+ state.outputTexture.bind();
} else {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
}
GLES20.glUseProgram(0);
@@ -289,6 +261,9 @@ public class MultiFilter implements Filter, OneParameterFilter, TwoParameterFilt
public Filter copy() {
synchronized (lock) {
MultiFilter copy = new MultiFilter();
+ if (size != null) {
+ copy.setSize(size.getWidth(), size.getHeight());
+ }
for (Filter filter : filters) {
copy.addFilter(filter.copy());
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filter/SimpleFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filter/SimpleFilter.java
index eda5ec3e..ca8d4b15 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filter/SimpleFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filter/SimpleFilter.java
@@ -33,6 +33,7 @@ public final class SimpleFilter extends BaseFilter {
return fragmentShader;
}
+ @NonNull
@Override
protected BaseFilter onCopy() {
return new SimpleFilter(fragmentShader);
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/AutoFixFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/AutoFixFilter.java
index 2d01e658..9b6ea8da 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/AutoFixFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/AutoFixFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/BrightnessFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/BrightnessFilter.java
index 3c453326..ffef8fbe 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/BrightnessFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/BrightnessFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/ContrastFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/ContrastFilter.java
index c4054e16..cfb537d5 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/ContrastFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/ContrastFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/DocumentaryFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/DocumentaryFilter.java
index 9e66c8d8..bc26488e 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/DocumentaryFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/DocumentaryFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/DuotoneFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/DuotoneFilter.java
index f335c019..d6c87a38 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/DuotoneFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/DuotoneFilter.java
@@ -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
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/FillLightFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/FillLightFilter.java
index bf801474..0d9d2fce 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/FillLightFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/FillLightFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GammaFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GammaFilter.java
index 4cef7017..6f320e52 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GammaFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GammaFilter.java
@@ -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");
}
}
\ No newline at end of file
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrainFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrainFilter.java
index 92df0d5f..47092979 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrainFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrainFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/HueFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/HueFilter.java
index 54c6d93f..22f798fd 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/HueFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/HueFilter.java
@@ -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");
}
}
\ No newline at end of file
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/LomoishFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/LomoishFilter.java
index 411e1531..71cb9ec1 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/LomoishFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/LomoishFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SaturationFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SaturationFilter.java
index 26d1b357..2dcae76c 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SaturationFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SaturationFilter.java
@@ -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");
}
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SharpnessFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SharpnessFilter.java
index 5f100ebc..e9e8f9ba 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SharpnessFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SharpnessFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TemperatureFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TemperatureFilter.java
index 432b1e37..96fc32dc 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TemperatureFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TemperatureFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TintFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TintFilter.java
index a5f8b146..ea7fc457 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TintFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TintFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/VignetteFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/VignetteFilter.java
index fcdd3bf2..1ac400e2 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/VignetteFilter.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/VignetteFilter.java
@@ -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");
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfiles.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/CamcorderProfiles.java
similarity index 98%
rename from cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfiles.java
rename to cameraview/src/main/java/com/otaliastudios/cameraview/internal/CamcorderProfiles.java
index 93ec596e..9ea2f933 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/CamcorderProfiles.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/CamcorderProfiles.java
@@ -1,4 +1,4 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
import android.annotation.SuppressLint;
import android.media.CamcorderProfile;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/CropHelper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/CropHelper.java
similarity index 96%
rename from cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/CropHelper.java
rename to cameraview/src/main/java/com/otaliastudios/cameraview/internal/CropHelper.java
index f7f71ee7..649acc80 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/CropHelper.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/CropHelper.java
@@ -1,4 +1,4 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
import android.graphics.Rect;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/ExifHelper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/ExifHelper.java
similarity index 96%
rename from cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/ExifHelper.java
rename to cameraview/src/main/java/com/otaliastudios/cameraview/internal/ExifHelper.java
index a890124f..8a150419 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/ExifHelper.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/ExifHelper.java
@@ -1,4 +1,4 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
import androidx.exifinterface.media.ExifInterface;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/GlTextureDrawer.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/GlTextureDrawer.java
new file mode 100644
index 00000000..24c956d5
--- /dev/null
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/GlTextureDrawer.java
@@ -0,0 +1,97 @@
+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 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 setTextureTransform(@NonNull float[] textureTransform) {
+ mTextureTransform = textureTransform;
+ }
+
+ public void draw(final 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("program creation");
+ }
+
+ GLES20.glUseProgram(mProgramHandle);
+ Egloo.checkGlError("glUseProgram(handle)");
+ mTexture.bind();
+ mFilter.draw(timestampUs, mTextureTransform);
+ mTexture.unbind();
+ GLES20.glUseProgram(0);
+ Egloo.checkGlError("glUseProgram(0)");
+ }
+
+ public void release() {
+ if (mProgramHandle == -1) return;
+ mFilter.onDestroy();
+ GLES20.glDeleteProgram(mProgramHandle);
+ mProgramHandle = -1;
+ }
+}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/GlUtils.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/GlUtils.java
deleted file mode 100644
index 05d5c4d8..00000000
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/GlUtils.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/Issue514Workaround.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/Issue514Workaround.java
index 828a5aff..f5e35725 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/Issue514Workaround.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/Issue514Workaround.java
@@ -6,7 +6,6 @@ import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.view.Surface;
-import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.preview.RendererThread;
@@ -84,7 +83,7 @@ import com.otaliastudios.cameraview.preview.RendererThread;
*
* This makes no sense, since overlaySurfaceTexture.updateTexImage() is setting it to
* overlayTextureId anyway, but it fixes the issue. Specifically, after any draw operation with
- * {@link EglViewport}, the bound texture is reset to 0 so this must be undone here. We offer:
+ * {@link GlTextureDrawer}, the bound texture is reset to 0 so this must be undone here. We offer:
*
* - {@link #beforeOverlayUpdateTexImage()} to be called before the
* {@link SurfaceTexture#updateTexImage()} call
@@ -92,7 +91,7 @@ import com.otaliastudios.cameraview.preview.RendererThread;
*
* Since updating and rendering can happen on different threads with a shared EGL context,
* in case they do, the {@link #beforeOverlayUpdateTexImage()}, the actual updateTexImage() and
- * finally the {@link EglViewport} drawing operations should be synchronized with a lock.
+ * finally the {@link GlTextureDrawer} drawing operations should be synchronized with a lock.
*
* REFERENCES
* https://github.com/natario1/CameraView/issues/514
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/OrientationHelper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/OrientationHelper.java
similarity index 99%
rename from cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/OrientationHelper.java
rename to cameraview/src/main/java/com/otaliastudios/cameraview/internal/OrientationHelper.java
index 36c5f1d5..de47526a 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/OrientationHelper.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/OrientationHelper.java
@@ -1,4 +1,4 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
import android.content.Context;
import android.hardware.SensorManager;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/Pool.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/Pool.java
similarity index 98%
rename from cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/Pool.java
rename to cameraview/src/main/java/com/otaliastudios/cameraview/internal/Pool.java
index d7c8b57c..5010b15b 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/Pool.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/Pool.java
@@ -1,4 +1,4 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
import com.otaliastudios.cameraview.CameraLogger;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/RotationHelper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/RotationHelper.java
similarity index 97%
rename from cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/RotationHelper.java
rename to cameraview/src/main/java/com/otaliastudios/cameraview/internal/RotationHelper.java
index bed0f90c..88225e25 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/RotationHelper.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/RotationHelper.java
@@ -1,4 +1,4 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
import com.otaliastudios.cameraview.size.Size;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/WorkerHandler.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/WorkerHandler.java
similarity index 99%
rename from cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/WorkerHandler.java
rename to cameraview/src/main/java/com/otaliastudios/cameraview/internal/WorkerHandler.java
index 81743f8d..e5cb9702 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/utils/WorkerHandler.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/WorkerHandler.java
@@ -1,4 +1,4 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
import android.os.Handler;
import android.os.HandlerThread;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglBaseSurface.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglBaseSurface.java
deleted file mode 100644
index f9ceec53..00000000
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglBaseSurface.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright 2013 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.otaliastudios.cameraview.internal.egl;
-
-import android.graphics.Bitmap;
-import android.opengl.EGL14;
-import android.opengl.EGLSurface;
-import android.opengl.GLES20;
-import android.os.Build;
-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;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Common base class for EGL surfaces.
- *
- * There can be multiple surfaces associated with a single context.
- */
-@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
-public class EglBaseSurface {
- protected static final String TAG = EglBaseSurface.class.getSimpleName();
- private final static CameraLogger LOG = CameraLogger.create(TAG);
-
- // EglCore object we're associated with. It may be associated with multiple surfaces.
- protected EglCore mEglCore;
-
- private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
- private int mWidth = -1;
- private int mHeight = -1;
-
- public EglBaseSurface(EglCore eglCore) {
- mEglCore = eglCore;
- }
-
- /**
- * Creates a window surface.
- *
- * @param surface May be a Surface or SurfaceTexture.
- */
- public void createWindowSurface(Object surface) {
- if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
- throw new IllegalStateException("surface already created");
- }
- mEGLSurface = mEglCore.createWindowSurface(surface);
-
- // Don't cache width/height here, because the size of the underlying surface can change
- // out from under us (see e.g. HardwareScalerActivity).
- //mWidth = mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
- //mHeight = mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
- }
-
- /**
- * Creates an off-screen surface.
- */
- public void createOffscreenSurface(int width, int height) {
- if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
- throw new IllegalStateException("surface already created");
- }
- mEGLSurface = mEglCore.createOffscreenSurface(width, height);
- mWidth = width;
- mHeight = height;
- }
-
- /**
- * Returns the surface's width, in pixels.
- *
- * If this is called on a window surface, and the underlying surface is in the process
- * of changing size, we may not see the new size right away (e.g. in the "surfaceChanged"
- * callback). The size should match after the next buffer swap.
- */
- public int getWidth() {
- if (mWidth < 0) {
- return mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
- } else {
- return mWidth;
- }
- }
-
- /**
- * Returns the surface's height, in pixels.
- */
- public int getHeight() {
- if (mHeight < 0) {
- return mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
- } else {
- return mHeight;
- }
- }
-
- /**
- * Release the EGL surface.
- */
- public void releaseEglSurface() {
- mEglCore.releaseSurface(mEGLSurface);
- mEGLSurface = EGL14.EGL_NO_SURFACE;
- mWidth = mHeight = -1;
- }
-
- /**
- * Makes our EGL context and surface current.
- */
- public void makeCurrent() {
- mEglCore.makeCurrent(mEGLSurface);
- }
-
- /**
- * Makes our EGL context and surface current for drawing, using the supplied surface
- * for reading.
- */
- public void makeCurrentReadFrom(EglBaseSurface readSurface) {
- mEglCore.makeCurrent(mEGLSurface, readSurface.mEGLSurface);
- }
-
- /**
- * Calls eglSwapBuffers. Use this to "publish" the current frame.
- *
- * @return false on failure
- */
- public boolean swapBuffers() {
- boolean result = mEglCore.swapBuffers(mEGLSurface);
- if (!result) {
- Log.d(TAG, "WARNING: swapBuffers() failed");
- }
- return result;
- }
-
- /**
- * Sends the presentation time stamp to EGL.
- * https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_presentation_time.txt
- *
- * @param nsecs Timestamp, in nanoseconds.
- */
- public void setPresentationTime(long nsecs) {
- mEglCore.setPresentationTime(mEGLSurface, nsecs);
- }
-
- /**
- * Saves the EGL surface to a file.
- *
- * Expects that this object's EGL surface is current.
- */
- public void saveFrameToFile(File file) throws IOException {
- if (!mEglCore.isCurrent(mEGLSurface)) {
- throw new RuntimeException("Expected EGL context/surface is not current");
- }
-
- // glReadPixels fills in a "direct" ByteBuffer with what is essentially big-endian RGBA
- // data (i.e. a byte of red, followed by a byte of green...). While the Bitmap
- // constructor that takes an int[] wants little-endian ARGB (blue/red swapped), the
- // Bitmap "copy pixels" method wants the same format GL provides.
- //
- // Ideally we'd have some way to re-use the ByteBuffer, especially if we're calling
- // here often.
- //
- // Making this even more interesting is the upside-down nature of GL, which means
- // our output will look upside down relative to what appears on screen if the
- // typical GL conventions are used.
-
- String filename = file.toString();
-
- int width = getWidth();
- int height = getHeight();
- ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4);
- buf.order(ByteOrder.LITTLE_ENDIAN);
- GLES20.glReadPixels(0, 0, width, height,
- GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
- GlUtils.checkError("glReadPixels");
- buf.rewind();
-
- BufferedOutputStream bos = null;
- try {
- bos = new BufferedOutputStream(new FileOutputStream(filename));
- Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- bmp.copyPixelsFromBuffer(buf);
- bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
- bmp.recycle();
- } finally {
- if (bos != null) bos.close();
- }
- }
-
- /**
- * Saves the EGL surface to given format.
- *
- * Expects that this object's EGL surface is current.
- */
- public byte[] saveFrameTo(Bitmap.CompressFormat compressFormat) {
- if (!mEglCore.isCurrent(mEGLSurface)) {
- throw new RuntimeException("Expected EGL context/surface is not current");
- }
-
- // glReadPixels fills in a "direct" ByteBuffer with what is essentially big-endian RGBA
- // data (i.e. a byte of red, followed by a byte of green...). While the Bitmap
- // constructor that takes an int[] wants little-endian ARGB (blue/red swapped), the
- // Bitmap "copy pixels" method wants the same format GL provides.
- //
- // Ideally we'd have some way to re-use the ByteBuffer, especially if we're calling
- // here often.
- //
- // Making this even more interesting is the upside-down nature of GL, which means
- // our output will look upside down relative to what appears on screen if the
- // typical GL conventions are used.
-
- int width = getWidth();
- int height = getHeight();
- ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4);
- buf.order(ByteOrder.LITTLE_ENDIAN);
- GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
- buf);
- GlUtils.checkError("glReadPixels");
- buf.rewind();
-
- ByteArrayOutputStream bos = new ByteArrayOutputStream(buf.array().length);
- Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- bmp.copyPixelsFromBuffer(buf);
- bmp.compress(compressFormat, 90, bos);
- bmp.recycle();
- return bos.toByteArray();
- }
-}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglCore.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglCore.java
deleted file mode 100644
index 3d9c6b65..00000000
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglCore.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright 2013 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.otaliastudios.cameraview.internal.egl;
-
-import android.graphics.SurfaceTexture;
-import android.opengl.EGL14;
-import android.opengl.EGLConfig;
-import android.opengl.EGLContext;
-import android.opengl.EGLDisplay;
-import android.opengl.EGLExt;
-import android.opengl.EGLSurface;
-import android.os.Build;
-import androidx.annotation.RequiresApi;
-import android.util.Log;
-import android.view.Surface;
-
-/**
- * -- from grafika --
- *
- * Core EGL state (display, context, config).
- *
- * The EGLContext must only be attached to one thread at a time. This class is not thread-safe.
- */
-@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
-public final class EglCore {
- private static final String TAG = EglCore.class.getSimpleName();
-
- /**
- * Constructor flag: surface must be recordable. This discourages EGL from using a
- * pixel format that cannot be converted efficiently to something usable by the video
- * encoder.
- */
- public static final int FLAG_RECORDABLE = 0x01;
-
- /**
- * Constructor flag: ask for GLES3, fall back to GLES2 if not available. Without this
- * flag, GLES2 is used.
- */
- public static final int FLAG_TRY_GLES3 = 0x02;
-
- // Android-specific extension.
- private static final int EGL_RECORDABLE_ANDROID = 0x3142;
-
- private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
- private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
- private EGLConfig mEGLConfig = null;
- private int mGlVersion = -1;
-
-
- /**
- * Prepares EGL display and context.
- *
- * Equivalent to EglCore(null, 0).
- */
- public EglCore() {
- this(null, 0);
- }
-
- /**
- * Prepares EGL display and context.
- *
- * @param sharedContext The context to share, or null if sharing is not desired.
- * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE.
- */
- public EglCore(EGLContext sharedContext, int flags) {
- if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
- throw new RuntimeException("EGL already set up");
- }
-
- if (sharedContext == null) {
- sharedContext = EGL14.EGL_NO_CONTEXT;
- }
-
- mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
- if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
- throw new RuntimeException("unable to get EGL14 display");
- }
- int[] version = new int[2];
- if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
- mEGLDisplay = null;
- throw new RuntimeException("unable to initialize EGL14");
- }
-
- // Try to get a GLES3 context, if requested.
- if ((flags & FLAG_TRY_GLES3) != 0) {
- //Log.d(TAG, "Trying GLES 3");
- EGLConfig config = getConfig(flags, 3);
- if (config != null) {
- int[] attrib3_list = {
- EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
- EGL14.EGL_NONE
- };
- EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
- attrib3_list, 0);
-
- if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
- //Log.d(TAG, "Got GLES 3 config");
- mEGLConfig = config;
- mEGLContext = context;
- mGlVersion = 3;
- }
- }
- }
- if (mEGLContext == EGL14.EGL_NO_CONTEXT) { // GLES 2 only, or GLES 3 attempt failed
- //Log.d(TAG, "Trying GLES 2");
- EGLConfig config = getConfig(flags, 2);
- if (config == null) {
- throw new RuntimeException("Unable to find a suitable EGLConfig");
- }
- int[] attrib2_list = {
- EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL14.EGL_NONE
- };
- EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
- attrib2_list, 0);
- checkEglError("eglCreateContext");
- mEGLConfig = config;
- mEGLContext = context;
- mGlVersion = 2;
- }
-
- // Confirm with query.
- int[] values = new int[1];
- EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION,
- values, 0);
- // Log.d(TAG, "EGLContext created, client version " + values[0]);
- }
-
- /**
- * Finds a suitable EGLConfig.
- *
- * @param flags Bit flags from constructor.
- * @param version Must be 2 or 3.
- */
- private EGLConfig getConfig(int flags, int version) {
- int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
- if (version >= 3) {
- renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
- }
-
- // The actual surface is generally RGBA or RGBX, so situationally omitting alpha
- // doesn't really help. It can also lead to a huge performance hit on glReadPixels()
- // when reading into a GL_RGBA buffer.
- int[] attribList = {
- EGL14.EGL_RED_SIZE, 8,
- EGL14.EGL_GREEN_SIZE, 8,
- EGL14.EGL_BLUE_SIZE, 8,
- EGL14.EGL_ALPHA_SIZE, 8,
- //EGL14.EGL_DEPTH_SIZE, 16,
- //EGL14.EGL_STENCIL_SIZE, 8,
- EGL14.EGL_RENDERABLE_TYPE, renderableType,
- EGL14.EGL_NONE, 0, // placeholder for recordable [@-3]
- EGL14.EGL_NONE
- };
- if ((flags & FLAG_RECORDABLE) != 0) {
- attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID;
- attribList[attribList.length - 2] = 1;
- }
- EGLConfig[] configs = new EGLConfig[1];
- int[] numConfigs = new int[1];
- if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0,
- configs.length, numConfigs, 0)) {
- Log.w(TAG, "unable to find RGB8888 / " + version + " EGLConfig");
- return null;
- }
- return configs[0];
- }
-
- /**
- * Discards all resources held by this class, notably the EGL context. This must be
- * called from the thread where the context was created.
- *
- * On completion, no context will be current.
- */
- public void release() {
- if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
- // Android is unusual in that it uses a reference-counted EGLDisplay. So for
- // every eglInitialize() we need an eglTerminate().
- EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
- EGL14.EGL_NO_CONTEXT);
- EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
- EGL14.eglReleaseThread();
- EGL14.eglTerminate(mEGLDisplay);
- }
-
- mEGLDisplay = EGL14.EGL_NO_DISPLAY;
- mEGLContext = EGL14.EGL_NO_CONTEXT;
- mEGLConfig = null;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
- // We're limited here -- finalizers don't run on the thread that holds
- // the EGL state, so if a surface or context is still current on another
- // thread we can't fully release it here. Exceptions thrown from here
- // are quietly discarded. Complain in the log file.
- Log.w(TAG, "WARNING: EglCore was not explicitly released! " +
- "State may be leaked");
- release();
- }
- } finally {
- super.finalize();
- }
- }
-
- /**
- * Destroys the specified surface. Note the EGLSurface won't actually be destroyed if it's
- * still current in a context.
- */
- public void releaseSurface(EGLSurface eglSurface) {
- EGL14.eglDestroySurface(mEGLDisplay, eglSurface);
- }
-
- /**
- * Creates an EGL surface associated with a Surface.
- *
- * If this is destined for MediaCodec, the EGLConfig should have the "recordable" attribute.
- */
- public EGLSurface createWindowSurface(Object surface) {
- if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) {
- throw new RuntimeException("invalid surface: " + surface);
- }
-
- // Create a window surface, and attach it to the Surface we received.
- int[] surfaceAttribs = {
- EGL14.EGL_NONE
- };
- EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface,
- surfaceAttribs, 0);
- checkEglError("eglCreateWindowSurface");
- if (eglSurface == null) {
- throw new RuntimeException("surface was null");
- }
- return eglSurface;
- }
-
- /**
- * Creates an EGL surface associated with an offscreen buffer.
- */
- public EGLSurface createOffscreenSurface(int width, int height) {
- int[] surfaceAttribs = {
- EGL14.EGL_WIDTH, width,
- EGL14.EGL_HEIGHT, height,
- EGL14.EGL_NONE
- };
- EGLSurface eglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig,
- surfaceAttribs, 0);
- checkEglError("eglCreatePbufferSurface");
- if (eglSurface == null) {
- throw new RuntimeException("surface was null");
- }
- return eglSurface;
- }
-
- /**
- * Makes our EGL context current, using the supplied surface for both "draw" and "read".
- */
- public void makeCurrent(EGLSurface eglSurface) {
- if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
- // called makeCurrent() before create?
- // Log.d(TAG, "NOTE: makeCurrent w/o display");
- }
- if (!EGL14.eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) {
- throw new RuntimeException("eglMakeCurrent failed");
- }
- }
-
- /**
- * Makes our EGL context current, using the supplied "draw" and "read" surfaces.
- */
- public void makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
- if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
- // called makeCurrent() before create?
- Log.d(TAG, "NOTE: makeCurrent w/o display");
- }
- if (!EGL14.eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext)) {
- throw new RuntimeException("eglMakeCurrent(draw,read) failed");
- }
- }
-
- /**
- * Makes no context current.
- */
- public void makeNothingCurrent() {
- if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
- EGL14.EGL_NO_CONTEXT)) {
- throw new RuntimeException("eglMakeCurrent failed");
- }
- }
-
- /**
- * Calls eglSwapBuffers. Use this to "publish" the current frame.
- *
- * @return false on failure
- */
- public boolean swapBuffers(EGLSurface eglSurface) {
- return EGL14.eglSwapBuffers(mEGLDisplay, eglSurface);
- }
-
- /**
- * Sends the presentation time stamp to EGL. Time is expressed in nanoseconds.
- * https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_presentation_time.txt
- */
- public void setPresentationTime(EGLSurface eglSurface, long nsecs) {
- EGLExt.eglPresentationTimeANDROID(mEGLDisplay, eglSurface, nsecs);
- }
-
- /**
- * Returns true if our context and the specified surface are current.
- */
- public boolean isCurrent(EGLSurface eglSurface) {
- return mEGLContext.equals(EGL14.eglGetCurrentContext()) &&
- eglSurface.equals(EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW));
- }
-
- /**
- * Performs a simple surface query.
- */
- public int querySurface(EGLSurface eglSurface, int what) {
- int[] value = new int[1];
- EGL14.eglQuerySurface(mEGLDisplay, eglSurface, what, value, 0);
- return value[0];
- }
-
- /**
- * Queries a string value.
- */
- public String queryString(int what) {
- return EGL14.eglQueryString(mEGLDisplay, what);
- }
-
- /**
- * Returns the GLES version this context is configured for (currently 2 or 3).
- */
- public int getGlVersion() {
- return mGlVersion;
- }
-
- /**
- * Checks for EGL errors. Throws an exception if an error has been raised.
- */
- private void checkEglError(String msg) {
- int error;
- if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
- throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
- }
- }
-}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglViewport.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglViewport.java
deleted file mode 100644
index 685b00ca..00000000
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglViewport.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package com.otaliastudios.cameraview.internal.egl;
-
-
-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.cameraview.internal.GlUtils;
-
-
-public class EglViewport {
-
- private final static CameraLogger LOG = CameraLogger.create(EglViewport.class.getSimpleName());
-
- private int mProgramHandle = -1;
- private int mTextureTarget;
- private int mTextureUnit;
-
- private Filter mFilter;
- private Filter mPendingFilter;
-
- public EglViewport() {
- this(new NoFilter());
- }
-
- public EglViewport(@NonNull Filter filter) {
- mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
- mTextureUnit = GLES20.GL_TEXTURE0;
- mFilter = filter;
- createProgram();
- }
-
- private void createProgram() {
- mProgramHandle = GlUtils.createProgram(mFilter.getVertexShader(),
- mFilter.getFragmentShader());
- mFilter.onCreate(mProgramHandle);
- }
-
- public void release() {
- if (mProgramHandle != -1) {
- mFilter.onDestroy();
- GLES20.glDeleteProgram(mProgramHandle);
- mProgramHandle = -1;
- }
- }
-
- public int createTexture() {
- int[] textures = new int[1];
- GLES20.glGenTextures(1, textures, 0);
- GlUtils.checkError("glGenTextures");
-
- int texId = textures[0];
- GLES20.glActiveTexture(mTextureUnit);
- GLES20.glBindTexture(mTextureTarget, texId);
- GlUtils.checkError("glBindTexture " + texId);
-
- GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
- GLES20.GL_NEAREST);
- GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
- GLES20.GL_LINEAR);
- GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
- 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");
-
- return texId;
- }
-
- public void setFilter(@NonNull Filter filter) {
- // TODO see if this is needed. If setFilter is always called from the correct GL thread,
- // we don't need to wait for a new draw call (which might not even happen).
- mPendingFilter = filter;
- }
-
- public void drawFrame(long timestampUs, int textureId, float[] textureMatrix) {
- if (mPendingFilter != null) {
- release();
- mFilter = mPendingFilter;
- mPendingFilter = null;
- createProgram();
- }
-
- GlUtils.checkError("draw start");
-
- // Select the program and the active texture.
- GLES20.glUseProgram(mProgramHandle);
- GlUtils.checkError("glUseProgram");
- GLES20.glActiveTexture(mTextureUnit);
- GLES20.glBindTexture(mTextureTarget, textureId);
-
- // Draw.
- mFilter.draw(timestampUs, textureMatrix);
-
- // Release.
- GLES20.glBindTexture(mTextureTarget, 0);
- GLES20.glUseProgram(0);
- }
-}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglWindowSurface.java b/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglWindowSurface.java
deleted file mode 100644
index 0cb49776..00000000
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglWindowSurface.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2013 Google Inc. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.otaliastudios.cameraview.internal.egl;
-
-import android.graphics.SurfaceTexture;
-import android.os.Build;
-import androidx.annotation.RequiresApi;
-import android.view.Surface;
-
-/**
- * Recordable EGL window surface.
- *
- * It's good practice to explicitly release() the surface, preferably from a "finally" block.
- */
-@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
-public class EglWindowSurface extends EglBaseSurface {
- private Surface mSurface;
- private boolean mReleaseSurface;
-
- /**
- * Associates an EGL surface with the native window surface.
- *
- * Set releaseSurface to true if you want the Surface to be released when release() is
- * called. This is convenient, but can interfere with framework classes that expect to
- * manage the Surface themselves (e.g. if you release a SurfaceView's Surface, the
- * surfaceDestroyed() callback won't fire).
- */
- public EglWindowSurface(EglCore eglCore, Surface surface, boolean releaseSurface) {
- super(eglCore);
- createWindowSurface(surface);
- mSurface = surface;
- mReleaseSurface = releaseSurface;
- }
-
- /**
- * Associates an EGL surface with the SurfaceTexture.
- */
- public EglWindowSurface(EglCore eglCore, SurfaceTexture surfaceTexture) {
- super(eglCore);
- createWindowSurface(surfaceTexture);
- }
-
- /**
- * Associates an EGL surface with the Surface.
- */
- public EglWindowSurface(EglCore eglCore, Surface surface) {
- super(eglCore);
- createWindowSurface(surface);
- }
-
- /**
- * Releases any resources associated with the EGL surface (and, if configured to do so,
- * with the Surface as well).
- *
- * Does not require that the surface's EGL context be current.
- */
- public void release() {
- releaseEglSurface();
- if (mSurface != null) {
- if (mReleaseSurface) {
- mSurface.release();
- }
- mSurface = null;
- }
- }
-}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java
index 03b87222..88f72668 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java
@@ -12,8 +12,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.otaliastudios.cameraview.CameraLogger;
+import com.otaliastudios.cameraview.internal.GlTextureDrawer;
import com.otaliastudios.cameraview.internal.Issue514Workaround;
-import com.otaliastudios.cameraview.internal.egl.EglViewport;
import com.otaliastudios.cameraview.size.Size;
import java.nio.Buffer;
@@ -27,7 +27,7 @@ import java.nio.Buffer;
* - Renders this into the current EGL window: {@link #render(long)}
* - Applies the {@link Issue514Workaround} the correct way
*
- * In the future we might want to use a different approach than {@link EglViewport},
+ * In the future we might want to use a different approach than {@link GlTextureDrawer},
* {@link SurfaceTexture} and {@link GLES11Ext#GL_TEXTURE_EXTERNAL_OES},
* for example by using a regular {@link GLES20#GL_TEXTURE_2D} that might
* be filled through {@link GLES20#glTexImage2D(int, int, int, int, int, int, int, int, Buffer)}.
@@ -40,22 +40,19 @@ public class OverlayDrawer {
private static final CameraLogger LOG = CameraLogger.create(TAG);
private Overlay mOverlay;
- @VisibleForTesting int mTextureId;
private SurfaceTexture mSurfaceTexture;
private Surface mSurface;
- private float[] mTransform = new float[16];
- @VisibleForTesting EglViewport mViewport;
+ @VisibleForTesting GlTextureDrawer mTextureDrawer;
private Issue514Workaround mIssue514Workaround;
private final Object mIssue514WorkaroundLock = new Object();
public OverlayDrawer(@NonNull Overlay overlay, @NonNull Size size) {
mOverlay = overlay;
- mViewport = new EglViewport();
- mTextureId = mViewport.createTexture();
- mSurfaceTexture = new SurfaceTexture(mTextureId);
+ mTextureDrawer = new GlTextureDrawer();
+ mSurfaceTexture = new SurfaceTexture(mTextureDrawer.getTexture().getId());
mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
mSurface = new Surface(mSurfaceTexture);
- mIssue514Workaround = new Issue514Workaround(mTextureId);
+ mIssue514Workaround = new Issue514Workaround(mTextureDrawer.getTexture().getId());
}
/**
@@ -77,7 +74,7 @@ public class OverlayDrawer {
mIssue514Workaround.beforeOverlayUpdateTexImage();
mSurfaceTexture.updateTexImage();
}
- mSurfaceTexture.getTransformMatrix(mTransform);
+ mSurfaceTexture.getTransformMatrix(mTextureDrawer.getTextureTransform());
}
/**
@@ -86,7 +83,7 @@ public class OverlayDrawer {
* @return the transform matrix
*/
public float[] getTransform() {
- return mTransform;
+ return mTextureDrawer.getTextureTransform();
}
/**
@@ -105,7 +102,7 @@ public class OverlayDrawer {
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
synchronized (mIssue514WorkaroundLock) {
- mViewport.drawFrame(timestampUs, mTextureId, mTransform);
+ mTextureDrawer.draw(timestampUs);
}
}
@@ -125,9 +122,9 @@ public class OverlayDrawer {
mSurface.release();
mSurface = null;
}
- if (mViewport != null) {
- mViewport.release();
- mViewport = null;
+ if (mTextureDrawer != null) {
+ mTextureDrawer.release();
+ mTextureDrawer = null;
}
}
}
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full1PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full1PictureRecorder.java
index 7690419b..1260015a 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full1PictureRecorder.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full1PictureRecorder.java
@@ -4,10 +4,9 @@ import android.hardware.Camera;
import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.engine.Camera1Engine;
-import com.otaliastudios.cameraview.internal.utils.ExifHelper;
+import com.otaliastudios.cameraview.internal.ExifHelper;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.exifinterface.media.ExifInterface;
import java.io.ByteArrayInputStream;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java
index f4ed71c7..10ea5775 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Full2PictureRecorder.java
@@ -15,8 +15,8 @@ import com.otaliastudios.cameraview.engine.Camera2Engine;
import com.otaliastudios.cameraview.engine.action.Action;
import com.otaliastudios.cameraview.engine.action.ActionHolder;
import com.otaliastudios.cameraview.engine.action.BaseAction;
-import com.otaliastudios.cameraview.internal.utils.ExifHelper;
-import com.otaliastudios.cameraview.internal.utils.WorkerHandler;
+import com.otaliastudios.cameraview.internal.ExifHelper;
+import com.otaliastudios.cameraview.internal.WorkerHandler;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java
index 71104f4e..ab4533d3 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java
@@ -7,9 +7,9 @@ import android.hardware.Camera;
import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.engine.Camera1Engine;
import com.otaliastudios.cameraview.engine.offset.Reference;
-import com.otaliastudios.cameraview.internal.utils.CropHelper;
-import com.otaliastudios.cameraview.internal.utils.RotationHelper;
-import com.otaliastudios.cameraview.internal.utils.WorkerHandler;
+import com.otaliastudios.cameraview.internal.CropHelper;
+import com.otaliastudios.cameraview.internal.RotationHelper;
+import com.otaliastudios.cameraview.internal.WorkerHandler;
import com.otaliastudios.cameraview.size.AspectRatio;
import com.otaliastudios.cameraview.size.Size;
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java
index 35b76a37..7b2dca41 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java
@@ -10,17 +10,14 @@ import android.opengl.Matrix;
import android.os.Build;
import com.otaliastudios.cameraview.PictureResult;
-import com.otaliastudios.cameraview.internal.egl.EglBaseSurface;
+import com.otaliastudios.cameraview.internal.GlTextureDrawer;
import com.otaliastudios.cameraview.overlay.Overlay;
import com.otaliastudios.cameraview.controls.Facing;
import com.otaliastudios.cameraview.engine.CameraEngine;
import com.otaliastudios.cameraview.engine.offset.Axis;
import com.otaliastudios.cameraview.engine.offset.Reference;
-import com.otaliastudios.cameraview.internal.egl.EglCore;
-import com.otaliastudios.cameraview.internal.egl.EglViewport;
-import com.otaliastudios.cameraview.internal.egl.EglWindowSurface;
-import com.otaliastudios.cameraview.internal.utils.CropHelper;
-import com.otaliastudios.cameraview.internal.utils.WorkerHandler;
+import com.otaliastudios.cameraview.internal.CropHelper;
+import com.otaliastudios.cameraview.internal.WorkerHandler;
import com.otaliastudios.cameraview.overlay.OverlayDrawer;
import com.otaliastudios.cameraview.preview.GlCameraPreview;
import com.otaliastudios.cameraview.preview.RendererFrameCallback;
@@ -28,6 +25,9 @@ import com.otaliastudios.cameraview.preview.RendererThread;
import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.size.AspectRatio;
import com.otaliastudios.cameraview.size.Size;
+import com.otaliastudios.opengl.core.EglCore;
+import com.otaliastudios.opengl.surface.EglSurface;
+import com.otaliastudios.opengl.surface.EglWindowSurface;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
@@ -45,7 +45,7 @@ import android.view.Surface;
* - We move to another thread, and create a new EGL surface for that EGL context.
* - We make this new surface current, and re-draw the textureId on it
* - [Optional: fill the overlayTextureId and draw it on the same surface]
- * - We use glReadPixels (through {@link EglBaseSurface#saveFrameTo(Bitmap.CompressFormat)})
+ * - We use glReadPixels (through {@link EglSurface#toByteArray(Bitmap.CompressFormat)})
* and save to file.
*
* We create a new EGL surface and redraw the frame because:
@@ -62,12 +62,7 @@ public class SnapshotGlPictureRecorder extends SnapshotPictureRecorder {
private Overlay mOverlay;
private boolean mHasOverlay;
private OverlayDrawer mOverlayDrawer;
-
- private int mTextureId;
- private float[] mTransform;
-
-
- private EglViewport mViewport;
+ private GlTextureDrawer mTextureDrawer;
public SnapshotGlPictureRecorder(
@NonNull PictureResult.Stub stub,
@@ -114,14 +109,10 @@ public class SnapshotGlPictureRecorder extends SnapshotPictureRecorder {
@RendererThread
@TargetApi(Build.VERSION_CODES.KITKAT)
protected void onRendererTextureCreated(int textureId) {
- mTextureId = textureId;
- mViewport = new EglViewport();
+ mTextureDrawer = new GlTextureDrawer(textureId);
// Need to crop the size.
Rect crop = CropHelper.computeCrop(mResult.size, mOutputRatio);
mResult.size = new Size(crop.width(), crop.height());
- mTransform = new float[16];
- Matrix.setIdentityM(mTransform, 0);
-
if (mHasOverlay) {
mOverlayDrawer = new OverlayDrawer(mOverlay, mResult.size);
}
@@ -131,7 +122,7 @@ public class SnapshotGlPictureRecorder extends SnapshotPictureRecorder {
@RendererThread
@TargetApi(Build.VERSION_CODES.KITKAT)
protected void onRendererFilterChanged(@NonNull Filter filter) {
- mViewport.setFilter(filter.copy());
+ mTextureDrawer.setFilter(filter.copy());
}
@SuppressWarnings("WeakerAccess")
@@ -194,8 +185,9 @@ public class SnapshotGlPictureRecorder extends SnapshotPictureRecorder {
// 1. Create an EGL surface
final EglCore core = new EglCore(eglContext, EglCore.FLAG_RECORDABLE);
- final EglBaseSurface eglSurface = new EglWindowSurface(core, fakeOutputSurface);
+ final EglSurface eglSurface = new EglWindowSurface(core, fakeOutputSurface);
eglSurface.makeCurrent();
+ final float[] transform = mTextureDrawer.getTextureTransform();
// 2. Apply scale and crop
boolean flip = mEngine.getAngles().flip(Reference.VIEW, Reference.SENSOR);
@@ -203,17 +195,17 @@ public class SnapshotGlPictureRecorder extends SnapshotPictureRecorder {
float realScaleY = flip ? scaleX : scaleY;
float scaleTranslX = (1F - realScaleX) / 2F;
float scaleTranslY = (1F - realScaleY) / 2F;
- Matrix.translateM(mTransform, 0, scaleTranslX, scaleTranslY, 0);
- Matrix.scaleM(mTransform, 0, realScaleX, realScaleY, 1);
+ Matrix.translateM(transform, 0, scaleTranslX, scaleTranslY, 0);
+ Matrix.scaleM(transform, 0, realScaleX, realScaleY, 1);
// 3. Apply rotation and flip
- Matrix.translateM(mTransform, 0, 0.5F, 0.5F, 0); // Go back to 0,0
- Matrix.rotateM(mTransform, 0, -mResult.rotation, 0, 0, 1); // Rotate (not sure why we need the minus)
+ Matrix.translateM(transform, 0, 0.5F, 0.5F, 0); // Go back to 0,0
+ Matrix.rotateM(transform, 0, -mResult.rotation, 0, 0, 1); // Rotate (not sure why we need the minus)
mResult.rotation = 0;
if (mResult.facing == Facing.FRONT) { // 5. Flip horizontally for front camera
- Matrix.scaleM(mTransform, 0, -1, 1, 1);
+ Matrix.scaleM(transform, 0, -1, 1, 1);
}
- Matrix.translateM(mTransform, 0, -0.5F, -0.5F, 0); // Go back to old position
+ Matrix.translateM(transform, 0, -0.5F, -0.5F, 0); // Go back to old position
// 4. Do pretty much the same for overlays
if (mHasOverlay) {
@@ -232,13 +224,13 @@ public class SnapshotGlPictureRecorder extends SnapshotPictureRecorder {
// 5. Draw and save
long timestampUs = surfaceTexture.getTimestamp() / 1000L;
LOG.i("takeFrame:", "timestampUs:", timestampUs);
- mViewport.drawFrame(timestampUs, mTextureId, mTransform);
+ mTextureDrawer.draw(timestampUs);
if (mHasOverlay) mOverlayDrawer.render(timestampUs);
- mResult.data = eglSurface.saveFrameTo(Bitmap.CompressFormat.JPEG);
+ mResult.data = eglSurface.toByteArray(Bitmap.CompressFormat.JPEG);
// 6. Cleanup
- eglSurface.releaseEglSurface();
- mViewport.release();
+ eglSurface.release();
+ mTextureDrawer.release();
fakeOutputSurface.release();
if (mHasOverlay) mOverlayDrawer.release();
core.release();
diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java b/cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java
index c678d85b..58b6f065 100644
--- a/cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java
+++ b/cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java
@@ -14,7 +14,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.otaliastudios.cameraview.R;
-import com.otaliastudios.cameraview.internal.egl.EglViewport;
+import com.otaliastudios.cameraview.internal.GlTextureDrawer;
import com.otaliastudios.cameraview.filter.Filter;
import com.otaliastudios.cameraview.filter.NoFilter;
import com.otaliastudios.cameraview.size.AspectRatio;
@@ -63,10 +63,8 @@ import javax.microedition.khronos.opengles.GL10;
public class GlCameraPreview extends FilterCameraPreview {
private boolean mDispatched;
- private final float[] mTransformMatrix = new float[16];
- private int mOutputTextureId = 0;
private SurfaceTexture mInputSurfaceTexture;
- private EglViewport mOutputViewport;
+ private GlTextureDrawer mOutputTextureDrawer;
// A synchronized set was not enough to avoid crashes, probably due to external classes
// removing the callback while this set is being iterated. CopyOnWriteArraySet solves this.
private final Set mRendererFrameCallbacks = new CopyOnWriteArraySet<>();
@@ -146,14 +144,15 @@ public class GlCameraPreview extends FilterCameraPreview {
private int mTransformRotation;
private EglCore mEglCore;
private EglWindowSurface mWindow;
- private EglViewport mViewport;
+ private GlTextureDrawer mDrawer;
private Pool mFramePool = new Pool<>(Integer.MAX_VALUE, new Pool.Factory() {
@Override
public Frame create() {
@@ -99,7 +99,7 @@ public class TextureMediaEncoder extends VideoMediaEncoder {
mEglCore = new EglCore(mConfig.eglContext, EglCore.FLAG_RECORDABLE);
mWindow = new EglWindowSurface(mEglCore, mSurface, true);
mWindow.makeCurrent();
- mViewport = new EglViewport();
+ mDrawer = new GlTextureDrawer(mConfig.textureId);
}
/**
@@ -149,7 +149,7 @@ public class TextureMediaEncoder extends VideoMediaEncoder {
}
private void onFilter(@NonNull Filter filter) {
- mViewport.setFilter(filter);
+ mDrawer.setFilter(filter);
}
private void onFrame(@NonNull Frame frame) {
@@ -229,7 +229,8 @@ public class TextureMediaEncoder extends VideoMediaEncoder {
"hasReachedMaxLength:", hasReachedMaxLength(),
"thread:", Thread.currentThread(),
"- gl rendering.");
- mViewport.drawFrame(frame.timestampUs(), mConfig.textureId, transform);
+ mDrawer.setTextureTransform(transform);
+ mDrawer.draw(frame.timestampUs());
if (mConfig.hasOverlay()) {
mConfig.overlayDrawer.render(frame.timestampUs());
}
@@ -252,9 +253,9 @@ public class TextureMediaEncoder extends VideoMediaEncoder {
mWindow.release();
mWindow = null;
}
- if (mViewport != null) {
- mViewport.release();
- mViewport = null;
+ if (mDrawer != null) {
+ mDrawer.release();
+ mDrawer = null;
}
if (mEglCore != null) {
mEglCore.release();
diff --git a/cameraview/src/test/java/com/otaliastudios/cameraview/internal/utils/ExifHelperTest.java b/cameraview/src/test/java/com/otaliastudios/cameraview/internal/ExifHelperTest.java
similarity index 93%
rename from cameraview/src/test/java/com/otaliastudios/cameraview/internal/utils/ExifHelperTest.java
rename to cameraview/src/test/java/com/otaliastudios/cameraview/internal/ExifHelperTest.java
index 319f2096..eab0e6ec 100644
--- a/cameraview/src/test/java/com/otaliastudios/cameraview/internal/utils/ExifHelperTest.java
+++ b/cameraview/src/test/java/com/otaliastudios/cameraview/internal/ExifHelperTest.java
@@ -1,8 +1,10 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
import androidx.exifinterface.media.ExifInterface;
+import com.otaliastudios.cameraview.internal.ExifHelper;
+
import org.junit.Test;
import static junit.framework.Assert.assertNotNull;
diff --git a/cameraview/src/test/java/com/otaliastudios/cameraview/internal/utils/PoolTest.java b/cameraview/src/test/java/com/otaliastudios/cameraview/internal/PoolTest.java
similarity index 97%
rename from cameraview/src/test/java/com/otaliastudios/cameraview/internal/utils/PoolTest.java
rename to cameraview/src/test/java/com/otaliastudios/cameraview/internal/PoolTest.java
index 90fb874c..3fa5b1fb 100644
--- a/cameraview/src/test/java/com/otaliastudios/cameraview/internal/utils/PoolTest.java
+++ b/cameraview/src/test/java/com/otaliastudios/cameraview/internal/PoolTest.java
@@ -1,6 +1,8 @@
-package com.otaliastudios.cameraview.internal.utils;
+package com.otaliastudios.cameraview.internal;
+import com.otaliastudios.cameraview.internal.Pool;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
index 1ee1891e..bf5a4c52 100644
--- a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
+++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
@@ -48,7 +48,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
private final static CameraLogger LOG = CameraLogger.create("DemoApp");
private final static boolean USE_FRAME_PROCESSOR = true;
- private final static boolean DECODE_BITMAP = true;
+ private final static boolean DECODE_BITMAP = false;
private CameraView camera;
private ViewGroup controlPanel;
@@ -76,7 +76,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
long newTime = frame.getTime();
long delay = newTime - lastTime;
lastTime = newTime;
- LOG.e("Frame delayMillis:", delay, "FPS:", 1000 / delay);
+ LOG.v("Frame delayMillis:", delay, "FPS:", 1000 / delay);
if (DECODE_BITMAP) {
if (frame.getFormat() == ImageFormat.NV21
&& frame.getDataClass() == byte[].class) {