Update first 6 filters with onPreDraw

pull/535/head
Mattia Iavarone 6 years ago
parent 8e8dea9ddd
commit 5543b93545
  1. 30
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  2. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/filter/BaseFilter.java
  3. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/filter/Filter.java
  4. 106
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/AutoFixFilter.java
  5. 14
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/BlackAndWhiteFilter.java
  6. 58
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/BrightnessFilter.java
  7. 60
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/ContrastFilter.java
  8. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/CrossProcessFilter.java
  9. 96
      cameraview/src/main/java/com/otaliastudios/cameraview/filters/DocumentaryFilter.java
  10. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/internal/egl/EglViewport.java
  11. 7
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java

@ -2157,6 +2157,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* Use {@link NoFilter} to clear the existing filter,
* and take a look at the {@link Filters} class for commonly used filters.
*
* This method will throw an exception if the current preview does not support real-time filters.
* Make sure you use {@link Preview#GL_SURFACE} (the default).
*
* @see Filters
* @param filter a new filter
*/
@ -2164,12 +2167,35 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
if (mCameraPreview == null) {
mPendingFilter = filter;
} else if (!(filter instanceof NoFilter) && !mExperimental) {
LOG.w("Filters are an experimental features and need the experimental flag set.");
throw new RuntimeException("Filters are an experimental features and need the experimental flag set.");
} else if (mCameraPreview instanceof FilterCameraPreview) {
((FilterCameraPreview) mCameraPreview).setFilter(filter);
} else {
LOG.w("setFilter() is supported only for GL_SURFACE. Preview:", mPreview);
throw new RuntimeException("Filters are only supported by the GL_SURFACE preview. Current:" + mPreview);
}
}
/**
* Returns the current real-time filter applied to the camera preview.
*
* This method will throw an exception if the current preview does not support real-time filters.
* Make sure you use {@link Preview#GL_SURFACE} (the default).
*
* @see #setFilter(Filter)
* @return the current filter
*/
@NonNull
public Filter getFilter() {
if (!mExperimental) {
throw new RuntimeException("Filters are an experimental features and need the experimental flag set.");
} else if (mCameraPreview == null) {
return mPendingFilter;
} else if (mCameraPreview instanceof FilterCameraPreview) {
return ((FilterCameraPreview) mCameraPreview).getCurrentFilter();
} else {
throw new RuntimeException("Filters are only supported by the GL_SURFACE preview. Current:" + mPreview);
}
}
//endregion

