|
|
|
@ -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,10 +14,51 @@ import java.util.Random; |
|
|
|
|
*/ |
|
|
|
|
public class GrainFilter extends BaseFilter { |
|
|
|
|
|
|
|
|
|
private final static Random RANDOM = new Random(); |
|
|
|
|
private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n" |
|
|
|
|
+ "precision mediump float;\n" |
|
|
|
|
+ "vec2 seed;\n" |
|
|
|
|
+ "varying vec2 vTextureCoord;\n" |
|
|
|
|
+ "uniform samplerExternalOES tex_sampler_0;\n" |
|
|
|
|
+ "uniform samplerExternalOES tex_sampler_1;\n" |
|
|
|
|
+ "uniform float scale;\n" |
|
|
|
|
+ "uniform float stepX;\n" |
|
|
|
|
+ "uniform float stepY;\n" |
|
|
|
|
+ "float rand(vec2 loc) {\n" |
|
|
|
|
+ " float theta1 = dot(loc, vec2(0.9898, 0.233));\n" |
|
|
|
|
+ " float theta2 = dot(loc, vec2(12.0, 78.0));\n" |
|
|
|
|
+ " float value = cos(theta1) * sin(theta2) + sin(theta1) * cos(theta2);\n" |
|
|
|
|
// keep value of part1 in range: (2^-14 to 2^14).
|
|
|
|
|
+ " float temp = mod(197.0 * value, 1.0) + value;\n" |
|
|
|
|
+ " float part1 = mod(220.0 * temp, 1.0) + temp;\n" |
|
|
|
|
+ " float part2 = value * 0.5453;\n" |
|
|
|
|
+ " float part3 = cos(theta1 + theta2) * 0.43758;\n" |
|
|
|
|
+ " float sum = (part1 + part2 + part3);\n" |
|
|
|
|
+ " return fract(sum)*scale;\n" |
|
|
|
|
+ "}\n" |
|
|
|
|
+ "void main() {\n" |
|
|
|
|
+ " seed[0] = " + RANDOM.nextFloat() + ";\n" |
|
|
|
|
+ " seed[1] = " + RANDOM.nextFloat() + ";\n" |
|
|
|
|
+ " float noise = texture2D(tex_sampler_1, vTextureCoord + vec2(-stepX, -stepY)).r * 0.224;\n" |
|
|
|
|
+ " noise += texture2D(tex_sampler_1, vTextureCoord + vec2(-stepX, stepY)).r * 0.224;\n" |
|
|
|
|
+ " noise += texture2D(tex_sampler_1, vTextureCoord + vec2(stepX, -stepY)).r * 0.224;\n" |
|
|
|
|
+ " noise += texture2D(tex_sampler_1, vTextureCoord + vec2(stepX, stepY)).r * 0.224;\n" |
|
|
|
|
+ " noise += 0.4448;\n" |
|
|
|
|
+ " noise *= scale;\n" |
|
|
|
|
+ " vec4 color = texture2D(tex_sampler_0, vTextureCoord);\n" |
|
|
|
|
+ " float energy = 0.33333 * color.r + 0.33333 * color.g + 0.33333 * color.b;\n" |
|
|
|
|
+ " float mask = (1.0 - sqrt(energy));\n" |
|
|
|
|
+ " float weight = 1.0 - 1.333 * mask * noise;\n" |
|
|
|
|
+ " gl_FragColor = vec4(color.rgb * weight, color.a);\n" |
|
|
|
|
+ " gl_FragColor = gl_FragColor+vec4(rand(vTextureCoord + seed), rand(vTextureCoord + seed),rand(vTextureCoord + seed),1);\n" |
|
|
|
|
+ "}\n"; |
|
|
|
|
|
|
|
|
|
private float strength = 0.5f; |
|
|
|
|
private Random mRandom = new Random(new Date().getTime()); |
|
|
|
|
private int mOutputWidth = 1; |
|
|
|
|
private int mOutputHeight = 1; |
|
|
|
|
private int width = 1; |
|
|
|
|
private int height = 1; |
|
|
|
|
private int strengthLocation = -1; |
|
|
|
|
private int stepXLocation = -1; |
|
|
|
|
private int stepYLocation = -1; |
|
|
|
|
|
|
|
|
|
@SuppressWarnings("WeakerAccess") |
|
|
|
|
public GrainFilter() { } |
|
|
|
@ -23,8 +66,8 @@ public class GrainFilter extends BaseFilter { |
|
|
|
|
@Override |
|
|
|
|
public void setSize(int width, int height) { |
|
|
|
|
super.setSize(width, height); |
|
|
|
|
mOutputWidth = width; |
|
|
|
|
mOutputHeight = height; |
|
|
|
|
this.width = width; |
|
|
|
|
this.height = height; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -52,68 +95,47 @@ public class GrainFilter extends BaseFilter { |
|
|
|
|
return strength; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected BaseFilter onCopy() { |
|
|
|
|
GrainFilter filter = new GrainFilter(); |
|
|
|
|
filter.setStrength(getStrength()); |
|
|
|
|
return filter; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@NonNull |
|
|
|
|
@Override |
|
|
|
|
public String getFragmentShader() { |
|
|
|
|
float[] seed = {mRandom.nextFloat(), mRandom.nextFloat()}; |
|
|
|
|
String scaleString = "scale = " + strength + ";\n"; |
|
|
|
|
String[] seedString = new String[2]; |
|
|
|
|
seedString[0] = "seed[0] = " + seed[0] + ";\n"; |
|
|
|
|
seedString[1] = "seed[1] = " + seed[1] + ";\n"; |
|
|
|
|
String stepX = "stepX = " + 0.5f / mOutputWidth + ";\n"; |
|
|
|
|
String stepY = "stepY = " + 0.5f / mOutputHeight + ";\n"; |
|
|
|
|
return FRAGMENT_SHADER; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// locString[1] = "loc[1] = loc[1]+" + seedString[1] + ";\n";
|
|
|
|
|
@Override |
|
|
|
|
public void onCreate(int programHandle) { |
|
|
|
|
super.onCreate(programHandle); |
|
|
|
|
strengthLocation = GLES20.glGetUniformLocation(programHandle, "scale"); |
|
|
|
|
GlUtils.checkLocation(strengthLocation, "scale"); |
|
|
|
|
stepXLocation = GLES20.glGetUniformLocation(programHandle, "stepX"); |
|
|
|
|
GlUtils.checkLocation(stepXLocation, "stepX"); |
|
|
|
|
stepYLocation = GLES20.glGetUniformLocation(programHandle, "stepY"); |
|
|
|
|
GlUtils.checkLocation(stepYLocation, "stepY"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return "#extension GL_OES_EGL_image_external : require\n" |
|
|
|
|
+ "precision mediump float;\n" |
|
|
|
|
+ " vec2 seed;\n" |
|
|
|
|
+ "varying vec2 vTextureCoord;\n" |
|
|
|
|
+ "uniform samplerExternalOES tex_sampler_0;\n" |
|
|
|
|
+ "uniform samplerExternalOES tex_sampler_1;\n" |
|
|
|
|
+ "float scale;\n" |
|
|
|
|
+ " float stepX;\n" |
|
|
|
|
+ " float stepY;\n" |
|
|
|
|
+ "float rand(vec2 loc) {\n" |
|
|
|
|
+ " float theta1 = dot(loc, vec2(0.9898, 0.233));\n" |
|
|
|
|
+ " float theta2 = dot(loc, vec2(12.0, 78.0));\n" |
|
|
|
|
+ " float value = cos(theta1) * sin(theta2) + sin(theta1) * cos(theta2);\n" |
|
|
|
|
+ |
|
|
|
|
// keep value of part1 in range: (2^-14 to 2^14).
|
|
|
|
|
" float temp = mod(197.0 * value, 1.0) + value;\n" |
|
|
|
|
+ " float part1 = mod(220.0 * temp, 1.0) + temp;\n" |
|
|
|
|
+ " float part2 = value * 0.5453;\n" |
|
|
|
|
+ " float part3 = cos(theta1 + theta2) * 0.43758;\n" |
|
|
|
|
+ " float sum = (part1 + part2 + part3);\n" |
|
|
|
|
+ " return fract(sum)*scale;\n" |
|
|
|
|
+ "}\n" |
|
|
|
|
+ "void main() {\n" |
|
|
|
|
// Parameters that were created above
|
|
|
|
|
+ seedString[0] |
|
|
|
|
+ seedString[1] |
|
|
|
|
+ scaleString |
|
|
|
|
+ stepX |
|
|
|
|
+ stepY |
|
|
|
|
+ " float noise = texture2D(tex_sampler_1, vTextureCoord + vec2(-stepX, -stepY)).r * 0.224;\n" |
|
|
|
|
+ " noise += texture2D(tex_sampler_1, vTextureCoord + vec2(-stepX, stepY)).r * 0.224;\n" |
|
|
|
|
+ " noise += texture2D(tex_sampler_1, vTextureCoord + vec2(stepX, -stepY)).r * 0.224;\n" |
|
|
|
|
+ " noise += texture2D(tex_sampler_1, vTextureCoord + vec2(stepX, stepY)).r * 0.224;\n" |
|
|
|
|
+ " noise += 0.4448;\n" |
|
|
|
|
+ " noise *= scale;\n" |
|
|
|
|
+ " vec4 color = texture2D(tex_sampler_0, vTextureCoord);\n" |
|
|
|
|
+ " float energy = 0.33333 * color.r + 0.33333 * color.g + 0.33333 * color.b;\n" |
|
|
|
|
+ " float mask = (1.0 - sqrt(energy));\n" |
|
|
|
|
+ " float weight = 1.0 - 1.333 * mask * noise;\n" |
|
|
|
|
+ " gl_FragColor = vec4(color.rgb * weight, color.a);\n" |
|
|
|
|
+ " gl_FragColor = gl_FragColor+vec4(rand(vTextureCoord + seed), rand(vTextureCoord + seed),rand(vTextureCoord + seed),1);\n" |
|
|
|
|
+ "}\n"; |
|
|
|
|
@Override |
|
|
|
|
public void onDestroy() { |
|
|
|
|
super.onDestroy(); |
|
|
|
|
strengthLocation = -1; |
|
|
|
|
stepXLocation = -1; |
|
|
|
|
stepYLocation = -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected void onPreDraw(float[] transformMatrix) { |
|
|
|
|
super.onPreDraw(transformMatrix); |
|
|
|
|
GLES20.glUniform1f(strengthLocation, strength); |
|
|
|
|
GlUtils.checkError("glUniform1f"); |
|
|
|
|
GLES20.glUniform1f(stepXLocation, 0.5f / width); |
|
|
|
|
GlUtils.checkError("glUniform1f"); |
|
|
|
|
GLES20.glUniform1f(stepYLocation, 0.5f / height); |
|
|
|
|
GlUtils.checkError("glUniform1f"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
protected BaseFilter onCopy() { |
|
|
|
|
GrainFilter filter = new GrainFilter(); |
|
|
|
|
filter.setStrength(getStrength()); |
|
|
|
|
return filter; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|