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 55497cbf..0d3ec794 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GammaFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GammaFilter.java @@ -1,15 +1,29 @@ 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; /** * Applies gamma correction to the frames. */ public class GammaFilter extends BaseFilter { + 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" + + "uniform float gamma;\n" + + "void main() {\n" + + " vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" + + " gl_FragColor = vec4(pow(textureColor.rgb, vec3(gamma)), textureColor.w);\n" + + "}\n"; + private float gamma = 2.0f; + private int gammaLocation = -1; @SuppressWarnings("WeakerAccess") public GammaFilter() { } @@ -42,24 +56,37 @@ public class GammaFilter extends BaseFilter { return gamma / 2.0f; } + @NonNull + @Override + public String getFragmentShader() { + return FRAGMENT_SHADER; + } + + @Override + public void onCreate(int programHandle) { + super.onCreate(programHandle); + gammaLocation = GLES20.glGetUniformLocation(programHandle, "gamma"); + GlUtils.checkLocation(gammaLocation, "gamma"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + gammaLocation = -1; + } + + @Override + protected void onPreDraw(float[] transformMatrix) { + super.onPreDraw(transformMatrix); + GLES20.glUniform1f(gammaLocation, gamma); + GlUtils.checkError("glUniform1f"); + + } + @Override protected BaseFilter onCopy() { GammaFilter filter = new GammaFilter(); filter.setGamma(getGamma()); return filter; } - - @NonNull - @Override - public String getFragmentShader() { - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "varying vec2 vTextureCoord;\n" - + "uniform samplerExternalOES sTexture;\n" - + "float gamma=" + gamma + ";\n" - + "void main() {\n" - + " vec4 textureColor = texture2D(sTexture, vTextureCoord);\n" - + " gl_FragColor = vec4(pow(textureColor.rgb, vec3(gamma)), textureColor.w);\n" - + "}\n"; - } } \ 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 e24fba6d..09285996 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrainFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrainFilter.java @@ -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"; - - // locString[1] = "loc[1] = loc[1]+" + seedString[1] + ";\n"; - - 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"; + return FRAGMENT_SHADER; + } + + @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"); + } + @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; } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrayscaleFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrayscaleFilter.java index 8905fe3c..a4a49f3d 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrayscaleFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/GrayscaleFilter.java @@ -9,19 +9,21 @@ import com.otaliastudios.cameraview.filter.BaseFilter; */ public class GrayscaleFilter extends BaseFilter { + 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" + + "void main() {\n" + + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + + " float y = dot(color, vec4(0.299, 0.587, 0.114, 0));\n" + + " gl_FragColor = vec4(y, y, y, color.a);\n" + + "}\n"; + public GrayscaleFilter() { } @NonNull @Override public String getFragmentShader() { - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "uniform samplerExternalOES sTexture;\n" - + "varying vec2 vTextureCoord;\n" - + "void main() {\n" - + " vec4 color = texture2D(sTexture, vTextureCoord);\n" - + " float y = dot(color, vec4(0.299, 0.587, 0.114, 0));\n" - + " gl_FragColor = vec4(y, y, y, color.a);\n" - + "}\n"; + return FRAGMENT_SHADER; } } 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 5d8ed430..f1f8f21d 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/HueFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/HueFilter.java @@ -1,15 +1,45 @@ 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; /** * Applies a hue effect on the input frames. */ public class HueFilter extends BaseFilter { - private float hueValue = 0.0f; + 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" + + "uniform float hue;\n" + + "void main() {\n" + + " vec4 kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);\n" + + " vec4 kRGBToI = vec4 (0.595716, -0.274453, -0.321263, 0.0);\n" + + " vec4 kRGBToQ = vec4 (0.211456, -0.522591, 0.31135, 0.0);\n" + + " vec4 kYIQToR = vec4 (1.0, 0.9563, 0.6210, 0.0);\n" + + " vec4 kYIQToG = vec4 (1.0, -0.2721, -0.6474, 0.0);\n" + + " vec4 kYIQToB = vec4 (1.0, -1.1070, 1.7046, 0.0);\n" + + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + + " float YPrime = dot(color, kRGBToYPrime);\n" + + " float I = dot(color, kRGBToI);\n" + + " float Q = dot(color, kRGBToQ);\n" + + " float chroma = sqrt (I * I + Q * Q);\n" + + " Q = chroma * sin (hue);\n" + + " I = chroma * cos (hue);\n" + + " vec4 yIQ = vec4 (YPrime, I, Q, 0.0);\n" + + " color.r = dot (yIQ, kYIQToR);\n" + + " color.g = dot (yIQ, kYIQToG);\n" + + " color.b = dot (yIQ, kYIQToB);\n" + + " gl_FragColor = color;\n" + + "}\n"; + + private float hue = 0.0f; + private int hueLocation = -1; @SuppressWarnings("WeakerAccess") public HueFilter() { } @@ -17,47 +47,57 @@ public class HueFilter extends BaseFilter { /** * Sets the hue value in degrees. See the values chart: * https://cloud.githubusercontent.com/assets/2201511/21810115/b99ac22a-d74a-11e6-9f6c-ef74d15c88c7.jpg + * + * @param hue hue degrees */ - @SuppressWarnings("unused") - public void setHue(float degrees) { - // manipulating input value so that we can map it on 360 degree circle - hueValue = ((degrees - 45) / 45f + 0.5f) * -1; + @SuppressWarnings({"unused", "WeakerAccess"}) + public void setHue(float hue) { + this.hue = hue; } - @Override - protected BaseFilter onCopy() { - HueFilter filter = new HueFilter(); - filter.hueValue = hueValue; - return filter; + /** + * Returns the current hue value. + * + * @see #setHue(float) + * @return hue + */ + @SuppressWarnings("WeakerAccess") + public float getHue() { + return hue; } @NonNull @Override public String getFragmentShader() { - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "varying vec2 vTextureCoord;\n" - + "uniform samplerExternalOES sTexture;\n" - + "float hue=" + hueValue + ";\n" - + "void main() {\n" - + " vec4 kRGBToYPrime = vec4 (0.299, 0.587, 0.114, 0.0);\n" - + " vec4 kRGBToI = vec4 (0.595716, -0.274453, -0.321263, 0.0);\n" - + " vec4 kRGBToQ = vec4 (0.211456, -0.522591, 0.31135, 0.0);\n" - + " vec4 kYIQToR = vec4 (1.0, 0.9563, 0.6210, 0.0);\n" - + " vec4 kYIQToG = vec4 (1.0, -0.2721, -0.6474, 0.0);\n" - + " vec4 kYIQToB = vec4 (1.0, -1.1070, 1.7046, 0.0);\n" - + " vec4 color = texture2D(sTexture, vTextureCoord);\n" - + " float YPrime = dot(color, kRGBToYPrime);\n" - + " float I = dot(color, kRGBToI);\n" - + " float Q = dot(color, kRGBToQ);\n" - + " float chroma = sqrt (I * I + Q * Q);\n" - + " Q = chroma * sin (hue);\n" - + " I = chroma * cos (hue);\n" - + " vec4 yIQ = vec4 (YPrime, I, Q, 0.0);\n" - + " color.r = dot (yIQ, kYIQToR);\n" - + " color.g = dot (yIQ, kYIQToG);\n" - + " color.b = dot (yIQ, kYIQToB);\n" - + " gl_FragColor = color;\n" - + "}\n"; + return FRAGMENT_SHADER; + } + + @Override + public void onCreate(int programHandle) { + super.onCreate(programHandle); + hueLocation = GLES20.glGetUniformLocation(programHandle, "hue"); + GlUtils.checkLocation(hueLocation, "hue"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + hueLocation = -1; + } + + @Override + protected void onPreDraw(float[] transformMatrix) { + super.onPreDraw(transformMatrix); + // map it on 360 degree circle + float shaderHue = ((hue - 45) / 45f + 0.5f) * -1; + GLES20.glUniform1f(hueLocation, shaderHue); + GlUtils.checkError("glUniform1f"); + } + + @Override + protected BaseFilter onCopy() { + HueFilter filter = new HueFilter(); + filter.setHue(getHue()); + return filter; } } \ No newline at end of file diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/InvertColorsFilter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/InvertColorsFilter.java index 53be7112..a5490d88 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/InvertColorsFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/InvertColorsFilter.java @@ -9,22 +9,23 @@ import com.otaliastudios.cameraview.filter.BaseFilter; */ public class InvertColorsFilter extends BaseFilter { + 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" + + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + + " float colorR = (1.0 - color.r) / 1.0;\n" + + " float colorG = (1.0 - color.g) / 1.0;\n" + + " float colorB = (1.0 - color.b) / 1.0;\n" + + " gl_FragColor = vec4(colorR, colorG, colorB, color.a);\n" + + "}\n"; + public InvertColorsFilter() { } @NonNull @Override public String getFragmentShader() { - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "varying vec2 vTextureCoord;\n" - + "uniform samplerExternalOES sTexture;\n" - + "void main() {\n" - + " vec4 color = texture2D(sTexture, vTextureCoord);\n" - + " float colorR = (1.0 - color.r) / 1.0;\n" - + " float colorG = (1.0 - color.g) / 1.0;\n" - + " float colorB = (1.0 - color.b) / 1.0;\n" - + " gl_FragColor = vec4(colorR, colorG, colorB, color.a);\n" - + "}\n"; - + return FRAGMENT_SHADER; } } 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 471d9be4..a6f3ea39 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/LomoishFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/LomoishFilter.java @@ -1,8 +1,11 @@ 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,132 +15,151 @@ import java.util.Random; */ public class LomoishFilter extends BaseFilter { - private final Random mRandom = new Random(new Date().getTime()); - private int mOutputWidth = 1; - private int mOutputHeight = 1; + 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" + + "uniform samplerExternalOES sTexture;\n" + + "vec2 seed;\n" + + "uniform float stepsizeX;\n" + + "uniform float stepsizeY;\n" + + "float stepsize;\n" + + "uniform vec2 scale;\n" + + "uniform float inv_max_dist;\n" + + "varying vec2 vTextureCoord;\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" + + " return fract(part1 + part2 + part3);\n" + + "}\n" + + "void main() {\n" + + " seed[0] = " + RANDOM.nextFloat() + ";\n" + + " seed[1] = " + RANDOM.nextFloat() + ";\n" + + " stepsize = " + 1.0f / 255.0f + ";\n" + // sharpen + + " vec3 nbr_color = vec3(0.0, 0.0, 0.0);\n" + + " vec2 coord;\n" + + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + + " coord.x = vTextureCoord.x - 0.5 * stepsizeX;\n" + + " coord.y = vTextureCoord.y - stepsizeY;\n" + + " nbr_color += texture2D(sTexture, coord).rgb - color.rgb;\n" + + " coord.x = vTextureCoord.x - stepsizeX;\n" + + " coord.y = vTextureCoord.y + 0.5 * stepsizeY;\n" + + " nbr_color += texture2D(sTexture, coord).rgb - color.rgb;\n" + + " coord.x = vTextureCoord.x + stepsizeX;\n" + + " coord.y = vTextureCoord.y - 0.5 * stepsizeY;\n" + + " nbr_color += texture2D(sTexture, coord).rgb - color.rgb;\n" + + " coord.x = vTextureCoord.x + stepsizeX;\n" + + " coord.y = vTextureCoord.y + 0.5 * stepsizeY;\n" + + " nbr_color += texture2D(sTexture, coord).rgb - color.rgb;\n" + + " vec3 s_color = vec3(color.rgb + 0.3 * nbr_color);\n" + // cross process + + " vec3 c_color = vec3(0.0, 0.0, 0.0);\n" + + " float value;\n" + + " if (s_color.r < 0.5) {\n" + + " value = s_color.r;\n" + + " } else {\n" + + " value = 1.0 - s_color.r;\n" + + " }\n" + + " float red = 4.0 * value * value * value;\n" + + " if (s_color.r < 0.5) {\n" + + " c_color.r = red;\n" + + " } else {\n" + + " c_color.r = 1.0 - red;\n" + + " }\n" + + " if (s_color.g < 0.5) {\n" + + " value = s_color.g;\n" + + " } else {\n" + + " value = 1.0 - s_color.g;\n" + + " }\n" + + " float green = 2.0 * value * value;\n" + + " if (s_color.g < 0.5) {\n" + + " c_color.g = green;\n" + + " } else {\n" + + " c_color.g = 1.0 - green;\n" + + " }\n" + + " c_color.b = s_color.b * 0.5 + 0.25;\n" + // blackwhite + + " float dither = rand(vTextureCoord + seed);\n" + + " vec3 xform = clamp((c_color.rgb - 0.15) * 1.53846, 0.0, 1.0);\n" + + " vec3 temp = clamp((color.rgb + stepsize - 0.15) * 1.53846, 0.0, 1.0);\n" + + " vec3 bw_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" + // vignette + + " coord = vTextureCoord - vec2(0.5, 0.5);\n" + + " float dist = length(coord * scale);\n" + + " float lumen = 0.85 / (1.0 + exp((dist * inv_max_dist - 0.73) * 20.0)) + 0.15;\n" + + " gl_FragColor = vec4(bw_color * lumen, color.a);\n" + + "}\n"; + + private int width = 1; + private int height = 1; + + private int scaleLocation = -1; + private int maxDistLocation = -1; + private int stepSizeXLocation = -1; + private int stepSizeYLocation = -1; public LomoishFilter() { } @Override public void setSize(int width, int height) { super.setSize(width, height); - mOutputWidth = width; - mOutputHeight = height; + this.width = width; + this.height = height; } @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"); + maxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist"); + GlUtils.checkLocation(maxDistLocation, "inv_max_dist"); + stepSizeXLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeX"); + GlUtils.checkLocation(stepSizeXLocation, "stepsizeX"); + stepSizeYLocation = GLES20.glGetUniformLocation(programHandle, "stepsizeY"); + GlUtils.checkLocation(stepSizeYLocation, "stepsizeY"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + scaleLocation = -1; + maxDistLocation = -1; + stepSizeXLocation = -1; + stepSizeYLocation = -1; + } + + @Override + protected void onPreDraw(float[] transformMatrix) { + super.onPreDraw(transformMatrix); float[] scale = new float[2]; - if (mOutputWidth > mOutputHeight) { + if (width > height) { scale[0] = 1f; - scale[1] = ((float) mOutputHeight) / mOutputWidth; + scale[1] = ((float) height) / width; } else { - scale[0] = ((float) mOutputWidth) / mOutputHeight; + scale[0] = ((float) width) / height; 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"; - String stepsizeXString = "stepsizeX = " + 1.0f / mOutputWidth + ";\n"; - String stepsizeYString = "stepsizeY = " + 1.0f / mOutputHeight + ";\n"; - - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "uniform samplerExternalOES sTexture;\n" - + " vec2 seed;\n" - + " float stepsizeX;\n" - + " float stepsizeY;\n" - + " float stepsize;\n" - + " vec2 scale;\n" - + " float inv_max_dist;\n" - + "varying vec2 vTextureCoord;\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" - + " 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 - + stepsizeXString - + stepsizeYString - // sharpen - + " vec3 nbr_color = vec3(0.0, 0.0, 0.0);\n" - + " vec2 coord;\n" - + " vec4 color = texture2D(sTexture, vTextureCoord);\n" - + " coord.x = vTextureCoord.x - 0.5 * stepsizeX;\n" - + " coord.y = vTextureCoord.y - stepsizeY;\n" - + " nbr_color += texture2D(sTexture, coord).rgb - color.rgb;\n" - + " coord.x = vTextureCoord.x - stepsizeX;\n" - + " coord.y = vTextureCoord.y + 0.5 * stepsizeY;\n" - + " nbr_color += texture2D(sTexture, coord).rgb - color.rgb;\n" - + " coord.x = vTextureCoord.x + stepsizeX;\n" - + " coord.y = vTextureCoord.y - 0.5 * stepsizeY;\n" - + " nbr_color += texture2D(sTexture, coord).rgb - color.rgb;\n" - + " coord.x = vTextureCoord.x + stepsizeX;\n" - + " coord.y = vTextureCoord.y + 0.5 * stepsizeY;\n" - + " nbr_color += texture2D(sTexture, coord).rgb - color.rgb;\n" - + " vec3 s_color = vec3(color.rgb + 0.3 * nbr_color);\n" - // cross process - + " vec3 c_color = vec3(0.0, 0.0, 0.0);\n" - + " float value;\n" - + " if (s_color.r < 0.5) {\n" - + " value = s_color.r;\n" - + " } else {\n" - + " value = 1.0 - s_color.r;\n" - + " }\n" - + " float red = 4.0 * value * value * value;\n" - + " if (s_color.r < 0.5) {\n" - + " c_color.r = red;\n" - + " } else {\n" - + " c_color.r = 1.0 - red;\n" - + " }\n" - + " if (s_color.g < 0.5) {\n" - + " value = s_color.g;\n" - + " } else {\n" - + " value = 1.0 - s_color.g;\n" - + " }\n" - + " float green = 2.0 * value * value;\n" - + " if (s_color.g < 0.5) {\n" - + " c_color.g = green;\n" - + " } else {\n" - + " c_color.g = 1.0 - green;\n" - + " }\n" - + " c_color.b = s_color.b * 0.5 + 0.25;\n" - // blackwhite - + " float dither = rand(vTextureCoord + seed);\n" - + " vec3 xform = clamp((c_color.rgb - 0.15) * 1.53846, 0.0, 1.0);\n" - + " vec3 temp = clamp((color.rgb + stepsize - 0.15) * 1.53846, 0.0, 1.0);\n" - + " vec3 bw_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" - // vignette - + " coord = vTextureCoord - vec2(0.5, 0.5);\n" - + " float dist = length(coord * scale);\n" - + " float lumen = 0.85 / (1.0 + exp((dist * inv_max_dist - 0.73) * 20.0)) + 0.15;\n" - + " gl_FragColor = vec4(bw_color * lumen, color.a);\n" + "}\n"; + float maxDist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] * scale[1])) * 0.5f; + GLES20.glUniform2fv(scaleLocation, 1, scale, 0); + GlUtils.checkError("glUniform2fv"); + GLES20.glUniform1f(maxDistLocation, 1.0F / maxDist); + GlUtils.checkError("glUniform1f"); + GLES20.glUniform1f(stepSizeXLocation, 1.0F / width); + GlUtils.checkError("glUniform1f"); + GLES20.glUniform1f(stepSizeYLocation, 1.0F / height); + GlUtils.checkError("glUniform1f"); } }