@ -116,6 +116,7 @@ public abstract class BaseFilter implements Filter {
fragmentTextureCoordinateName);
}
@SuppressWarnings("WeakerAccess")
@NonNull
protected String createDefaultFragmentShader() {
return createDefaultFragmentShader(fragmentTextureCoordinateName);
@ -134,7 +135,7 @@ public abstract class BaseFilter implements Filter {
}
@Override
public void onDestroy(int programHandle) {
public void onDestroy() {
vertexPositionLocation = -1;
vertexTextureCoordinateLocation = -1;
vertexModelViewProjectionMatrixLocation = -1;
@ -196,6 +197,7 @@ public abstract class BaseFilter implements Filter {
GLES20.glDisableVertexAttribArray(vertexTextureCoordinateLocation);
}
@NonNull
@Override
public final BaseFilter copy() {
BaseFilter copy = onCopy();

@ -52,9 +52,8 @@ public interface Filter {
/**
* The filter program is about to be destroyed.
*
* @param programHandle handle
*/
void onDestroy(int programHandle);
void onDestroy();
/**
* Called to render the actual texture. The given transformation matrix
@ -78,5 +77,6 @@ public interface Filter {
*
* @return a clone
*/
@NonNull
Filter copy();
}

@ -1,57 +1,23 @@
package com.otaliastudios.cameraview.filters;
import android.opengl.GLES20;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
/**
* Attempts to auto-fix the frames based on histogram equalization.
*/
public class AutoFixFilter extends BaseFilter {
private float scale = 1.0f;
@SuppressWarnings("WeakerAccess")
public AutoFixFilter() { }
/**
* A parameter between 0 and 1. Zero means no adjustment, while 1 indicates
* the maximum amount of adjustment.
*
* @param scale scale
*/
public void setScale(float scale) {
if (scale < 0.0f) scale = 0.0f;
if (scale > 1.0f) scale = 1.0f;
this.scale = scale;
}
/**
* Returns the current scale.
*
* @see #setScale(float)
* @return current scale
*/
public float getScale() {
return scale;
}
@Override
protected BaseFilter onCopy() {
AutoFixFilter filter = new AutoFixFilter();
filter.setScale(getScale());
return filter;
}
@NonNull
@Override
public String getFragmentShader() {
return "#extension GL_OES_EGL_image_external : require\n"
private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES tex_sampler_0;\n"
+ "uniform samplerExternalOES tex_sampler_1;\n"
+ "uniform samplerExternalOES tex_sampler_2;\n"
+ "float scale;\n"
+ "uniform float scale;\n"
+ "float shift_scale;\n"
+ "float hist_offset;\n"
+ "float hist_scale;\n"
@ -64,7 +30,6 @@ public class AutoFixFilter extends BaseFilter {
+ " hist_scale = " + (765f / 766f) + ";\n"
+ " density_offset = " + (0.5f / 1024f) + ";\n"
+ " density_scale = " + (1023f / 1024f) + ";\n"
+ " scale = " + scale + ";\n"
+ " const vec3 weights = vec3(0.33333, 0.33333, 0.33333);\n"
+ " vec4 color = texture2D(tex_sampler_0, vTextureCoord);\n"
+ " float energy = dot(color.rgb, weights);\n"
@ -92,5 +57,66 @@ public class AutoFixFilter extends BaseFilter {
+ " gl_FragColor = vec4(color.rgb * dst_energy / energy, color.a);\n"
+ " }\n"
+ "}\n";
private float scale = 1.0f;
private int scaleLocation = -1;
@SuppressWarnings("WeakerAccess")
public AutoFixFilter() { }
/**
* A parameter between 0 and 1. Zero means no adjustment, while 1 indicates
* the maximum amount of adjustment.
*
* @param scale scale
*/
public void setScale(float scale) {
if (scale < 0.0f) scale = 0.0f;
if (scale > 1.0f) scale = 1.0f;
this.scale = scale;
}
/**
* Returns the current scale.
*
* @see #setScale(float)
* @return current scale
*/
public float getScale() {
return scale;
}
@NonNull
@Override
public String getFragmentShader() {
return FRAGMENT_SHADER;
}
@Override
public void onCreate(int programHandle) {
super.onCreate(programHandle);
scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(scaleLocation, "scale");
}
@Override
public void onDestroy() {
super.onDestroy();
scaleLocation = -1;
}
@Override
protected void onPreDraw(float[] transformMatrix) {
super.onPreDraw(transformMatrix);
GLES20.glUniform1f(scaleLocation, scale);
GlUtils.checkError("glUniform1f");
}
@Override
protected BaseFilter onCopy() {
AutoFixFilter filter = new AutoFixFilter();
filter.setScale(getScale());
return filter;
}
}

@ -9,12 +9,7 @@ import com.otaliastudios.cameraview.filter.BaseFilter;
*/
public class BlackAndWhiteFilter extends BaseFilter {
public BlackAndWhiteFilter() { }
@NonNull
@Override
public String getFragmentShader() {
return "#extension GL_OES_EGL_image_external : require\n"
private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "varying vec2 vTextureCoord;\n"
+ "uniform samplerExternalOES sTexture;\n" + "void main() {\n"
@ -24,5 +19,12 @@ public class BlackAndWhiteFilter extends BaseFilter {
+ " float colorB = (color.r + color.g + color.b) / 3.0;\n"
+ " gl_FragColor = vec4(colorR, colorG, colorB, color.a);\n"
+ "}\n";
public BlackAndWhiteFilter() { }
@NonNull
@Override
public String getFragmentShader() {
return FRAGMENT_SHADER;
}
}

@ -1,15 +1,30 @@
package com.otaliastudios.cameraview.filters;
import android.opengl.GLES20;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
/**
* Adjusts the brightness of the frames.
*/
public class BrightnessFilter extends BaseFilter {
private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES sTexture;\n"
+ "uniform float brightness;\n"
+ "varying vec2 vTextureCoord;\n"
+ "void main() {\n"
+ " vec4 color = texture2D(sTexture, vTextureCoord);\n"
+ " gl_FragColor = brightness * color;\n"
+ "}\n";
private float brightness = 2.0f;
private int brightnessLocation = -1;
@SuppressWarnings("WeakerAccess")
public BrightnessFilter() { }
@ -44,26 +59,37 @@ public class BrightnessFilter extends BaseFilter {
return brightness - 1.0f;
}
@NonNull
@Override
protected BaseFilter onCopy() {
BrightnessFilter filter = new BrightnessFilter();
filter.setBrightness(getBrightness());
return filter;
public String getFragmentShader() {
return FRAGMENT_SHADER;
}
@NonNull
@Override
public String getFragmentShader() {
return "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES sTexture;\n"
+ "float brightness ;\n"
+ "varying vec2 vTextureCoord;\n"
+ "void main() {\n"
+ " brightness =" + brightness + ";\n"
+ " vec4 color = texture2D(sTexture, vTextureCoord);\n"
+ " gl_FragColor = brightness * color;\n"
+ "}\n";
public void onCreate(int programHandle) {
super.onCreate(programHandle);
brightnessLocation = GLES20.glGetUniformLocation(programHandle, "brightness");
GlUtils.checkLocation(brightnessLocation, "brightness");
}
@Override
public void onDestroy() {
super.onDestroy();
brightnessLocation = -1;
}
@Override
protected void onPreDraw(float[] transformMatrix) {
super.onPreDraw(transformMatrix);
GLES20.glUniform1f(brightnessLocation, brightness);
GlUtils.checkError("glUniform1f");
}
@Override
protected BaseFilter onCopy() {
BrightnessFilter filter = new BrightnessFilter();
filter.setBrightness(getBrightness());
return filter;
}
}

@ -1,14 +1,32 @@
package com.otaliastudios.cameraview.filters;
import android.opengl.GLES20;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
/**
* Adjusts the contrast.
*/
public class ContrastFilter extends BaseFilter {
private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES sTexture;\n"
+ "uniform float contrast;\n"
+ "varying vec2 vTextureCoord;\n"
+ "void main() {\n"
+ " vec4 color = texture2D(sTexture, vTextureCoord);\n"
+ " color -= 0.5;\n"
+ " color *= contrast;\n"
+ " color += 0.5;\n"
+ " gl_FragColor = color;\n"
+ "}\n";
private float contrast = 2.0f;
private int contrastLocation = -1;
@SuppressWarnings("WeakerAccess")
public ContrastFilter() { }
@ -42,30 +60,36 @@ public class ContrastFilter extends BaseFilter {
return contrast - 1.0f;
}
@NonNull
@Override
public String getFragmentShader() {
return FRAGMENT_SHADER;
}
@Override
protected BaseFilter onCopy() {
ContrastFilter filter = new ContrastFilter();
filter.setContrast(getContrast());
return filter;
public void onCreate(int programHandle) {
super.onCreate(programHandle);
contrastLocation = GLES20.glGetUniformLocation(programHandle, "contrast");
GlUtils.checkLocation(contrastLocation, "contrast");
}
@NonNull
@Override
public String getFragmentShader() {
return "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES sTexture;\n"
+ "float contrast;\n"
+ "varying vec2 vTextureCoord;\n"
+ "void main() {\n"
+ " contrast =" + contrast + ";\n"
+ " vec4 color = texture2D(sTexture, vTextureCoord);\n"
+ " color -= 0.5;\n"
+ " color *= contrast;\n"
+ " color += 0.5;\n"
+ " gl_FragColor = color;\n" + "}\n";
public void onDestroy() {
super.onDestroy();
contrastLocation = -1;
}
@Override
protected void onPreDraw(float[] transformMatrix) {
super.onPreDraw(transformMatrix);
GLES20.glUniform1f(contrastLocation, contrast);
GlUtils.checkError("glUniform1f");
}
@Override
protected BaseFilter onCopy() {
ContrastFilter filter = new ContrastFilter();
filter.setContrast(getContrast());
return filter;
}
}

@ -10,12 +10,7 @@ import com.otaliastudios.cameraview.filter.BaseFilter;
*/
public class CrossProcessFilter extends BaseFilter {
public CrossProcessFilter() { }
@NonNull
@Override
public String getFragmentShader() {
return "#extension GL_OES_EGL_image_external : require\n"
private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES sTexture;\n"
+ "varying vec2 vTextureCoord;\n"
@ -48,6 +43,12 @@ public class CrossProcessFilter extends BaseFilter {
+ " ncolor.b = color.b * 0.5 + 0.25;\n"
+ " gl_FragColor = vec4(ncolor.rgb, color.a);\n"
+ "}\n";
}
public CrossProcessFilter() { }
@NonNull
@Override
public String getFragmentShader() {
return FRAGMENT_SHADER;
}
}

@ -1,10 +1,12 @@
package com.otaliastudios.cameraview.filters;
import android.opengl.GLES20;
import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.filter.BaseFilter;
import com.otaliastudios.cameraview.internal.GlUtils;
import java.util.Date;
import java.util.Random;
/**
@ -12,54 +14,31 @@ import java.util.Random;
*/
public class DocumentaryFilter extends BaseFilter {
private final Random mRandom = new Random(new Date().getTime());
private int mOutputWidth = 1;
private int mOutputHeight = 1;
private final Random mRandom = new Random();
private int mWidth = 1;
private int mHeight = 1;
private int mScaleLocation = -1;
private int mMaxDistLocation = -1;
public DocumentaryFilter() { }
@Override
public void setSize(int width, int height) {
super.setSize(width, height);
mOutputWidth = width;
mOutputHeight = height;
mWidth = width;
mHeight = height;
}
@NonNull
@Override
public String getFragmentShader() {
float[] scale = new float[2];
if (mOutputWidth > mOutputHeight) {
scale[0] = 1f;
scale[1] = ((float) mOutputHeight) / mOutputWidth;
} else {
scale[0] = ((float) mOutputWidth) / mOutputHeight;
scale[1] = 1f;
}
float max_dist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1]
* scale[1])) * 0.5f;
float[] seed = {mRandom.nextFloat(), mRandom.nextFloat()};
String[] scaleString = new String[2];
String[] seedString = new String[2];
scaleString[0] = "scale[0] = " + scale[0] + ";\n";
scaleString[1] = "scale[1] = " + scale[1] + ";\n";
seedString[0] = "seed[0] = " + seed[0] + ";\n";
seedString[1] = "seed[1] = " + seed[1] + ";\n";
String inv_max_distString = "inv_max_dist = " + 1.0f / max_dist + ";\n";
String stepsizeString = "stepsize = " + 1.0f / 255.0f + ";\n";
return "#extension GL_OES_EGL_image_external : require\n"
+ "precision mediump float;\n"
+ "uniform samplerExternalOES sTexture;\n"
+ " vec2 seed;\n"
+ " float stepsize;\n"
+ " float inv_max_dist;\n"
+ " vec2 scale;\n"
+ "vec2 seed;\n"
+ "float stepsize;\n"
+ "uniform float inv_max_dist;\n"
+ "uniform vec2 scale;\n"
+ "varying vec2 vTextureCoord;\n"
+ "float rand(vec2 loc) {\n"
+ " float theta1 = dot(loc, vec2(0.9898, 0.233));\n"
@ -74,13 +53,9 @@ public class DocumentaryFilter extends BaseFilter {
+ " return fract(part1 + part2 + part3);\n"
+ "}\n"
+ "void main() {\n"
// Parameters that were created above
+ scaleString[0]
+ scaleString[1]
+ seedString[0]
+ seedString[1]
+ inv_max_distString
+ stepsizeString
+ " seed[0] = " + mRandom.nextFloat() + ";\n"
+ " seed[1] = " + mRandom.nextFloat() + ";\n"
+ " stepsize = " + 1.0f / 255.0f + ";\n"
// black white
+ " vec4 color = texture2D(sTexture, vTextureCoord);\n"
@ -98,4 +73,41 @@ public class DocumentaryFilter extends BaseFilter {
+ " gl_FragColor = vec4(new_color * lumen, color.a);\n"
+ "}\n";
}
@Override
public void onCreate(int programHandle) {
super.onCreate(programHandle);
mScaleLocation = GLES20.glGetUniformLocation(programHandle, "scale");
GlUtils.checkLocation(mScaleLocation, "scale");
mMaxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist");
GlUtils.checkLocation(mMaxDistLocation, "inv_max_dist");
}
@Override
public void onDestroy() {
super.onDestroy();
mScaleLocation = -1;
mMaxDistLocation = -1;
}
@Override
protected void onPreDraw(float[] transformMatrix) {
super.onPreDraw(transformMatrix);
float[] scale = new float[2];
if (mWidth > mHeight) {
scale[0] = 1f;
scale[1] = ((float) mHeight) / mWidth;
} else {
scale[0] = ((float) mWidth) / mHeight;
scale[1] = 1f;
}
GLES20.glUniform2fv(mScaleLocation, 1, scale, 0);
GlUtils.checkError("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");
}
}

@ -42,7 +42,7 @@ public class EglViewport {
public void release() {
if (mProgramHandle != -1) {
mFilter.onDestroy(mProgramHandle);
mFilter.onDestroy();
GLES20.glDeleteProgram(mProgramHandle);
mProgramHandle = -1;
}

@ -28,6 +28,7 @@ import com.otaliastudios.cameraview.controls.Mode;
import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Preview;
import com.otaliastudios.cameraview.filter.Filters;
import com.otaliastudios.cameraview.filters.BrightnessFilter;
import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor;
@ -331,6 +332,12 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
mCurrentFilter = 0;
}
camera.setFilter(mAllFilters[mCurrentFilter].newInstance());
/* BrightnessFilter filter = (BrightnessFilter) camera.getFilter();
if (filter.getBrightness() == 1.0F) {
filter.setBrightness(0.0F);
} else {
filter.setBrightness(filter.getBrightness() + 0.2F);
} */
}
@Override

Loading…
Cancel
Save