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 995fc74c..c3bc417d 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SharpnessFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/SharpnessFilter.java @@ -1,17 +1,49 @@ 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; /** * Sharpens the input frames. */ public class SharpnessFilter 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 scale;\n" + + "uniform float stepsizeX;\n" + + "uniform float stepsizeY;\n" + + "varying vec2 vTextureCoord;\n" + + "void main() {\n" + + " 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" + + " gl_FragColor = vec4(color.rgb - 2.0 * scale * nbr_color, color.a);\n" + + "}\n"; + private float scale = 0.5f; - private int mOutputWidth = 1; - private int mOutputHeight = 1; + private int width = 1; + private int height = 1; + private int scaleLocation = -1; + private int stepSizeXLocation = -1; + private int stepSizeYLocation = -1; @SuppressWarnings("WeakerAccess") public SharpnessFilter() { } @@ -19,8 +51,8 @@ public class SharpnessFilter extends BaseFilter { @Override public void setSize(int width, int height) { super.setSize(width, height); - mOutputWidth = width; - mOutputHeight = height; + this.width = width; + this.height = height; } /** @@ -48,50 +80,45 @@ public class SharpnessFilter extends BaseFilter { return scale; } - @Override - protected BaseFilter onCopy() { - SharpnessFilter filter = new SharpnessFilter(); - filter.setSharpness(getSharpness()); - return filter; - } - @NonNull @Override public String getFragmentShader() { - String stepsizeXString = "stepsizeX = " + 1.0f / mOutputWidth + ";\n"; - String stepsizeYString = "stepsizeY = " + 1.0f / mOutputHeight + ";\n"; - String scaleString = "scale = " + scale + ";\n"; + return FRAGMENT_SHADER; + } - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "uniform samplerExternalOES sTexture;\n" - + " float scale;\n" - + " float stepsizeX;\n" - + " float stepsizeY;\n" - + "varying vec2 vTextureCoord;\n" - + "void main() {\n" - // Parameters that were created above - + stepsizeXString - + stepsizeYString - + scaleString - + " 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" - + " gl_FragColor = vec4(color.rgb - 2.0 * scale * nbr_color, color.a);\n" - + "}\n"; + @Override + public void onCreate(int programHandle) { + super.onCreate(programHandle); + scaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); + GlUtils.checkLocation(scaleLocation, "scale"); + 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; + stepSizeXLocation = -1; + stepSizeYLocation = -1; } + @Override + protected void onPreDraw(float[] transformMatrix) { + super.onPreDraw(transformMatrix); + GLES20.glUniform1f(scaleLocation, scale); + GlUtils.checkError("glUniform1f"); + GLES20.glUniform1f(stepSizeXLocation, 1.0F / width); + GlUtils.checkError("glUniform1f"); + GLES20.glUniform1f(stepSizeYLocation, 1.0F / height); + GlUtils.checkError("glUniform1f"); + } + @Override + protected BaseFilter onCopy() { + SharpnessFilter filter = new SharpnessFilter(); + filter.setSharpness(getSharpness()); + return filter; + } } 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 414c4cf7..75c86df6 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TemperatureFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TemperatureFilter.java @@ -1,15 +1,39 @@ 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 color temperature. */ public class TemperatureFilter 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 scale;\n" + + "varying vec2 vTextureCoord;\n" + + "void main() {\n" + + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + + " vec3 new_color = color.rgb;\n" + + " new_color.r = color.r + color.r * ( 1.0 - color.r) * scale;\n" + + " new_color.b = color.b - color.b * ( 1.0 - color.b) * scale;\n" + + " if (scale > 0.0) { \n" + + " new_color.g = color.g + color.g * ( 1.0 - color.g) * scale * 0.25;\n" + + " }\n" + + " float max_value = max(new_color.r, max(new_color.g, new_color.b));\n" + + " if (max_value > 1.0) { \n" + + " new_color /= max_value;\n" + + " } \n" + + " gl_FragColor = vec4(new_color, color.a);\n" + + "}\n"; + private float scale = 0f; + private int scaleLocation = -1; @SuppressWarnings("WeakerAccess") public TemperatureFilter() { } @@ -40,37 +64,36 @@ public class TemperatureFilter extends BaseFilter { return scale; } + @NonNull @Override - protected BaseFilter onCopy() { - TemperatureFilter filter = new TemperatureFilter(); - filter.setTemperature(getTemperature()); - return filter; + public String getFragmentShader() { + return FRAGMENT_SHADER; } - @NonNull @Override - public String getFragmentShader() { - String scaleString = "scale = " + (2.0f * scale - 1.0f) + ";\n"; - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "uniform samplerExternalOES sTexture;\n" - + "float scale;\n" - + "varying vec2 vTextureCoord;\n" - + "void main() {\n" // Parameters that were created above - + scaleString - + " vec4 color = texture2D(sTexture, vTextureCoord);\n" - + " vec3 new_color = color.rgb;\n" - + " new_color.r = color.r + color.r * ( 1.0 - color.r) * scale;\n" - + " new_color.b = color.b - color.b * ( 1.0 - color.b) * scale;\n" - + " if (scale > 0.0) { \n" - + " new_color.g = color.g + color.g * ( 1.0 - color.g) * scale * 0.25;\n" - + " }\n" - + " float max_value = max(new_color.r, max(new_color.g, new_color.b));\n" - + " if (max_value > 1.0) { \n" - + " new_color /= max_value;\n" - + " } \n" - + " gl_FragColor = vec4(new_color, color.a);\n" - + "}\n"; + 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, (2.0f * scale - 1.0f)); + GlUtils.checkError("glUniform1f"); + } + + @Override + protected BaseFilter onCopy() { + TemperatureFilter filter = new TemperatureFilter(); + filter.setTemperature(getTemperature()); + return filter; } } 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 e5c51aa8..e31e5f90 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TintFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/TintFilter.java @@ -1,18 +1,37 @@ package com.otaliastudios.cameraview.filters; import android.graphics.Color; +import android.opengl.GLES20; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import com.otaliastudios.cameraview.filter.BaseFilter; +import com.otaliastudios.cameraview.internal.GlUtils; /** * Tints the frames with specified color. */ public class TintFilter extends BaseFilter { - private int mTint = 0xFFFF0000; + + private final static String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n" + + "precision mediump float;\n" + + "uniform samplerExternalOES sTexture;\n" + + "uniform vec3 tint;\n" + + "vec3 color_ratio;\n" + + "varying vec2 vTextureCoord;\n" + + "void main() {\n" + + " color_ratio[0] = " + 0.21f + ";\n" + + " color_ratio[1] = " + 0.71f + ";\n" + + " color_ratio[2] = " + 0.07f + ";\n" + + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + + " float avg_color = dot(color_ratio, color.rgb);\n" + + " vec3 new_color = min(0.8 * avg_color + 0.2 * tint, 1.0);\n" + + " gl_FragColor = vec4(new_color.rgb, color.a);\n" + "}\n"; + + private int tint = Color.RED; + private int tintLocation = -1; @SuppressWarnings("WeakerAccess") public TintFilter() { } @@ -23,7 +42,7 @@ public class TintFilter extends BaseFilter { */ @SuppressWarnings("WeakerAccess") public void setTint(@ColorInt int color) { - this.mTint = color; + this.tint = color; } /** @@ -35,50 +54,44 @@ public class TintFilter extends BaseFilter { @SuppressWarnings("WeakerAccess") @ColorInt public int getTint() { - return mTint; + return tint; } + @NonNull @Override - protected BaseFilter onCopy() { - TintFilter filter = new TintFilter(); - filter.setTint(getTint()); - return filter; + public String getFragmentShader() { + return FRAGMENT_SHADER; } - @NonNull @Override - public String getFragmentShader() { - float[] color_ratio = {0.21f, 0.71f, 0.07f}; - String[] color_ratioString = new String[3]; - color_ratioString[0] = "color_ratio[0] = " + color_ratio[0] + ";\n"; - color_ratioString[1] = "color_ratio[1] = " + color_ratio[1] + ";\n"; - color_ratioString[2] = "color_ratio[2] = " + color_ratio[2] + ";\n"; + public void onCreate(int programHandle) { + super.onCreate(programHandle); + tintLocation = GLES20.glGetUniformLocation(programHandle, "tint"); + GlUtils.checkLocation(tintLocation, "tint"); + } - float[] tint_color = {Color.red(mTint) / 255f, - Color.green(mTint) / 255f, Color.blue(mTint) / 255f}; + @Override + public void onDestroy() { + super.onDestroy(); + tintLocation = -1; + } - String[] tintString = new String[3]; - tintString[0] = "tint[0] = " + tint_color[0] + ";\n"; - tintString[1] = "tint[1] = " + tint_color[1] + ";\n"; - tintString[2] = "tint[2] = " + tint_color[2] + ";\n"; + @Override + protected void onPreDraw(float[] transformMatrix) { + super.onPreDraw(transformMatrix); + float[] channels = new float[]{ + Color.red(tint) / 255f, + Color.green(tint) / 255f, + Color.blue(tint) / 255f + }; + GLES20.glUniform3fv(tintLocation, 1, channels, 0); + GlUtils.checkError("glUniform3fv"); + } - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "uniform samplerExternalOES sTexture;\n" - + " vec3 tint;\n" - + " vec3 color_ratio;\n" - + "varying vec2 vTextureCoord;\n" - + "void main() {\n" - // Parameters that were created above - + color_ratioString[0] - + color_ratioString[1] - + color_ratioString[2] - + tintString[0] - + tintString[1] - + tintString[2] - + " vec4 color = texture2D(sTexture, vTextureCoord);\n" - + " float avg_color = dot(color_ratio, color.rgb);\n" - + " vec3 new_color = min(0.8 * avg_color + 0.2 * tint, 1.0);\n" - + " gl_FragColor = vec4(new_color.rgb, color.a);\n" + "}\n"; + @Override + protected BaseFilter onCopy() { + TintFilter filter = new TintFilter(); + filter.setTint(getTint()); + return filter; } } 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 6158eecc..a3981954 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/filters/VignetteFilter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/filters/VignetteFilter.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; /** @@ -10,10 +13,32 @@ import com.otaliastudios.cameraview.filter.BaseFilter; */ public class VignetteFilter 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 range;\n" + + "uniform float inv_max_dist;\n" + + "uniform float shade;\n" + + "uniform vec2 scale;\n" + + "varying vec2 vTextureCoord;\n" + + "void main() {\n" + + " const float slope = 20.0;\n" + + " vec2 coord = vTextureCoord - vec2(0.5, 0.5);\n" + + " float dist = length(coord * scale);\n" + + " float lumen = shade / (1.0 + exp((dist * inv_max_dist - range) * slope)) + (1.0 - shade);\n" + + " vec4 color = texture2D(sTexture, vTextureCoord);\n" + + " gl_FragColor = vec4(color.rgb * lumen, color.a);\n" + + "}\n"; + private float mScale = 0.85f; private float mShade = 0.5f; - private int mOutputWidth = 1; - private int mOutputHeight = 1; + private int mWidth = 1; + private int mHeight = 1; + + private int mRangeLocation = -1; + private int mMaxDistLocation = -1; + private int mShadeLocation = -1; + private int mScaleLocation = -1; @SuppressWarnings("WeakerAccess") public VignetteFilter() { } @@ -21,8 +46,8 @@ public class VignetteFilter extends BaseFilter { @Override public void setSize(int width, int height) { super.setSize(width, height); - mOutputWidth = width; - mOutputHeight = height; + mWidth = width; + mHeight = height; } /** @@ -31,11 +56,9 @@ public class VignetteFilter extends BaseFilter { */ @SuppressWarnings("WeakerAccess") public void setVignetteScale(float scale) { - if (scale < 0.0f) - scale = 0.0f; - else if (scale > 1.0f) - scale = 1.0f; - this.mScale = scale; + if (scale < 0.0f) scale = 0.0f; + if (scale > 1.0f) scale = 1.0f; + mScale = scale; } /** @@ -71,61 +94,68 @@ public class VignetteFilter extends BaseFilter { return mShade; } + @NonNull @Override - protected BaseFilter onCopy() { - VignetteFilter filter = new VignetteFilter(); - filter.setVignetteScale(getVignetteScale()); - filter.setVignetteShade(getVignetteShade()); - return filter; + public String getFragmentShader() { + return FRAGMENT_SHADER; } - @NonNull @Override - public String getFragmentShader() { + public void onCreate(int programHandle) { + super.onCreate(programHandle); + mRangeLocation = GLES20.glGetUniformLocation(programHandle, "range"); + GlUtils.checkLocation(mRangeLocation, "range"); + mMaxDistLocation = GLES20.glGetUniformLocation(programHandle, "inv_max_dist"); + GlUtils.checkLocation(mMaxDistLocation, "inv_max_dist"); + mShadeLocation = GLES20.glGetUniformLocation(programHandle, "shade"); + GlUtils.checkLocation(mShadeLocation, "shade"); + mScaleLocation = GLES20.glGetUniformLocation(programHandle, "scale"); + GlUtils.checkLocation(mScaleLocation, "scale"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mRangeLocation = -1; + mMaxDistLocation = -1; + mShadeLocation = -1; + mScaleLocation = -1; + } + + @Override + protected void onPreDraw(float[] transformMatrix) { + super.onPreDraw(transformMatrix); float[] scale = new float[2]; - if (mOutputWidth > mOutputHeight) { + if (mWidth > mHeight) { scale[0] = 1f; - scale[1] = ((float) mOutputHeight) / mOutputWidth; + scale[1] = ((float) mHeight) / mWidth; } else { - scale[0] = ((float) mOutputWidth) / mOutputHeight; + scale[0] = ((float) mWidth) / mHeight; scale[1] = 1f; } - float max_dist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] - * scale[1])) * 0.5f; + GLES20.glUniform2fv(mScaleLocation, 1, scale, 0); + GlUtils.checkError("glUniform2fv"); - String[] scaleString = new String[2]; - scaleString[0] = "scale[0] = " + scale[0] + ";\n"; - scaleString[1] = "scale[1] = " + scale[1] + ";\n"; - String inv_max_distString = "inv_max_dist = " + 1.0f / max_dist + ";\n"; - String shadeString = "shade = " + mShade + ";\n"; + float maxDist = ((float) Math.sqrt(scale[0] * scale[0] + scale[1] * scale[1])) * 0.5f; + GLES20.glUniform1f(mMaxDistLocation, 1F / maxDist); + GlUtils.checkError("glUniform1f"); + + GLES20.glUniform1f(mShadeLocation, mShade); + GlUtils.checkError("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. - String rangeString = "range = " - + (1.30f - (float) Math.sqrt(mScale) * 0.7f) + ";\n"; - - return "#extension GL_OES_EGL_image_external : require\n" - + "precision mediump float;\n" - + "uniform samplerExternalOES sTexture;\n" - + " float range;\n" - + " float inv_max_dist;\n" - + " float shade;\n" - + " vec2 scale;\n" - + "varying vec2 vTextureCoord;\n" - + "void main() {\n" - // Parameters that were created above - + scaleString[0] - + scaleString[1] - + inv_max_distString - + shadeString - + rangeString - + " const float slope = 20.0;\n" - + " vec2 coord = vTextureCoord - vec2(0.5, 0.5);\n" - + " float dist = length(coord * scale);\n" - + " float lumen = shade / (1.0 + exp((dist * inv_max_dist - range) * slope)) + (1.0 - shade);\n" - + " vec4 color = texture2D(sTexture, vTextureCoord);\n" - + " gl_FragColor = vec4(color.rgb * lumen, color.a);\n" - + "}\n"; + float range = (1.30f - (float) Math.sqrt(mScale) * 0.7f); + GLES20.glUniform1f(mRangeLocation, range); + GlUtils.checkError("glUniform1f"); + } + + @Override + protected BaseFilter onCopy() { + VignetteFilter filter = new VignetteFilter(); + filter.setVignetteScale(getVignetteScale()); + filter.setVignetteShade(getVignetteShade()); + return filter; } }